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 8251: Updates to the Opus Audio Codec

Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : JM. Valin (Mozilla Corporation), K. Vos (vocTone)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF codec
Première rédaction de cet article le 22 octobre 2017


Le codec Opus, normalisé dans le RFC 6716 a une particularité rare à l'IETF : il est spécifié par un programme, pas par une description écrite en langue naturelle. Les programmes ont des bogues et ce nouveau RFC corrige quelques petites bogues pas graves trouvées dans le RFC 6716.

C'est un vieux débat dans le monde de la normalisation : faut-il décrire un protocole ou un format par une description en langue naturelle (vecteur souvent ambigu) ou par une mise en œuvre dans un langage de programmation (qui fournira directement un programme utilisable), dans laquelle il sera difficile de distinguer ce qui est réellement obligatoire et ce qui n'est qu'un détail de cette mise en œuvre particulière ? Presque tout le temps, à l'IETF, c'est la voie de la description en anglais qui est choisie. Mais le RFC 6716, qui normalisait Opus, avait choisi une autre voie, celle du code source, écrit en C. C'est ce code qui est la loi.

Dans les deux cas, en anglais ou en C, les humains qui rédigent les normes font des erreurs. D'où ce RFC de correction, qui répare le RFC 6716 sur des points mineurs (la compatibilité est maintenue, il ne s'agit pas d'une nouvelle version d'Opus).

Chaque section du RFC est ensuite consacrée à une des erreurs. La section 3, par exemple, corrige un simple oubli dans le code de réinitialiser l'état du décodeur lors d'un changement. Le patch ne fait que deux lignes. Notez qu'il change le résultat produit par le décodeur, mais suffisamment peu pour que les vecteurs de test de l'annexe A.4 du RFC 6716 soient inchangés.

L'erreur en section 9 est également une erreur de logique dans la programmation. Sa correction, par contre, nécessite de changer les vecteurs de tests (cf. section 11).

Le reste des bogues, en revanche, consiste en erreurs de programmation C banales. Ainsi, en section 4, il y a un débordement d'entier si les données font plus de 2^31-1 octets, menant à lire en dehors de la mémoire. En théorie, cela peut planter le décodeur (mais, en pratique, le code existant n'a pas planté.) Notez que cela ne peut pas arriver si on utilise Opus dans RTP, dont les limites seraient rencontrées avant qu'Opus ne reçoive ces données anormales. Cette bogue peut quand même avoir des conséquences de sécurité et c'est pour cela qu'elle a reçu un CVE, CVE-2013-0899. Le patch est très court. (Petit rappel de C : la norme de ce langage ne spécifie pas ce qu'il faut faire lorsqu'on incrémente un entier qui a la taille maximale. Le résultat dépend donc de l'implémentation. Pour un entier signé, comme le type int, le comportement le plus courant est de passer à des valeurs négatives.)

Notez qu'une autre bogue, celle de la section 7, a eu un CVE, CVE-2017-0381.

En section 5, c'est un problème de typage : un entier de 32 bits utilisé au lieu d'un de 16 bits, ce qui pouvait mener une copie de données à écraser partiellement les données originales.

Dans la section 6 du RFC, la bogue était encore une valeur trop grande pour un entier. Cette bogue a été découverte par fuzzing, une technique très efficace pour un programme traitant des données externes venues de sources qu'on ne contrôle pas !

Bref, pas de surprise : programmer en C est difficile, car de trop bas niveau, et de nombreux pièges guettent le programmeur.

La section 11 du RFC décrit les nouveaux vecteurs de test rendus nécessaires par les corrections à ces bogues. Ils sont téléchargeables.

Une version à jour du décodeur normatif figure désormais sur le site officiel. Le patch traitant les problèmes décrits dans ce RFC est en ligne. Ce patch global est de petite taille (244 lignes, moins de dix kilo-octets, ce qui ne veut pas dire que les bogues n'étaient pas sérieuses).


Téléchargez le RFC 8251


L'article seul

RFC 8254: Uniform Resource Name (URN) Namespace Registration Transition

Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : J. Klensin, J. Hakala (The National Library of Finland)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF urnbis
Première rédaction de cet article le 21 octobre 2017


Pour avoir un espace de noms d'URN, autrefois, il fallait un examen par l'IETF, ce qui était un peu contraignant (et risquait de mener au « squat », avec des gens utilisant un espace de noms qu'ils ne se sont pas donné la peine d'enregistrer). Le RFC 8141 ayant changé cette politique d'enregistrement pour celle, plus facile, d'examen par un expert, il a fallu clarifier quelques processus, ce que fait ce RFC. Dans la foulée, ce RFC change le statut de quelques RFC précédents, qui avaient servi de base à l'enregistrement de certains espaces de noms. Bref, que de la procédure et de la bureaucratie (mais pas inutile).

Un des buts des URN (désormais normalisés dans le RFC 8141) était d'intégrer des systèmes d'identificateurs existants. C'est ainsi que le RFC 2288 avait décrit un espace de noms pour les ISBN, mais sans l'enregistrer formellement à l'IANA, en partie parce que le mécanisme d'enregistrement n'était pas clair à l'époque. Par la suite, le RFC 3187 a remplacé le RFC 2288 et comblé ce manque (dans sa section 5). Idem pour les ISSN dans le RFC 3044. Ces deux RFC 3187 et RFC 3044 sont reclassés comme étant désormais d'intérêt historique uniquement (section 2 de notre RFC), les enregistrements de ces espaces de noms ayant été refaits selon les nouvelles règles (section 5). Voyez le nouvel enregistrement d'isbn et celui d'issn.

Pour ISBN, le principal changement est que l'espace de noms URN fait désormais référence à la nouvelle version de la norme, ISO 2108:2017 (cf. la page officielle et, comme toutes les normes du dinosaure ISO, elle n'est pas disponible en ligne.) Elle permet les ISBN de treize caractères (les ISBN avaient dix caractères au début). Pour ISSN, la norme du nouvel enregistrement est ISO 3297:2007, qui permet notamment à un ISSN de référencer tous les médias dans lesquels a été fait une publication (il fallait un ISSN par média, avant).

Et les NBN (National Bibliography Number, section 3) ? C'est un cas rigolo, car NBN ne désigne pas une norme spécifique, ni même une famille d'identificateurs, mais regroupe tous les mécanismes d'identificateurs utilisés par les bibliothèques nationales partout dans le monde. En effet, les bibibliothèques peuvent recevoir des documents qui n'ont pas d'autre identificateur. Si la bibliothèque nationale de Finlande reçoit un tel document, son identificateur national pourrait être, mettons, fe19981001, et, grâce à l'espace de noms URN nbn, il aura un URN, urn:nbn:fi-fe19981001.

Les NBN sont spécifiés dans le RFC 3188, pour lequel il n'y a pas encore de mise à jour prévue, contrairement aux ISBN et ISSN.

Outre les NBN, il y a bien d'autres schémas d'URN qui mériteraient une mise à jour, compte-tenu des changements du RFC 8141 (section 4). Mais il n'y a pas le feu, même si leur enregistrement actuel à l'IANA n'est pas tout à fait conforme au RFC 8141. On peut continuer à les utiliser.


Téléchargez le RFC 8254


L'article seul

RFC 8244: Special-Use Domain Names Problem Statement

Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : T. Lemon (Nominum), R. Droms, W. Kumari (Google)
Pour information
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 20 octobre 2017


Le RFC 6761 créait un registre de « noms de domaines d'usage spécial », où l'IETF enregistrait des noms qui pouvaient nécessiter un traitement spécial par une des entités de la chaîne d'avitaillement et de résolution des noms de domaines. Un exemple est celui des noms de domaine qui n'utilisaient pas le DNS, comme le .onion du RFC 7686. Certaines personnes ont émis des réserves sur ce registre, par exemple parce qu'il marchait sur une pelouse que l'ICANN considère comme sienne. Ce nouveau RFC, très controversé, fait la liste de tous les reproches qui ont été faits au RFC 6761 et à son registre des noms spéciaux.

La résolution de noms - partir d'un nom de domaine et obtenir des informations comme l'adresse IP - est une des fonctions cruciales de l'Internet. Il est donc normal de la prendre très au sérieux. Et les noms ont une valeur pour les utilisateurs (la vente record semble être celle de business.com). Souvent, mais pas toujours, cette résolution se fait avec le DNS. Mais, je l'ai dit, pas toujours : il ne faut pas confondre les noms de domaines (une organisation arborescente, une syntaxe, les composants séparés par des points, par exemple _443._tcp.www.bortzmeyer.org., réussir-en.fr. ou 7j3ncmar4jm2r3e7.onion.) et le DNS, un protocole réseau particulier (section 2 du RFC, pour la terminologie). La section 2 de ce RFC, suivant la définition du RFC 7719, ne considère pas qu'un nom de domaine utilise forcément le DNS. (Le problème n° 20, en section 3 revient sur cette question.)

Le registre des noms de domaines spéciaux a été créé en 2013 par le RFC 6761. Contrairement à ce que croient beaucoup de gens, il ne contient pas que des TLD (par exemple, il a aussi a.e.f.ip6.arpa ou bien example.org). Depuis sa création, plusieurs noms ont été ajoutés, le plus spectaculaire ayant été le .onion en 2015, via le RFC 7686. Beaucoup d'autres noms ont été proposés, sans être explicitement rejetés mais sans être acceptés non plus (voir les Internet-Drafts draft-grothoff-iesg-special-use-p2p-gns et draft-chapin-additional-reserved-tlds). Ces enregistrements ne se sont pas très bien passés, avec beaucoup d'engueulades, notamment pour les TLD.

En effet, la bagarre a toujours fait rage pour la gestion des noms dans la racine du DNS. Il y a cinq sortes de TLD :

  • Ceux qui sont mis dans la racine publique du DNS, comme .bo, .net, .guru, etc. Contrairement à ce que dit le RFC, ils ne sont pas forcément délégués par l'ICANN (.de a été créé bien avant l'ICANN, et .su résiste depuis longtemps à toutes les tentatives de l'ICANN de le supprimer).
  • Ceux gérés par l'IETF « pour des raisons techniques », et qui sont parfois délégués dans la racine publique, le plus connu étant .arpa. Les domaines du registre des noms spéciaux en font partie. Les fanas de gouvernance noteront qu'ils ne passent jamais par l'ICANN.
  • Ceux que l'ICANN bloque, pour des raisons variées (cf. leurs règles, notamment sections 2.2.1.2.1 ou 2.2.1.4.1).
  • Ceux qui sont utilisés en dehors de la racine publique du DNS, comme .eth pour Ethereum ou .bit pour Namecoin. Cela inclut aussi des noms très utilisés localement comme .home ou .corp.
  • Et ceux qui sont libres, que personne n'utilise encore. Demain, peut-être que .macron sera utilisé par les fans du Président ?

Cette classification est imparfaite, comme toutes les classifications. Par exemple, un nom peut être sur la liste noire de l'ICANN et être quand même utilisé par des organisations locales, ou via un logiciel particulier.

Ce RFC veut présenter une liste exhaustive des problèmes posés lors de l'enregistrement de ces noms spéciaux. Comme elle est exhaustive (« an unfiltered compilation of issues »), elle n'est pas consensuelle, loin de là ! Plusieurs des problèmes ne sont pas considérés comme tels par tout le monde, ce que le RFC note bien dans les sections 1 et 3.

Le gros du RFC est donc cette liste de problèmes, en section 3. Je ne vais pas tous les citer. Notez tout de suite que ce RFC décrit des problèmes, ou perçus comme tels par certains, pas des solutions. Il ne propose pas de réforme du RFC 6761. Notez aussi que les problèmes ne sont pas classés (leur ordre d'exposition, et le numéro qu'ils reçoivent, n'est pas un ordre d'importance.)

Premier problème cité, la coordination avec l'ICANN. Les cinq catégories de noms de TLD cités plus haut coexistent dans le même espace de noms. S'il y a deux .macron, il y aura un problème (voir par exemple le rapport SAC 090, mais en se rappelant que c'est l'ICANN qui s'auto-défend). L'IETF et l'ICANN ont un mécanisme d'information réciproque mais pas de processus bureaucratique formel de coordination. Si l'IETF veut réserver .home (RFC 7788), il n'y a officiellement pas de mécanisme pour communiquer cette décision à l'ICANN et obtenir d'elle la confirmation que ce nom n'est pas par ailleurs en cours de réservation. (Cf. 4.1.4 qui revient sur ce mécanisme - « liaison » en anglais.)

(Personnellement, cet argument me fait plutôt rire : beaucoup de gens sont actifs à la fois à l'IETF et à l'ICANN, les deux organisations opèrent de manière assez publique, surtout l'IETF, et la possibilité qu'elles réservent chacune un nom sans savoir ce qu'a fait l'autre est purement théorique.)

Le problème 2 est celui de la définition de « pour des raisons techniques », introduit dans le RFC 2860, et qui fonde le droit de l'IETF à réserver des TLD sans passer par la caisse de l'ICANN (« assignments of domain names for technical uses [...] are not considered to be policy issues, and shall remain subject to the provisions of this Section 4 »). Le problème est que ces raisons techniques ne sont définies nulle part et que personne ne sait trop ce que cela veut dire.

Les problèmes 3 et 4 sont qu'il n'y a pas de Directeur Chef de l'Internet. Ni l'ICANN, ni l'IETF (ni évidemment l'UIT) ne sont reconnus, ni en droit, ni en fait, comme ayant une autorité quelconque (le raccourci journalistique de présenter l'ICANN comme « le régulateur mondial de l'Internet » est ridiculement faux). Imaginons qu'une développeuse crée un nouveau logiciel qui utilise le TLD .zigzag pour son nommage, et diffuse le logiciel en question : personne ne peut l'en empêcher, même si l'ICANN souffre de la perte de revenus potentiels, même si des grognons à l'IETF murmurent bruyamment que c'est du squatting (voir aussi le début de la section 4). Et heureusement que c'est ainsi : c'est le côté « innover sans autorisation » (« permissionless innovation » qui a été si essentiel pour le succès de l'Internet). Un autre exemple est bien sûr le projet .42, aujourd'hui abandonné mais qui illustrait ce côté décentralisé de l'Internet.

La définition du problème 5 note que les organisations (ou les individus) utilisent des TLD (ou des domaines quelconques, mais c'est surtout sur les TLD que se focalise la discussion) sans suivre les procédures. Elles ont plusieurs raisons pour agir ainsi :

  • Elles ne savent pas qu'il existe des procédures (c'est sans doute le cas le plus fréquent).
  • Elles savent vaguement qu'il existe des procédures mais elles se disent que, pour une utilisation purement locale, ce n'est pas important. Le trafic sur les serveurs de noms de la racine montre au contraire que les fuites sont importantes : les noms locaux ne le restent pas. Sans compter le risque de « collisions » entre un nom supposé purement local et une allocation par l'ICANN : .corp et .home sont des TLD locaux très populaires et il y a des candidatures (actuellement gelées de facto) pour ces TLD à l'ICANN
  • Les procédures existent, l'organisation les connait, mais ce n'est pas ouvert. C'est par exemple le cas des TLD ICANN, pour lesquels il n'y a actuellement pas de cycle de candidature, et on n'espère pas le prochain avant 2019 ou 2020.
  • Les procédures existent à l'ICANN, l'organisation les connait, c'est ouvert, mais le prix fait reculer (185 000 $ US au précédent cycle ICANN, et uniquement pour déposer le dossier).
  • Les procédures existent à l'IETF, l'organisation les connait, c'est ouvert, mais l'organisation refuse délibérement de participer (cas analogue à CARP).
  • Les procédures existent, l'organisation les connait, c'est ouvert, mais l'organisation estime, à tort ou à raison, que sa candidature sera refusée (c'est actuellement le cas à l'IETF).

Ensuite, le problème 6 : il y a plusieurs protocoles de résolution de nom, le DNS n'étant que le principal. En l'absence de métadonnées indiquant le protocole à utiliser (par exemple dans les URL), se servir du TLD comme « aiguillage » est une solution tentante (if tld == "onion" then use Tor elsif tld == "gnu" then use GnuNet else use DNS…)

Le registre des noms de domaines spéciaux est essentiellement en texte libre, sans grammaire formelle. Cela veut dire que le code spécifique qui peut être nécessaire pour traiter tous ces domaines spéciaux (un résolveur doit, par exemple, savoir que .onion n'utilise pas le DNS et qu'il ne sert donc à rien de l'envoyer à la racine) doit être fait à la main, il ne peut pas être automatiquement dérivé du registre (problème 7). Résultat, quand un nouveau domaine est ajouté à ce registre, il sera traité « normalement » par les logiciels pas mis à jour, et pendant un temps assez long. Par exemple, les requêtes seront envoyées à la racine, ce qui pose des problèmes de vie privée (cf. RFC 7626).

Comme noté plus haut, certains candidats à un nom de domaine spécial sont inquiets du temps que prendra l'examen de leur candidature, et du risque de rejet (problème 8). Ils n'ont pas tort. L'enregistrement du .local (RFC 6762) a pris dix ans, et les efforts de Christian Grothoff pour enregistrer .gnu se sont enlisés dans d'épaisses couches de bureaucratie. Ce n'est peut-être pas un hasard si Apple a fini par avoir son .local (non sans mal) alors que des projets de logiciel libre comme GNUnet se sont vu fermer la porte au nez.

C'est que l'IETF n'est pas toujours facile et qu'un certain nombre de participants à cette organisation ont souvent une attitude de blocage face à tout intervenant extérieur (problème 9). Ils n'ont pas tort non plus de se méfier des systèmes non-DNS, leurs raisons sont variées :

  • Absence d'indication du mécanisme de résolution donc difficulté supplémentaire pour les logiciels (« c'est quoi, .zkey ? »). De plus, un certain nombre de participants à l'IETF estiment qu'il ne faut de toute façon qu'un seul protocole de résolution, afin de limiter la complexité.
  • Le problème de la complexité est d'autant plus sérieux que les autres protocoles de résolution de noms n'ont pas forcément la même sémantique que le DNS : ils sont peut-être sensibles à la casse, par exemple.
  • Conviction que l'espace de nommage est « propriété » de l'IETF et/ou de l'ICANN, et que ceux qui réservent des TLD sans passer par les procédures officielles sont de vulgaires squatteurs.
  • Sans compter le risque, s'il existe un moyen simple et gratuit de déposer un TLD via l'IETF, que plus personne n'alimente les caisses de l'ICANN.
  • Et enfin le risque juridique : si Ser Davos fait un procès à l'IETF contre l'enregistrement de .onion, cela peut durer longtemps et coûter cher. L'ICANN court le même risque mais, elle, elle a des avocats en quantité, et de l'argent pour les payer.

Le problème 11 est davantage lié au RFC 6761 lui-même. Ce RFC a parfois été compris de travers, comme pour l'enregistrement de ipv4only.arpa (RFC 7050) et surtout celui de .home (RFC 7788, qui citait le RFC 6761 mais n'avait tenu aucune de ses obligations). Voir aussi le problème 16.

Les problèmes 12 et 13 concernent les TLD existants (au sens où ils sont utilisés) mais pas enregistrés officiellement (voir par exemple le rapport de l'ICANN sur les « collisions »). Cela serait bien de documenter quelque part cette utilisation, de façon à, par exemple, être sûrs qu'ils ne soient pas délégués par l'ICANN. Mais cela n'a pas marché jusqu'à présent.

Le problème 14 concerne le fait que des noms de domaine spéciaux sont parfois désignés comme spéciaux par leur écriture dans le registre, mais parfois simplement par leur délégation dans le DNS.

Le problème 15 est celui de la différence entre enregistrer un usage et l'approuver. Avec la règle « géré par l'IETF pour un usage technique » du RFC 2860, il n'y a pas moyen d'enregistrer un nom sans une forme d'« approbation ». (Pas mal d'articles sur l'enregistrement de .onion avaient ainsi dit, à tort, que « l'IETF approuvait officiellement Tor ».)

Le problème 17 est un bon exemple du fait que la liste de Prévert qu'est ce RFC est en effet « non filtrée » et que toute remarque soulevée y a été mise, quels que soient ses mérites. Il consiste à dire que l'utilisation du registre des noms spéciaux est incohérente, car les enregistrements donnent des règles différentes selon le nom. Cela n'a rien d'incohérent, c'était prévu dès le départ par le RFC 6761 (section 5) : il n'y a pas de raison de traiter de la même façon .onion (qui n'utilise pas le DNS du tout) et .bit (passerelle entre le DNS et Namecoin).

Le problème 19 découle du fait que les noms de domaine ne sont pas de purs identificateurs techniques comme le sont, par exemple, les adresses MAC. Ils ont un sens pour les utilisateurs. Bien que, techniquement parlant, les développeurs de Tor auraient pu choisir le nom .04aab3642f5 ou onion.torproject.org comme suffixe pour les services en oignon, ils ont préféré un nom d'un seul composant, et compréhensible, .onion. Ce désir est bien compréhensible (une proposition à l'IETF est de reléguer tous les noms spéciaux sous un futur TLD .alt, qui ne connaitra probablement aucun succès même s'il est créé un jour). Mais il entraine une pression accrue sur la racine des noms de domaine : si deux projets réservent .zigzag, lequel des deux usages faut-il enregistrer ?

Enfin, le dernier problème, le 21, est davantage technique : il concerne DNSSEC. Si un TLD est enregistré comme domaine spécial, faut-il l'ajouter dans la racine du DNS et, si oui, faut-il que cette délégation soit signée ou pas ? S'il n'y a pas de délégation, le TLD sera considéré comme invalide par les résolveurs validants. Par exemple, si je fais une requête pour quelquechose.zigzag, la racine du DNS va répondre :


% dig @k.root-servers.net A quelquechose.zigzag

; <<>> DiG 9.10.3-P4-Debian <<>> @k.root-servers.net A quelquechose.zigzag
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 7785
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;quelquechose.zigzag.	IN A

;; AUTHORITY SECTION:
zero.			86400 IN NSEC zip. NS DS RRSIG NSEC
zero.			86400 IN RRSIG NSEC 8 1 86400 (
				20171017050000 20171004040000 46809 .
				wyKfrNEygGCbDscCu6uV/DFofs5DKYiV+jJd2s4xkkAT
				...
.			86400 IN NSEC aaa. NS SOA RRSIG NSEC DNSKEY
.			86400 IN RRSIG NSEC 8 0 86400 (
				20171017050000 20171004040000 46809 .
				kgvHoclQNwmDKfgy4b96IgoOkdkyRWyXYwohW+mpfG+R
				...
.			86400 IN SOA a.root-servers.net. nstld.verisign-grs.com. (
				2017100400 ; serial
				1800       ; refresh (30 minutes)
				900        ; retry (15 minutes)
				604800     ; expire (1 week)
				86400      ; minimum (1 day)
				)
.			86400 IN RRSIG SOA 8 0 86400 (
				20171017050000 20171004040000 46809 .
				GnTMS7cx+XB+EmbMFWt+yEAg29w17HJfUaOqvPsTn0eJ
				...

;; Query time: 44 msec
;; SERVER: 2001:7fd::1#53(2001:7fd::1)
;; WHEN: Wed Oct 04 12:58:12 CEST 2017
;; MSG SIZE  rcvd: 1036
      
    

Et l'enregistrement NSEC prouvera qu'il n'y a rien entre .zero et .zip, amenant le résolveur validant à considérer que .zigzag ne peut pas exister. Si le nom devait être traité par le DNS (et, par exemple, résolu localement comme ceux du RFC 6303, c'est une réponse correcte : la requête n'aurait pas dû aller à la racine). Dans d'autres cas, cela peut être gênant. De toute façon, le débat est assez théorique : l'IETF n'a aucun pouvoir sur la racine du DNS, et aucun moyen d'y ajouter un nom.

Après cet examen des possibles et potentiels problèmes, la section 4 du RFC examine les pratiques existantes. Plusieurs documents plus ou moins officiels examinent déjà ces questions. Mais je vous préviens tout de suite : aucun ne répond complètement au·x problème·s.

Commençons par le RFC 2826 sur la nécessité d'une racine unique. C'est un document IAB et qui n'engage donc pas l'IETF, même s'il est souvent cité comme texte sacré. Sa thèse principale est qu'il faut une racine unique, et que .zigzag, .pm ou .example doivent donc avoir la même signification partout, sous peine de confusion importante chez l'utilisateur. Cela n'interdit pas des noms à usage local à condition que cela reste bien local. Comme les utilisateurs ne vont pas faire la différence, ces noms locaux vont forcément fuiter tôt ou tard. (Par exemple, un utilisateur qui regarde http://something.corp/ ne va pas réaliser que ce nom ne marche qu'en utilisant les résolveurs de l'entreprise, et va essayer d'y accéder depuis chez lui. Un autre exemple serait celui d'un utilisateur qui essaierait de taper ping 7j3ncmar4jm2r3e7.onion depuis la ligne de commande, sans se rendre compte que ping ne connait pas Tor.)

Bref, le RFC 2826 dit clairement qu'une racine unique est nécessaire, et que les noms locaux sont casse-gueule.

Le second RFC à lire est évidemment le RFC 6761, qui a créé le registre des noms de domaine spéciaux. Lui, c'est un document IETF sur le Chemin des Normes. Quelques points qui sont souvent oubliés par les lecteurs du RFC 6761 méritent d'être répétés :

  • Certains noms spéciaux ne le sont en fait pas tellement, et sont résolus par le DNS de la manière habituelle (c'est le cas de in-addr.arpa, par exemple, et rappelez-vous bien à son sujet que les noms de domaine spéciaux ne sont pas forcément des TLD).
  • Parfois, les noms spéciaux sont résolus par le DNS mais de manière inhabituelle, comme 10.in-addr.arpa (pour les adresses IP du RFC 1918), qui doit être traité par le résolveur, sans jamais interroger la racine (cf. RFC 6303).
  • D'autres noms spéciaux sont gravement spéciaux et ne doivent pas utiliser le DNS du tout. Ils servent de « commutateurs » pour indiquer à la bibliothèque de résolution de noms (la GNU libc sur Ubuntu ou Mint par exemple) qu'il faut changer de protocole. C'est le cas de .onion (qui doit utiliser Tor) ou de .local (qui doit utiliser mDNS, RFC 6762).
  • Et tous ces cas sont valides et normaux (même si certains traditionnalistes à l'IETF rechignent devant le dernier).

Notez qu'à l'heure actuelle, tous les noms enregistrés comme noms de domaine spéciaux sont des TLD ou bien sont résolus avec le DNS.

Troisième RFC à lire absolument avant de dire des bêtises sur les noms de domaine spéciaux, le RFC 2860, qui fixe le cadre des relations compliquées entre l'IETF et l'ICANN. En gros, la règle par défaut est que l'ajout (ou le retrait) de TLD dans la racine est une prérogative de l'ICANN sauf les « noms de domaine à usage technique » (notion non définie…) où l'IETF décide. Notez que ce document concerne uniquement les relations entre l'IETF et l'ICANN. Si le W3C, ou la développeuse du logiciel ZigZag, veut créer un TLD, que se passe-t-il ? Ce point n'est pas traité dans le RFC 2860. Certains exégètes estiment que cela veut dire que ces tiers sont implicitement exclus.

Il y a aussi d'autres documents mais moins cruciaux. Le RFC 6762 qui normalise mDNS est celui qui a réservé .local et c'est donc un exemple d'un enregistrement réussi (mais qui fut laborieux, plus de douze années de développement furent nécessaires, cf. l'annexe H du RFC 6762).

Autre exemple réussi, le RFC 7686 sur le .onion. .onion était utilisé depuis longtemps quand le RFC 6761 a créé le registre des noms de domaine spéciaux. L'enregistrement a posteriori a réussi, malgré de vigoureuses oppositions mais il faut noter que le consensus approximatif de l'IETF a été facilité par une décision du CA/B Forum de ne plus allouer de certificats pour des TLD internes.

Encore un autre RFC à lire, le RFC 6303, qui décrit les noms qui devraient idéalement être résolus localement, c'est-à-dire par le résolveur de l'utilisateur, sans demander aux serveurs faisant autorité. C'est par exemple le cas des in-addr.arpa correspondant aux adresses IPv4 privées du RFC 1918. Il ne sert à rien de demander à la racine l'enregistrement PTR de 3.2.1.10.in-addr.arpa : ces adresses IP étant purement locales, il ne peut pas y avoir de réponse intelligente de la racine. Les noms en 10.in-addr.arpa doivent donc être résolus localement, et sont donc, eux aussi, des « noms de domaine spéciaux ». Par contre, contrairement à .local ou à .onion, ils sont résolus par le DNS.

Pas fatigué·e·s ? Encore envie de lire ? Il y aussi l'étude d'Interisle sur les « collisions ». Derrière ce nom sensationnaliste conçu pour faire peur, il y a un vrai problème, le risque qu'un TLD récent masque, ou soit masqué par, un TLD alloué localement sans réfléchir (comme .dev). L'étude montrait par exemple que .home était celui posant le plus de risques.

Sur un sujet proche, il y a aussi une étude du SSAC, un comité ICANN.

On a dit plus haut que les noms de domaine « spéciaux » n'étaient pas forcément des TLD. C'est par exemple le cas d'un nom utilisé pour certaines manipulations IPv6, ipv4only.arpa, créé par le RFC 7050, mais qui, par suite d'un cafouillage dans le processus, n'a pas été ajouté au registre des noms de domaine spéciaux. Dommage : ce nom, n'étant pas un TLD et n'ayant pas de valeur particulière, n'avait pas posé de problème et avait été accepté rapidement.

Enfin, un dernier échec qu'il peut être utile de regarder, est la tentative d'enregistrer comme noms de domaine spéciaux des TLD très souvent alloués localement, et qu'il serait prudent de ne pas déléguer dans la racine, comme le .home cité plus haut. Un projet avait été rédigé en ce sens, mais n'avait jamais abouti, enlisé dans les sables procéduraux.

Si vous n'avez pas mal à la tête à ce stade, vous pouvez encore lire la section 5, qui rappelle l'histoire tourmentée de ce concept de noms de domaine spéciaux. Lorsque le DNS a été produit (RFC 882 et RFC 883) pour remplacer l'ancien système HOSTS.TXT (RFC 608), la transition ne s'est pas faite sans douleur, car plusieurs systèmes de résolution coexistaient (le plus sérieux étant sans doute les Yellow Pages sur Unix, mais il y avait aussi NetBIOS name service/WINS, qui ne tournait pas que sur Windows). Encore aujourd'hui, des anciens systèmes de résolution fonctionnent toujours. Le HOSTS.TXT survit sous la forme du /etc/hosts d'Unix (et de son équivalent Windows). Les systèmes d'exploitation ont en général un « commutateur » qui permet d'indiquer quel mécanisme de résolution utiliser pour quel nom. Voici un exemple d'un /etc/nsswitch.conf sur une machine Debian qui, pour résoudre un nom de domaine, va utiliser successivement /etc/hosts, LDAP, puis le DNS :

hosts:   files ldap dns
    

Le concept de TLD privé, connu uniquement en local, a été (bien à tort) recommandé par certaines entreprises comme Sun ou Microsoft. Il a survécu à la disparition des technologies qui l'utilisaient, comme Yellow Pages. Aujourd'hui, c'est une source d'ennuis sans fin, et bien des administrateurs réseau ont maudit leur prédécesseur pour avoir configuré ainsi tout le réseau local, allumant ainsi une bombe qui allait exploser quand le TLD « privé » s'est retrouvé délégué.

La discussion à l'IETF, notamment dans son groupe de travail DNSOP a été très chaude. Un premier document avait été élaboré, draft-adpkja-dnsop-special-names-problem, puis l'ancêtre de ce RFC avait été écrit, le premier document étant abandonné (il était très proche du point de vue de l'ICANN comme quoi seule l'ICANN devrait pouvoir créer des TLD, les autres acteurs n'étant que de vilains squatteurs).


Téléchargez le RFC 8244


L'article seul

RFC 8257: Data Center TCP (DCTCP): TCP Congestion Control for Data Centers

Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : S. Bensley, D. Thaler, P. Balasubramanian (Microsoft), L. Eggert (NetApp), G. Judd (Morgan Stanley)
Pour information
Réalisé dans le cadre du groupe de travail IETF tcpm
Première rédaction de cet article le 19 octobre 2017


DCTCP (Datacenter TCP), décrit dans ce RFC (qui n'est pas une norme, attention), est une variante de TCP avec un contrôle de congestion moins prudent. Elle est conçue pour le cas particulier des centres de données et ne doit pas être utilisée sur l'Internet public.

DCTCP repose sur la technique ECN du RFC 3168. Normalement, cette technique permet de signaler la congestion plus tôt qu'avec la méthode traditionnelle d'attendre les pertes de paquets. L'ECN est binaire : il y a eu de la congestion ou il n'y en a pas eu. DCTCP va plus loin et utilise ECN pour estimer le nombre d'octets qui ont rencontré de la congestion. DCTCP va ensuite refermer sa fenêtre (le nombre d'octets qu'on peut envoyer avant d'avoir reçu des accusés de réception) plus lentement que le TCP classique (et c'est pour cela que la concurrence entre eux est inégale, et que DCTCP ne doit pas être activé dans l'Internet, mais seulement dans des environnements fermés).

Quelles sont les particularités du réseau dans un centre de données ? Il faut beaucoup de commutateurs pour relier ces machines. Il est tentant d'utiliser pour cela des commutateurs bon marché. Mais ceux-ci ont des tampons de taille limitée, et le risque de congestion est donc plus élevé. Dans un centre de données, les flots sont de deux types : des courts et des longs. Les courts veulent en général une faible latence et les longs sont souvent davantage intéressés par une forte capacité. Enfin, le trafic est souvent très synchronisé. Si on fait du MapReduce, tous les serveurs vont voir de l'activité réseau en même temps (quand le travail est réparti, et quand il se termine).

Le cahier des charges des commutateurs est donc plein de contradictions :

  • Tampon de petite taille, pour ne pas augmenter la latence (attention au bufferbloat),
  • Tampon de grande taille pour être sûr de pouvoir toujours utiliser les liens de sortie au maximum,
  • Tampon de grande taille pour pouvoir encaisser les brusques variations de trafic, lorsqu'un flot bavard commence (par exemple une distribution d'un nouveau travail MapReduce).

Avec le TCP traditionnel (pré-ECN), l'indicateur de congestion est la perte de paquets, détectée par l'absence d'accusé de réception. (Voir le RFC 5681 pour une bonne synthèse sur le contrôle de congestion dans TCP.) Attendre la perte de paquets pour ralentir n'est pas très efficace : pour un flot court qui rencontre de la congestion au début, la majorité des paquets aura été jetée avant que TCP ne puisse ralentir. D'où l'invention d'ECN (RFC 3168). ECN permet de réagir avant qu'on perde des paquets. Mais, comme expliqué plus haut, il est binaire : il détecte la congestion, pas son importance. Il va donc souvent mener TCP à refermer trop énergiquement la fenêtre d'envoi.

La section 3 du RFC présente les algorithmes à utiliser. Les commutateurs/routeurs détectent la congestion et la signalent via ECN (RFC 3168). Les récepteurs des données renvoient l'ECN à l'émetteur et celui-ci réduit sa fenêtre de congestion (cwnd pour Congestion WiNDow, cf. RFC 5681, section 2). Tout ceci est le fonctionnement classique d'ECN. C'est surtout dans la dernière étape, le calcul de la réduction de la fenêtre, que DCTCP apporte des nouveautés. Mais, avant, quelques détails sur les deux premières étapes.

D'abord, la décision des commutateurs et·ou routeurs de considérer qu'il y a congestion. Fondamentalement, c'est une décision locale, qui n'est pas standardisée. En général, on décide qu'il y a congestion dès que le temps de séjour des paquets dans les tampons du commutateur/routeur augmente « trop ». On n'attend donc pas que les files d'attente soient pleines (si elles sont grandes - bufferbloat - la latence va augmenter sérieusement bien avant qu'elles ne soient pleines). Une fois que l'engin décide qu'il y a congestion, il marque les paquets avec ECN (bit CE - Congestion Experienced, cf. RFC 3168).

Le récepteur du paquet va alors se dire « ouh là, ce paquet a rencontré de la congestion sur son trajet, il faut que je prévienne l'émetteur de se calmer » et il va mettre le bit ECE dans ses accusés de réception. Ça, c'est l'ECN normal. Mais pour DCTCP, il faut davantage de détails, puisqu'on veut savoir précisément quels octets ont rencontré de la congestion. Une possibilité serait d'envoyer un accusé de réception à chaque segment (paquet TCP), avec le bit ECE si ce segment a rencontré de la congestion. Mais cela empêcherait d'utiliser des optimisations très utiles de TCP, comme les accusés de réception retardés (on attend un peu de voir si un autre segment arrive, pour pouvoir accuser réception des deux avec un seul paquet). À la place, DCTCP utilise une nouvelle variable booléenne locale chez le récepteur qui stocke l'état CE du précédent segment. On envoie un accusé de réception dès que cette variable change d'état. Ainsi, l'accusé de réception des octets M à N indique, selon qu'il a le bit ECE ou pas, que tous ces octets ont eu ou n'ont pas eu de congestion.

Et chez l'émetteur qui reçoit ces nouvelles notifications de congestion plus subtiles ? Il va s'en servir pour déterminer quel pourcentage des octets qu'il a envoyé ont rencontré de la congestion. Les détails du calcul (dont une partie est laissé à l'implémenteur, cf. section 4.2) figurent en section 3.3. Le résultat est stocké dans une nouvelle variable locale, DCTCP.Alpha.

Une fois ces calculs faits et cette variable disponible, lorsque la congestion apparait, au lieu de diviser brusquement sa fenêtre de congestion, l'émetteur la fermera plus doucement, par la formule cwnd = cwnd * (1 - DCTCP.Alpha / 2) (où cwnd est la taille de la fenêtre de congestion ; avec l'ancien algorithme, tout se passait comme si tous les octets avaient subi la congestion, donc DCTCP.Alpha = 1).

La formule ci-dessus était pour la cas où la congestion était signalée par ECN. Si elle était signalée par une perte de paquets, DCTCP se conduit comme le TCP traditionnel, divisant sa fenêtre par deux. De même, une fois la congestion passée, Datacenter TCP agrandit sa fenêtre exactement comme un TCP normal.

Voilà, l'algorithme est là, il n'y a plus qu'à le mettre en œuvre. Cela mène à quelques points subtils, que traite la section 4. Par exemple, on a dit que DCTCP, plus agressif qu'un TCP habituel, ne doit pas rentrer en concurrence avec lui (car il gagnerait toujours). Une implémentation de DCTCP doit donc savoir quand activer le nouvel algorithme et quand garder le comportement conservateur traditionnel. (Cela ne peut pas être automatique, puisque TCP ne fournit pas de moyen de négocier l'algorithme de gestion de la congestion avec son pair.) On pense à une variable globale (configurée avec sysctl sur Unix) mais cela ne suffit pas : la même machine dans le centre de données peut avoir besoin de communiquer avec d'autres machines du centre, en utilisant DCTCP, et avec l'extérieur, où il ne faut pas l'utiliser. Il faut donc utiliser des configurations du genre « DCTCP activé pour les machines dans le même /48 que moi ».

Une solution plus rigolote mais un peu risquée, serait d'activer DCTCP dès que la mesure du RTT indique une valeur inférieure à N millisecondes, où N est assez bas pour qu'on soit sûr que seules les machines de la même tribu soient concernées.

Après le programmeur en section 4, l'administrateur réseaux en section 5. Comment déployer proprement DCTCP ? Comme on a vu que les flots TCP traditionnels et DCTCP coexistaient mal, la section 5 recommande de les séparer. Par exemple, l'article « Attaining the Promise and Avoiding the Pitfalls of TCP in the Datacenter » décrit un déploiement où le DSCP (RFC 2474) d'IPv4 est utilisé pour distinguer les deux TCP, ce qui permet d'appliquer de l'AQM (RFC 7567) à DCTCP et des méthodes plus traditionnelles (laisser tomber le dernier paquet en cas de congestion) au TCP habituel. (Il faut aussi penser au trafic non-TCP, ICMP, par exemple, quand on configure ses commutateurs/routeurs.)

Aujourd'hui, DCTCP est déjà largement déployé et ce RFC ne fait que prendre acte de ce déploiement On trouve DCTCP dans Linux (cf. ce commit de 2014, notez les mesures de performance qui accompagnent sa description), dans FreeBSD (ce commit, et cette description de l'implémentation), et sur Windows (cette fois, on ne peut pas voir le source mais il y a une documentation). Sur Linux, on peut voir la liste des algorithmes de gestion de la congestion qui ont été compilés dans ce noyau :

% sysctl net.ipv4.tcp_available_congestion_control  
net.ipv4.tcp_available_congestion_control = cubic reno
    

Si DCTCP manque, c'est peut-être parce qu'il faut charger le module :

% modprobe tcp_dctcp
% sysctl net.ipv4.tcp_available_congestion_control  
net.ipv4.tcp_available_congestion_control = cubic reno dctcp
    

Si DCTCP se trouve dans la liste, on peut l'activer (c'est une activation globale, par défaut) :

% sysctl -w net.ipv4.tcp_congestion_control=dctcp
    

Pour le faire uniquement vers certaines destinations (par exemple à l'intérieur du centre de données) :

%  ip route add 192.168.0.0/16 congctl dctcp
    

Le choix des algorithmes de gestion de la congestion peut également être fait par chaque application (setsockopt(ns, IPPROTO_TCP, TCP_CONGESTION, …).

Enfin, la section 6 du RFC rassemble quelques problèmes non résolus avec DCTCP :

  • Si les estimations de congestion sont fausses, les calculs de DCTCP seront faux. C'est particulièrement un problème en cas de perte de paquets, problème peu étudié pour l'instant.
  • Comme indiqué plus haut, DCTCP n'a aucun mécanisme pour négocier dynamiquement son utilisation. Il ne peut donc pas coexister avec le TCP traditionnel mais, pire, il ne peut pas non plus partager gentiment le réseau avec un futur mécanisme qui, lui aussi, « enrichirait » ECN. (Cf. la thèse de Midori Kato.)

Enfin, la section 7 du RFC, portant sur la sécurité, note que DCTCP hérite des faiblesses de sécurité d'ECN (les bits ECN dans les en-têtes IP et TCP peuvent être modifiés par un attaquant actif) mais que c'est moins grave pour DCTCP, qui ne tourne que dans des environnements fermés.

Si vous aimez lire, l'article original décrivant DCTCP en 2010 est celui de Alizadeh, M., Greenberg, A., Maltz, D., Padhye, J., Patel, P., Prabhakar, B., Sengupta, S., et M. Sridharan, « Data Center TCP (DCTCP) ». Le dinosaure ACM ne le rendant pas disponible librement, il faut le récupérer sur Sci-Hub (encore merci aux créateurs de ce service).

Merci à djanos pour ses nombreuses corrections sur la gestion de DCTCP dans Linux.


Téléchargez le RFC 8257


L'article seul

RFC 8221: Cryptographic Algorithm Implementation Requirements and Usage Guidance for Encapsulating Security Payload (ESP) and Authentication Header (AH)

Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : P. Wouters (Red Hat), D. Migault, J. Mattsson (Ericsson), Y. Nir (Check Point), T. Kivinen
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF ipsecme
Première rédaction de cet article le 18 octobre 2017


Le protocole de cryptographie IPsec vient avec une liste d'obligations concernant les algorithmes cryptographiques qu'il faut inclure. Cette liste figure dans ce RFC 8221 (qui remplace l'ancien RFC 7321, avec beaucoup de changements). Ainsi, les différentes mises en œuvre d'IPsec sont sûres d'avoir un jeu d'algorithmes corrects en commun, assurant ainsi l'interopérabilité. Cette nouvelle version marque notamment l'officialisation de l'algorithme ChaCha20.

Ce nouveau RFC concerne les deux services d'IPsec, ESP (Encapsulating Security Payload, RFC 4303) et AH (Authentication Header, RFC 4302). Les RFC normatifs sur IPsec se veulent stables, alors que la cryptographie évolue. D'où le choix de mettre les algorithmes dans un RFC à part. Par exemple, la section 3.2 du RFC 4303 note « The mandatory-to-implement algorithms for use with ESP are described in a separate RFC, to facilitate updating the algorithm requirements independently from the protocol per se » (c'était à l'époque le RFC 4305, remplacé depuis par le RFC 4835, puis par le RFC 7321, puis par notre RFC 8221, trois ans après son prédécesseur).

Ce RFC « extérieur » à IPsec spécifie les algorithmes obligatoires, ceux sur lesquels on peut toujours compter que le pair IPsec les comprenne, ceux qui ne sont pas encore obligatoires mais qu'il vaut mieux mettre en œuvre car ils vont sans doute le devenir dans le futur, et ceux qui sont au contraire déconseillés, en général suite aux progrès de la cryptanalyse, qui nécessitent de réviser régulièrement ce RFC (voir section 1.1). Cette subtilité (différence entre « obligatoire aujourd'hui » et « sans doute obligatoire demain ») mène à une légère adaptation des termes officiels du RFC 2119 : MUST- (avec le signe moins à la fin) est utilisé pour un algorithme obligatoire aujourd'hui mais qui ne le sera sans doute plus demain, en raison des avancées cryptanalytiques, et SHOULD+ est pour un algorithme qui n'est pas obligatoire maintenant mais le deviendra sans doute.

Les section 5 et 6 donnent la liste des algorithmes. Je ne les répète pas intégralement ici, d'autant plus que tous les algorithmes ne sont pas cités, seulement ceux qu'il faut mettre en œuvre, car ils sont fiables, et ceux qu'il ne faut pas mettre en œuvre, car trop cassés, et qui doivent donc être documentés pour mettre les programmeurs en garde. Parmi les points à noter :

  • ESP a un mode de chiffrement intègre (authenticated encryption qu'on peut aussi traduire par chiffrement vérifié ou chiffrement authentifié, que je n'aime pas trop parce qu'on peut confondre avec l'authentification, cf. RFC 5116). Ce mode est préféré à celui où chiffrement et intégrité sont séparés. Ce mode a un algorithme obligatoire, AES-GCM, décrit dans le RFC 4106 (il était SHOULD+ dans le RFC 7321). Il y aussi un algorithme recommandé (SHOULD, pas SHOULD+ car trop récent et pas encore assez souvent présent dans les mises en œuvres d'IPsec), ChaCha20-Poly1305 (normalisé dans le RFC 7539), qui fait son entrée dans la famille IPsec (cf. RFC 7634). Il semble bien parti pour remplacer AES-GCM un jour. Dans le RFC 7321, chiffrement intègre et chiffrement tout court étaient décrits séparément mais ils sont depuis dans le même tableau.
  • Le mode le plus connu d'ESP, celui de chiffrement tout court (le chiffrement intègre est désormais préféré), a deux algorithmes obligatoires (sans compter AES-GCM, cité plus haut, qui permet le chiffrement intègre), ces deux algorithmes sont AES-CBC (RFC 3602) et le surprenant NULL, c'est-à-dire l'absence de chiffrement (RFC 2410 ; on peut utiliser ESP pour l'authentification seule, d'où cet algorithme). Il y a aussi plusieurs algorithmes notés MUST NOT, comme DES-CBC (RFC 2405). Ils ne doivent pas être programmés, afin d'être sûr qu'on ne s'en serve pas.
  • Le mode d'authentification (enfin, intégrité serait peut-être un meilleur mot mais c'est subtil) d'ESP a un MUST, HMAC-SHA-256 (RFC 4868). L'étoile de SHA-1 (RFC 2404) baissant sérieusement, HMAC-SHA1 n'est plus que MUST-. AES-GMAC, GMAC étant une variante de GCM, qui était en SHOULD+ dans le vieux RFC, redescend en MAY.
  • Et AH, lui, a les mêmes algorithmes que ce mode d'authentification d'ESP.

La section 4 donne des conseils sur l'utilisation d'ESP et AH. AH ne fournit que l'authentification, alors qu'ESP peut fournir également le chiffrement. Bien sûr, le chiffrement sans l'authentification ne sert pas à grand'chose, puisqu'on risque alors de parler à l'homme du milieu sans le savoir (voir l'article de Bellovin, S. « Problem areas for the IP security protocols » dans les Proceedings of the Sixth Usenix Unix Security Symposium en 1996). Certaines combinaisons d'algorithmes ne sont pas sûres, par exemple, évidemment, ESP avec les algorithmes de chiffrement et d'authentification tous les deux à NULL (voir par exemple l'article de Paterson, K. et J. Degabriele, « On the (in)security of IPsec in MAC-then-encrypt configurations » à l'ACM Conference on Computer and Communications Security en 2010). Si on veut de l'authentification/intégrité sans chiffrement, le RFC recommande d'utiliser ESP avec le chiffrement NULL, plutôt que AH. En fait, AH est rarement utile, puisque ESP en est presque un sur-ensemble, et il y a même eu des propositions de le supprimer. AH avait été prévu pour une époque où le chiffrement était interdit d'utilisation ou d'exportation dans certains pays et un logiciel n'ayant que AH posait donc moins de problèmes légaux. Aujourd'hui, la seule raison d'utiliser encore AH est si on veut protéger certains champs de l'en-tête IP, qu'ESP ne défend pas.

Le chiffrement intègre/authentifié d'un algorithme comme AES-GCM (RFC 5116 et RFC 4106) est la solution recommandée dans la plupart des cas. L'intégration du chiffrement et de la vérification d'intégrité est probablement la meilleure façon d'obtenir une forte sécurité.

Triple DES et DES, eux, ont des défauts connus et ne doivent plus être utilisés. Triple DES a une taille de bloc trop faible et, au-delà d'un gigaoctet de données chiffrées avec la même clé, il laisse fuiter des informations à un écoutant, qui peuvent l'aider dans son travail de décryptage. Comme, en prime, il est plus lent qu'AES, il n'y a vraiment aucune raison de l'utiliser. (DES est encore pire, avec sa clé bien trop courte. Il a été cassé avec du matériel dont les plans sont publics.)

Triple DES étant sorti, la « solution de remplacement », si un gros problème est découvert dans AES, sera ChaCha20. Il n'y a aucune indication qu'une telle vulnérabilité existe mais il faut toujours garder un algorithme d'avance.

Pour l'authentification/intégrité, MD5, ayant des vulnérabilités connues (RFC 6151), question résistance aux collisions, est relégué en MUST NOT. (Notez que des vulnérabilités à des collisions ne gênent pas forcément l'utilisation dans HMAC.) SHA-1 a des vulnérabilités analogues mais qui ne concernent pas non plus son utilisation dans HMAC-SHA1, qui est donc toujours en MUST- (le moins indiquant son futur retrait). Les membres de la famille SHA-2 n'ont pas ces défauts, et sont désormais MUST pour SHA-256 et SHOULD pour SHA-512.

Voilà, c'est fini, la section 10 sur la sécurité rappelle juste quelques règles bien connues, notamment que la sécurité d'un système cryptographique dépend certes des algorithmes utilisés mais aussi de la qualité des clés, et de tout l'environnement (logiciels, humains).

Ce RFC se conclut en rappelant que, de même qu'il a remplacé ses prédécesseurs comme le RFC 7321, il sera probablement à son tour remplacé par d'autres RFC, au fur et à mesure des progrès de la recherche en cryptographie.

Si vous voulez comparer avec un autre document sur les algorithmes cryptographiques à choisir, vous pouvez par exemple regarder l'annexe B1 du RGS, disponible en ligne.

Merci à Florian Maury pour sa relecture acharnée. Naturellement, comme c'est moi qui tiens le clavier, les erreurs et horreurs restantes viennent de ma seule décision. De toute façon, vous n'alliez pas vous lancer dans la programmation IPsec sur la base de ce seul article, non ?

La section 8 du RFC résume les changements depuis le RFC 7321. Le RFC a été sérieusement réorganisé, avec une seule section regroupant les algorithmes avec AEAD et sans (j'ai gardé ici la présentation séparée de mon précédent article). Les sections d'analyse et de discussion ont donc été très modifiées. Par exemple, le RFC 7321 s'inquiétait de l'absence d'alternative à AES, problème qui a été résolu par ChaCha20. Parmi les principaux changements d'algorithmes :

  • AES-GCM prend du galon en passant de SHOULD+ à MUST comme prévu,
  • ChaCha20 et Poly1305 (RFC 7539 et RFC 7634) font leur apparition (ils n'étaient pas mentionnés dans le RFC 7321),
  • SHA-1, lui, recule, au profit de SHA-2,
  • Triple DES est désormais très déconseillé (SHOULD NOT),
  • Un nouveau concept apparait, celui des algorithmes cryptographiques « pour l'IoT », un domaine qui pose des problèmes particuliers, en raisons des fortes contraintes pesant sur ces objets (peu de mémoire, peu de puissance électrique disponible, des processeurs lents). C'est ainsi qu'AES-CCM (RFC 4309) passe à SHOULD mais uniquement pour l'Internet des Objets, et qu'AES-XCBC (RFC 3566), qui semblait voué à disparaitre, refait surface spécifiquement pour les objets connectés (peut remplacer HMAC pour l'authentification, afin de ne pas avoir à implémenter SHA-2). Notez que XCBC est peu connu et n'a même pas de page dans le Wikipédia anglophone ☺

En pratique, ce RFC est déjà largement mis en œuvre, la plupart des algorithmes cités sont présents (quand ils sont MUST ou SHOULD) ou absents (quand ils sont MUST NOT) dans les implémentations d'IPsec. Une exception est peut-être ChaCha20, qui est loin d'être présent partout. Malgré cela, ce RFC n'a pas suscité de controverses particulières, l'IETF avait un consensus sur ces nouveaux choix.


Téléchargez le RFC 8221


L'article seul

RFC 8255: Multiple Language Content Type

Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : N. Tomkinson, N. Borenstein (Mimecast)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF slim
Première rédaction de cet article le 10 octobre 2017


La norme MIME permet d'étiqueter un message en indiquant la langue, avec l'en-tête Content-Language: (RFC 4021). Mais comment faire si on veut envoyer le même message en plusieurs langues, pour s'adapter à une audience variée, ou bien si on n'est pas sûr des langues parlées par le destinataire ? C'est ce que permet le nouveau type de message multipart/multilingual qui permet d'étiqueter les messages multilingues.

C'est le premier RFC du groupe de travail SLIM, chargé de créer des normes pour indiquer la langue utilisée dans le courrier et pour les communications synchrones (téléphonie sur IP, par exemple), dans le cas où plusieurs langues sont en présence.

Le type de premier niveau multipart/ (RFC 2046, section 5.1, enregistré à l'IANA) permet d'indiquer un message (ou une partie de message, en cas de récursivité) composé de plusieurs parties. Ainsi, multipart/mixed (RFC 2046, section 5.1.3) indique un message dont les dfférentes parties sont indépendantes (un texte et une image, par exemple). Alors que multipart/alternative (RFC 2046, section 5.1.4) est utilisé pour le cas où les différentes parties veulent dire la même chose, mais avec des formats différentes (une version texte seul et une version HTML, par exemple, ou bien une image en JPEG et la même en PNG). Normalement, le lecteur de courrier ne va afficher qu'une seule des parties d'une multipart/alternative, celle qui convient le mieux, selon les capacités du logiciel de lecture et les préférences de l'utilisateur. Ce sera la même chose avec ce nouveau multipart/multilingual : l'émetteur enverra le message en plusieurs langues, le logiciel de lecture n'affichera que celle qui colle le mieux aux choix de l'utilisateur. Ce type est désormais enregistré à l'IANA (section 9 du RFC).

Rappelez-vous que MIME est récursif : un multipart/ peut contenir un autre multipart/. Voici, par exemple, vu par le MUA mutt, un multipart/mixed, dont la première partie est un multipart/alternative et la seconde un fichier PDF :

      
I     1 <no description>                                [multipart/alternative, 7bit, 45K]
I     2 ├─><no description>                               [text/plain, 8bit, utf-8, 1,8K]
I     3 └─><no description>                               [text/html, 8bit, utf-8, 43K]
A     4 35378031645-672029836-registration.pdf          [application/pdf, base64, 84K]

    

Cette première partie du corps du message, de type multipart/alternative, a à son tour deux parties, une en texte brut et l'autre en HTML. Comme ma configuration mutt inclut la directive alternative_order text/plain, seule la version texte sera affichée, le HTML étant ignoré.

Revenons aux messages multilingues. Avant ce RFC 8255, la solution la plus fréquente, quand on envoyait un message à quelqu'un dont on n'était pas sûr des préférences linquistiques, était de mettre toutes les versions dans un même message. Du genre :

(English translation at the end)

Bonjour, ceci est un message.

Hello, this is a message.
    

(La première ligne ayant pour but d'éviter que le lecteur anglophone se décourage en ne voyant au début que du français.) Cette solution n'est pas satisfaisante : elle passe mal à l'échelle dès qu'il y a plus de deux langues. Et elle ne permet pas la sélection automatique de la langue par le logiciel du destinataire.

Le type MIME d'un message est mis dans un en-tête Content-Type:. Par exemple, un message de plusieurs parties représentant le même contenu serait :

Content-Type: multipart/alternative; boundary="------------E6043EF6F3B557567F3B18F4"
    

boundary indique le texte qui sera le séparateur entre les parties (RFC 2046, section 5.1). La section 2 du RFC décrit ce qu'on peut désormais mettre dans le Content-Type:, avec le nouveau type multipart/multilingual.

Il ressemble beaucoup au multipart/alternative et, comme tous les multipart/quelquechose (RFC 2046, section 5.1.1), a une chaîne de caractères qui indique le passage d'une partie à une autre :

Content-Type: multipart/multilingual; boundary=01189998819991197253
    

Chacune des parties d'un message en plusieurs langues doit indiquer la langue de la partie, avec l'en-tête Content-Language:. Mais attention, comme il faut tenir compte des vieux clients de messagerie qui ne connaissent pas ce RFC, la première partie ne doit pas avoir de Content-Language:, elle sert de solution de repli, car c'est elle qui sera affichée en premier par les logiciels qui ne connaissent pas multipart/multilingual. Comme elle sert de secours, il est recommandé qu'elle soit dans le format le plus simple, donc text/plain, en UTF-8 (car il faut pouvoir représenter diverses langues). Cette partie se nomme la préface. En la lisant, l'utilisateur pourra donc comprendre qu'il doit mettre à jour vers un logiciel plus récent.

Les parties suivantes, après la préface, vont être écrites dans des langues différentes. Le RFC recommande de les mettre dans l'ordre, avec celles qui ont le plus de chance d'être pertinentes en premier. (Ainsi, une société française ayant une activité européenne, avec une majorité de clients français, et envoyant un message en anglais, allemand et français, mettre sans doute le français en premier, suivi de l'anglais.)

Chacune de ces parties aura un en-tête Content-Language:, pour indiquer la langue, et Content-Type: pour indiquer le type MIME. (Rappelez-vous toujours que MIME est récursif.) Il est recommandé que chaque partie soit un message complet (avec notamment le champ Subject:, qui a besoin d'être traduit, lui aussi, et le champ From:, dont une partie peut être utilement traduite). Le type conseillé est donc message/rfc822 (RFC 2046, section 5.2.1), mais on peut aussi utiliser le plus récent message/global (RFC 6532).

Notez bien que ce RFC ne spécifie évidemment pas comment se fera la traduction : il ne s'occupe que d'étiqueter proprement le résultat.

Le message peut se terminer par une section « indépendante de la langue » (par exemple une image ne comportant pas de texte, si le message peut être porté par une image). Dans ce cas, son Content-Language: doit indiquer zxx, ce qui veut dire « information non pertinente » (RFC 5646, section 4.1). C'est elle qui sera sélectionnée si aucune partie ne correspond aux préférences de l'utilisateur.

Maintenant, que va faire le client de messagerie qui reçoit un tel message multilingue ? La section 4 de notre RFC décrit les différents cas. D'abord, avec les logiciels actuels, le client va afficher les différentes parties de multipart/multilingual dans l'ordre où elles apparaissent (donc, en commençant par la préface).

Mais le cas le plus intéressant est évidemment celui d'un client plus récent, qui connait les messages multilingues. Il va dans ce cas sauter la préface (qui n'a pas de langue indiquée, rappelez-vous) et sélectionner une des parties, celle qui convient le mieux à l'utilisateur.

Un moment. Arrêtons-nous un peu. C'est quoi, la « meilleure » version ? Imaginons un lecteur francophone, mais qui parle anglais couramment. Il reçoit un message multilingue, en français et en anglais. S'il choisissait manuellement, il prendrait forcément le français, non ? Eh bien non, car cela dépend de la qualité du texte. Comme peut le voir n'importe quel utilisateur du Web, les différentes versions linguistiques d'un site Web ne sont pas de qualité égale. Il y a le texte original, les traductions faites par un professionnel compétent, les traductions faites par le stagiaire, et celles faites par un programme (en général, les plus drôles). Sélectionner la meilleure version uniquement sur la langue n'est pas une bonne idée, comme le montre la mauvaise expérience de HTTP. Ici, pour reprendre notre exemple, si la version en anglais est la version originale, et que le français est le résultat d'une mauvaise traduction par un amateur, notre francophone qui comprend bien l'anglais va sans doute préférer la version en anglais.

Il est donc crucial d'indiquer le type de traduction effectuée, ce que permet le Content-Translation-Type: exposé plus loin, en section 6. (Les premières versions du projet qui a mené à ce RFC, naïvement, ignoraient complètement ce problème de la qualité de la traduction, et de la version originale.)

Donc, le mécanisme de sélection par le MUA de la « meilleure » partie dans un message multilingue n'est pas complètement spécifié. Mais il va dépendre des préférences de l'utilisateur, et sans doute des règles du RFC 4647.

Si aucune partie ne correspond aux choix de l'utilisateur, le RFC recommande que le MUA affiche la partie indépendante de la langue, ou, si elle n'existe pas, la partie après la préface. Le MUA peut également proposer le choix à l'utilisateur (« Vous n'avez indiqué qu'une langue, le français. Ce message est disponible en anglais et en chinois. Vous préférez lequel ? »)

La section 5 du RFC présente en détail l'utilisation de l'en-tête Content-Language:. Il doit suivre la norme existante de cet en-tête, le RFC 3282, et sa valeur doit donc être une étiquette de langue du RFC 5646. Des exemples (en pratique, il ne faut évidemment en mettre qu'un) :

Content-Language: fr

Content-Language: sr-Cyrl

Content-Language: ay

Content-Language: aaq
    

Le premier exemple concerne le français, le second le serbe écrit en alphabet cyrillique, le troisième l'aymara et le quatrième l'abénaqui oriental.

La section décrit le nouvel en-tête Content-Translation-Type: qui indique le type de traduction réalisé. Il peut prendre trois valeurs, original (la version originale), human (traduit par un humain) et automated (traduit par un programme). Notez que les humains (les programmes aussi, d'ailleurs) varient considérablement dans leurs compétences de traducteur. J'aurais personnellement préféré qu'on distingue un traducteur professionnel d'un amateur, mais la traduction fait partie des métiers mal compris, où beaucoup de gens croient que si on parle italien et allemand, on peut traduire de l'italien en allemand correctement. C'est loin d'être le cas. (D'un autre côté, comme pour tous les étiquetages, si on augmente le nombre de choix, on rend l'étiquetage plus difficile et il risque d'être incorrect.)

Voici un exemple complet, tel qu'il apparait entre deux logiciels de messagerie. Il est fortement inspiré du premier exemple de la section 8 du RFC, qui comprend également des exemples plus complexes. Dans ce message, l'original est en anglais, mais une traduction française a été faite par un humain.

	
From: jeanne@example.com
To: jack@example.com
Subject: Example of a message in French and English
Date: Thu, 7 Apr 2017 21:28:00 +0100
MIME-Version: 1.0
Content-Type: multipart/multilingual;
           boundary="01189998819991197253"

--01189998819991197253
Content-Type: text/plain; charset="UTF-8"
Content-Disposition: inline
Content-Transfer-Encoding: 8bit

This is a message in multiple languages.  It says the
same thing in each language.  If you can read it in one language,
you can ignore the other translations. The other translations may be
presented as attachments or grouped together.

Ce message est disponible en plusieurs langues, disant la même chose
dans toutes les langues. Si vous le lisez dans une des langues, vous
pouvez donc ignorer les autres. Dans votre logiciel de messagerie, ces
autres traductions peuvent se présenter comme des pièces jointes, ou
bien collées ensemble.

--01189998819991197253
Content-Type: message/rfc822
Content-Language: en
Content-Translation-Type: original
Content-Disposition: inline

From: Manager <jeanne@example.com>
Subject: Example of a message in French and English
Content-Type: text/plain; charset="US-ASCII"
Content-Transfer-Encoding: 7bit
MIME-Version: 1.0

Hello, this message content is provided in your language.

--01189998819991197253
Content-Type: message/rfc822
Content-Language: fr
Content-Translation-Type: human
Content-Disposition: inline

From: Directrice <jeanne@example.com>
Subject: =?utf-8?q?Message_d=27exemple=2C_en_fran=C3=A7ais_et_en_anglais?=
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0

Bonjour, ce message est disponible dans votre langue.

--01189998819991197253--

      

On note qu'il y a une préface (la première partie, celle qui commence par « This is a message in multiple languages »), que chacun des deux parties étiquetées a un From: (pour pouvoir adapter le titre de l'expéditrice) et un sujet (celui en français étant encodé selon le RFC 2047). Les deux dernières parties ont un Content-Translation-Type:.

Actuellement, il ne semble pas qu'il existe de MUA qui gère cette nouvelle norme. Si vous voulez envoyer des messages à ce format, vous pouvez copier/coller l'exemple ci-dessus, ou bien utiliser le programme Python send-multilingual.py.

Voici le message d'exemple cité plus haut affiché par mutt (version NeoMutt 20170113 (1.7.2)) : mutt-multilingual.png

Thunderbird affiche à peu près la même chose.

Gmail a stupidement décidé de mettre ce message dans le dossier Spam. Une fois le message sorti de ce purgatoire, voici ce que ça donnait : gmail-multilingual.png


Téléchargez le RFC 8255


L'article seul

RFC 8265: Preparation, Enforcement, and Comparison of Internationalized Strings Representing Usernames and Passwords

Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : P. Saint-Andre (Jabber.org), A. Melnikov (Isode)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF precis
Première rédaction de cet article le 6 octobre 2017


Ah, les plaisirs de l'internationalisation du logiciel... Quand l'informatique ne concernait que les États-Unis, tout était simple. Un utilisateur ne pouvait avoir un nom (un login) que s'il était composé uniquement des lettres de l'alphabet latin (et même chose pour son mot de passe). Mais de nos jours, il n'est pas acceptable de se limiter au RFC 20. Il faut que Пу́тин ou 艾未未 puissent écrire leur nom dans leur écriture et l'informatique doit suivre. Notre RFC décrit comment le faire (il remplace le RFC 7613, mais il y a peu de changements, rassurez-vous).

Mais pourquoi faut-il standardiser quelque chose ? Pourquoi ne pas dire simplement « les noms d'utilisateur sont en Unicode » et basta ? Parce que les logiciels qui gèrent noms d'utilisateurs et mots de passe ont besoin de les manipuler, notamment de les comparer. Si ПУ́ТИН essaie de se loguer, et que la base de données contient un utilisateur пу́тин, il faut bien déterminer si c'est le même utilisateur (ici, oui, à part la casse). C'est en général assez simple dans l'alphabet latin (ou dans le cyrillique utilisé pour les exemples) mais il y a d'autres cas plus vicieux qui nécessitent quelques règles supplémentaires.

Le cadre général de l'internationalisation des identificateurs est normalisé dans le RFC 8264. Notre nouveau RFC 8265 est l'adaptation de ce RFC 8264 au cas spécifique des noms d'utilisateur et des mots de passe.

Ces noms et ces mots de passe (aujourd'hui, il faudrait plutôt dire phrases de passe) sont largement utilisés pour l'authentification, soit directement (SASL PLAIN du RFC 4616, authentification de base de HTTP du RFC 7617), ou bien comme entrée d'une fonction de condensation cryptographique (SASL SCRAM du RFC 5802 ou bien authentification HTTP digest du RFC 7616). L'idée est que les opérations de base sur ces noms (et sur les mots de passe) ne surprennent pas excessivement l'utilisateur, quel que soit son pays, sa langue, sa culture. Un Français ou un Belge ne sera probablement pas trop étonné que Dupont soit accepté comme synonyme de dupont mais il le serait davantage si dupond l'était. Évidemment, la tâche est impossible (les utilisateurs sont tous différents) mais l'idée est de ne pas faire un système parfait mais un système qui marche la plupart du temps.

C'est là qu'intervient le cadre PRECIS (PReparation, Enforcement, and Comparison of Internationalized Strings) du RFC 8264. Il évite que chaque développeur d'un système d'authentification doive lire et comprendre toutes les conséquences d'Unicode, et lui permet de s'appuyer sur une analyse déjà faite. Un exemple de piège d'Unicode (et qui montre pourquoi « on va juste dire que les noms d'utilisateur peuvent être n'importe quel caractère Unicode » est sans doute une mauvaise politique) est le chiffre 1 en exposant, U+00B9 (comme ça : « ¹ » Si vous ne voyez rien, c'est parce que votre logiciel ne sait pas afficher ce caractère. Vous pouvez toujours regarder le source HTML pour comprendre l'exemple.). Doit-on l'autoriser ? Le mettre en correspondance avec le 1 traditionnel de façon à ce que user¹ et user1 soient le même nom ? Imaginez un client XMPP qui vous dise « user¹@example.com veut être votre copain. Je l'autorise ? » et que vous acceptiez en croyant que c'était user1@example.com. Bien sûr, on ne peut pas réellement parler d'« attaque » dans ce cas, une telle erreur permettrait juste de faire quelques farces mais, pour le logiciel qui vérifie une identité, des confusions seraient plus gênantes. Si les « attaques » exploitant la similitude de caractères Unicode sont surtout un fantasme d'informaticien anglophone n'ayant jamais réellement accepté l'internationalisation (plutôt qu'une réalité du terrain), il est quand même plus prudent de supprimer quelques causes de cafouillage le plus tôt possible.

(Ce RFC suggère également de séparer le nom d'utilisateur, identificateur formel et n'utilisant qu'un jeu de caractères restreint, et la description (cf. RFC 8266) qui pourrait utiliser d'avantage de caractères. Twitter ou Mastodon fonctionnent ainsi.)

Notre RFC compte deux sections importantes, décrivant le profil PRECIS pour les noms d'utilisateur (section 3) et les mots de passe (section 4). Commençons par les noms d'utilisateur. Un nom est une chaîne de caractères Unicode composée de parties séparées par des espaces. Chaque partie doit être une instance de IdentifierClass et est normalisée en NFC. Pourquoi cette notion de « parties séparées par des espaces » ? Parce que la classe IdentifierClass du RFC 8264 ne permet pas les espaces, ce qui est gênant pour certains identificateurs (« Prénom Nom » par exemple, cf. section 3.5). D'où la grammaire de la section 3.1 :

      username   = userpart *(1*SP userpart)
    

qui dit « un nom d'utilisateur est composé d'au moins une partie, les parties étant séparées par un nombre quelconque d'espaces ». Une des conséquences de cette grammaire étant que le nombre d'espaces n'est pas significatif : Jean Dupont et Jean Dupont sont le même identificateur.

Chaque partie étant une instance de l'IdentifierClass du RFC 8264, les caractères interdits par cette classe sont donc interdits pour les noms d'utilisateurs. Une application donnée peut restreindre (mais pas étendre) ce profil. Ces noms d'utilisateurs sont-ils sensibles à la casse ? Certains protocoles ont fait un choix et d'autres le choix opposé. Eh bien, il y a deux sous-profils, un sensible et un insensible (ce dernier étant recommandé). Les protocoles et applications utilisant ce RFC 8265 devront annoncer clairement lequel ils utilisent. Et les bibliothèques logicielles manipulant ces utilisateurs auront probablement une option pour indiquer le sous-profil à utiliser.

Le sous-profil UsernameCaseMapped rajoute donc une règle de préparation des chaînes de caractères : tout passer en minuscules (avant les comparaisons, les condensations cryptographiques, etc), en utilisant l'algorithme toLowerCase d'Unicode (section 3.13 de la norme Unicode ; et, oui, changer la casse est bien plus compliqué en Unicode qu'en ASCII). Une fois la préparation faite, on peut comparer octet par octet, si l'application a bien pris soin de définir l'encodage.

L'autre sous-profil, UsernameCasePreserved ne change pas la casse, comme son nom l'indique. ПУ́ТИН et пу́тин y sont donc deux identificateurs différents. C'est la seule différence entre les deux sous-profils. Notre RFC recommande le profil insensible à la casse, UsernameCaseMapped, pour éviter des surprises comme celles décrites dans le RFC 6943 (cf. section 8.2 de notre RFC).

Bon, tout ça est bien nébuleux et vous préféreriez des exemples ? Le RFC nous en fournit. D'abord, des identificateurs peut-être surprenants mais légaux (légaux par rapport à PRECIS : certains protocoles peuvent mettre des restrictions supplémentaires). Attention, pour bien les voir, il vous faut un navigateur Unicode, avec toutes les polices nécessaires :

  • juliet@example.com : le @ est autorisé donc un JID (identificateur XMPP) est légal.
  • fussball : un nom d'utilisateur traditionnel, purement ASCII, qui passera partout, même sur les systèmes les plus antédiluviens.
  • fußball : presque le même mais avec un peu d'Unicode. Bien qu'en allemand, on traite en général ces deux identificateurs comme identiques, pour PRECIS, ils sont différents. Si on veut éviter de la confusion aux germanophones, on peut interdire la création d'un des deux identificateurs si l'autre existe déjà : PRECIS ne donne que des règles miminales, on a toujours droit à sa propre politique derrière.
  • π : entièrement en Unicode, une lettre.
  • Σ : une lettre majuscule.
  • σ : la même en minuscule. Cet identificateur et le précédent seront identiques si on utilise le profil UsernameCaseMapped et différents si on utilise le profil UsernameCasePreserved.
  • ς : la même, lorsqu'elle est en fin de mot. Le cas de ce sigma final est compliqué, PRECIS ne tente pas de le résoudre. Comme pour le eszett plus haut, vous pouvez toujours ajouter des règles locales.

Par contre, ces noms d'utilisateurs ne sont pas valides :

  • Une chaîne de caractères vide.
  • HenriⅣ : le chiffre romain 4 à la fin est illégal (car il a ce qu'Unicode appelle « un équivalent en compatibilité », la chaîne « IV »).
  •  : seules les lettres sont acceptées, pas les symboles (même règle que pour les noms de domaine).

Continuons avec les mots de passe (section 4). Comme les noms, le mot de passe est une chaîne de caractères Unicode. Il doit être une instance de FreeformClass. Cette classe autorise bien plus de caractères que pour les noms d'utilisateur, ce qui est logique : un mot de passe doit avoir beaucoup d'entropie. Peu de caractères sont donc interdits (parmi eux, les caractères de contrôle, voir l'exemple plus bas). Les mots de passe sont sensibles à la casse.

Des exemples ? Légaux, d'abord :

  • correct horse battery staple : vous avez reconnu un fameux dessin de XKCD.
  • Correct Horse Battery Staple : les mots de passe sont sensibles à la casse, donc c'est un mot de passe différent du précédent.
  • πßå : un mot de passe en Unicode ne pose pas de problème.
  • Jack of ♦s : et les symboles sont acceptés, contrairement à ce qui se passe pour les noms d'utilisateur.
  • foo bar : le truc qui ressemble à un trait est l'espace de l'Ogham, qui doit normalement être normalisé en un espace ordinaire, donc ce mot de passe est équivalent à foo bar.

Par contre, ce mot de passe n'est pas valide :

Rappelez-vous que ces profils PRECIS ne spécifient que des règles minimales. Un protocole utilisant ce RFC peut ajouter d'autres restrictions (section 5). Par exemple, il peut imposer une longueur minimale à un mot de passe (la grammaire de la section 4.1 n'autorise pas les mots de passe vides mais elle autorise ceux d'un seul caractère, ce qui serait évidemment insuffisant pour la sécurité), une longueur maximale à un nom d'utilisateur, interdire certains caractères qui sont spéciaux pour ce protocole, etc.

Certains profils de PRECIS suggèrent d'être laxiste en acceptant certains caractères ou certaines variantes dans la façon d'écrire un mot (accepter strasse pour straße ? C'est ce qu'on nomme le principe de robustesse.) Mais notre RFC dit que c'est une mauvaise idée pour des mots utilisés dans la sécurité, comme ici (voir RFC 6943).

Les profils PRECIS ont été ajoutés au registre IANA (section 7 de notre RFC).

Un petit mot sur la sécurité et on a fini. La section 8 de notre RFC revient sur quelques points difficiles. Notamment, est-ce une bonne idée d'utiliser Unicode pour les mots de passe ? Ça se discute. D'un côté, cela augmente les possibilités (en simplifiant les hypothèses, avec un mot de passe de 8 caractères, on passe de 10^15 à 10^39 possibilités en permettant Unicode et plus seulement ASCII), donc l'entropie. D'un autre, cela rend plus compliquée la saisie du mot de passe, surtout sur un clavier avec lequel l'utilisateur n'est pas familier, surtout en tenant compte du fait que lors de la saisie d'un mot de passe, ce qu'on tape ne s'affiche pas. Le monde est imparfait et il faut choisir le moindre mal...

L'annexe A du RFC décrit les changements (peu nombreux) depuis l'ancien RFC 7613. Le principal est le passage de la conversion de casse de la fonction Unicode CaseFold() à toLowerCase(). Et UTF-8 n'est plus obligatoire, c'est désormais à l'application de décider (en pratique, cela ne changera rien, UTF-8 est déjà l'encodage recommandé dans l'écrasante majorité des applications). Sinon, il y a une petite correction dans l'ordre des opérations du profil UsernameCaseMapped, et quelques nettoyages et mises à jour.


Téléchargez le RFC 8265


L'article seul

RFC 8264: PRECIS Framework: Preparation, Enforcement, and Comparison of Internationalized Strings in Application Protocols

Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : P. Saint-Andre (Jabber.org), M. Blanchet (Viagenie)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF precis
Première rédaction de cet article le 6 octobre 2017


Dans la longue marche vers une plus grande internationalisation de l'Internet, la question des identificateurs (comme par exemple les noms de domaine) a toujours été délicate. Ce sont en effet à la fois des éléments techniques, traités automatiquement par les programmes, et des marqueurs d'identité, vus par les humains (par exemple sur des cartes de visite) et manipulés par eux. Plutôt que de laisser chaque protocole internationaliser ses identificateurs (plus ou moins bien), l'approche de ce RFC est unificatrice, en élaborant des règles qui peuvent servir à de larges classes d'identificateurs, pour de nombreux protocoles différents. Il remplace le premier RFC qui avait suivi cette voie, le RFC 7564, désormais dépassé (mais les changements sont peu importants, c'est juste de la maintenance).

Cette opposition entre « éléments techniques » et « textes prévus pour l'utilisateur » est au cœur du RFC 2277 qui pose comme principe politique qu'on internationalise les seconds, mais pas les premiers. Cet excellent principe achoppe souvent sur la question des identificateurs, qui sont les deux à la fois. D'un côté, les programmes doivent les traiter (les identificateurs doivent donc être clairement définis, sans ambiguïté), de l'autre les humains les voient, les communiquent, les échangent (les identificateurs doivent donc permettre, sinon toutes les constructions du langage humain, en tout cas un sous-ensemble d'Unicode qui parait raisonnable aux humains ordinaires : pas question d'imposer stephane comme nom de login à un utilisateur nommé Stéphane, avec un accent sur le E). C'est cette double nature des identificateurs (ainsi, il est vrai, que l'énorme couche de bureaucratie qui gère les noms de domaine) qui explique la durée et la vivacité des discussions sur les IDN.

Maintenant que ces IDN existent (depuis plus de quatorze ans, RFC 3490), que faire avec les autres identificateurs ? Une possibilité aurait été que chaque protocole se débrouille avec ses propres identificateurs, comme l'a fait le DNS avec les noms de domaine. Mais cela menait à une duplication du travail (et tous les auteurs de protocole ne sont pas des experts Unicode) et surtout à un risque de choix très différents : certains protocoles autoriseraient tel caractère Unicode et d'autres pas, sans que l'utilisateur ordinaire puisse comprendre clairement les raisons de cette différence. L'idée de base du groupe PRECIS était donc d'essayer de faire des règles qui s'appliqueraient à beaucoup de protocoles, épargnant aux concepteurs de ces protocoles de les concevoir eux-mêmes, et offrant à l'utilisateur une certaine homogénéité. Ce RFC 8264 est le cadre de définition des identificateurs internationalisés. Ce cadre permet la manipulation d'identificateurs internationalisés (par exemple leur comparaison, comme lorsqu'un utilisateur tape son nom et son mot de passe, et qu'il faut les vérifier, cf. RFC 6943.)

Certaines personnes, surtout en Anglosaxonnie, pourraient estimer que c'est bien compliqué tout cela, et qu'il vaudrait mieux limiter les identificateurs à ASCII. Certes, Unicode est compliqué, mais sa complexité ne fait que refléter celle des langues humaines (section 6.1 de notre RFC). On ne peut pas simplifier Unicode, sauf à éliminer une partie de la diversité humaine.

Le nom du groupe de travail PRECIS reflète les trois fonctions essentielles que font les programmes qui manipulent les identificateurs internationalisés :

  • PR pour la préparation des identificateurs, les opérations préliminaires comme la conversion depuis un jeu de caractères local non-Unicode,
  • E pour l'application (enforcement) des règles, afin de s'assurer qu'une chaîne de caractères Unicode est légale ou pas pour un usage donné,
  • C pour la comparaison entre les identificateurs, afin de déterminer leur égalité (« cette ressource n'est accessible qu'à l'utilisateur Thérèse or elle est connectée avec le nom therese »),
  • Le IS final étant pour Internationalized Strings.

A priori, les serveurs Internet seront responsables de l'application, les clients n'ayant à faire qu'une préparation. Ayant souvent moins de ressources, les clients pourraient en effet avoir du mal à faire certaines opérations Unicode complexes (section 3).

Les principes du travail de PRECIS sont :

  • Définir un petit nombre (deux, actuellement) de classes spécifiant un jeu de caractères autorisés, classes applicables à un grand nombre d'usages.
  • Définir le contenu de ces classes en fonction d'un algorithme, reposant sur les propriétés Unicode (contrairement à stringprep où le contenu des classes était énuméré dans le RFC). Ainsi, lorsqu'une nouvelle version d'Unicode sort (la version actuelle est la 10.0), il suffit de refaire tourner l'algorithme et on obtient le contenu à jour de la classe.
  • Spécifier les classes par une inclusion : tout caractère non explicitement listé comme membre de la classe est automatiquement exclu.
  • Permettre aux protocoles applicatifs de définir un profil d'une classe, à savoir une restriction de ses membres, ou bien d'autres précisions sur des sujets comme la normalisation Unicode. Il y a ainsi un profil pour les noms d'utilisateurs, un profil pour les mots de passe, etc (cf. RFC 8265). Notez qu'il est prévu que le nombre de profils reste limité, pour ne pas réintroduire l'excessive variété que voulait justement éviter PRECIS.

Si tout va bien, ces principes permettront l'indépendance vis-à-vis des versions d'Unicode (ou, plus exactement, la possibilité de passer à une nouvelle version d'Unicode sans faire une nouvelle norme incompatible), le partage des tables et du logiciel entre applications (par exemple par le biais de bibliothèques communes, utilisables si toutes les applications reposent sur PRECIS), et moins de surprises pour les utilisateurs, qui auraient été bien embêtés si chaque protocole Internet avait eu une manière complètement différente de prévoir l'internationalisation.

Bien, quelles sont donc les classes prévues par PRECIS (section 4) ? L'idée est de faire un compromis entre expressivité et sécurité. Qu'est-ce que la sécurité vient faire là dedans ? Personne ne le sait trop (le RFC utilise plusieurs fois safe et safety sans expliquer face à quels risques) mais Unicode fait souvent peur aux informaticiens anglo-saxons et il est donc courant d'estimer qu'il existe des caractères dangereux.

Il y a donc deux classes en tout et pour tout dans PRECIS : IdentifierClass et FreeformClass. La première classe servira à identifier utilisateurs, machines, pièces de discussions en messagerie instantanée, fichiers, etc, et ne permettra que les lettres, les nombres et quelques symboles (comme le ! ou le +, car ils étaient dans ASCII). C'est contraignant mais l'idée est qu'on veut des désignations simples et sans ambiguïté, pas écrire des romans. La seconde classe servira à tout le reste (mots de passe, textes affichés comme la description d'une pièce XMPP, nom complet d'une machine, etc). Par exemple, une imprimante sera imprimante-jean-et-thérèse pour les protocoles qui demandent un nom de la classe IdentifierClass et Imprimante de Jean & Thérèse lorsqu'on pourra utiliser un nom FreeformClass.

Les classes sont définies par les caractères inclus et exclus. Plus précisément, un caractère peut être, pour une classe donnée (voir aussi la section 8) :

  • Valide (ou PVALID pour Protocol Valid),
  • Interdit,
  • Autorisé dans certains contextes seulement, c'est-à-dire que l'autorisation dépendra des caractères voisins (pas facile de trouver un exemple dans l'alphabet latin, cela sert surtout à d'autres écritures),
  • Pas encore affecté dans Unicode.

La classe IdentifierClass se définit donc par :

  • Caractères valides : les lettres et chiffres (rappelons qu'on définit des identificateurs internationalisés : on ne se limite donc pas aux lettres et chiffres latins), et les symboles traditionnels, ceux d'ASCII (comme le tiret ou le tilde).
  • Caractères valides dans certains contextes : ceux de la rare catégorie JoinControl du RFC 5892, section 2.8.
  • Caractères interdits : tout le reste (espaces, ponctuation, la plupart des symboles...).
  • Les caractères non encore affectés sont également interdits.

La classe Freeform Class se définit, elle, par :

  • Caractères valides : les lettres et chiffres (rappelons qu'on définit des identificateurs internationalisés : on ne se limite donc pas aux lettres et chiffres latins), les espaces, la ponctuation, les symboles (oui, oui, y compris les emojis comme 🥞 cf. sections 4.3.1 et 9.15).
  • Caractères valides dans certains contextes : ceux de la catégorie JoinControl plus quelques exceptions.
  • Caractères interdits : tout le reste, ce qui fait surtout les caractères de contrôle comme U+0007 (celui qui fait sonner votre ordinateur).
  • Les caractères non encore affectés sont également interdits.

Ces listes de caractères autorisés ou interdits ne sont pas suffisantes. Il y a d'autres points à régler (section 5), ce qui se fait typiquement dans les profils. Ainsi, un profil doit définir :

  • Normalisation Unicode à utiliser. NFC est recommandée.
  • La correspondance entre les caractères et leur version large (width mapping). Est-ce que FULLWIDTH DIGIT ZERO (U+FF10) doit être considéré comme équivalent au zéro traditionnel, de largeur « normale », U+0030 ? Notez que certaines normalisations (mais qui ne sont pas celle recommandée), comme NFKC, règlent le problème. Autrement, la recommandation du RFC est « oui, il faut rendre ces caractères équivalents » car l'utilisateur serait certainement surpris que target0 et target0 soient considérés différents (le fameux POLA, principe de la moindre surprise).
  • D'autres correspondances peuvent être spécifiées par le profil (comme de transformer tous les espaces Unicode en l'espace traditionnel ASCII U+0020).
  • Changement de la casse des caractères, par exemple pour tout mettre en minuscules. Si c'est décidé pour un profil, le RFC recommande que cela soit fait avec la méthode Unicode standard, toLowerCase() (section 3.13 de la norme Unicode). Attention, cette méthode Unicode ne gère pas des cas qui dépendent de la langue, dont le plus fameux est le i sans point du turc (U+0131 c'est-à-dire ı). Le changement de casse est évidemment déconseillé pour les mots de passe (puisqu'il diminue l'entropie). Notez aussi que ce cas illustre le fait que les transformations PRECIS ne sont pas sans perte : si on met tout en minuscules, Poussin ne se distingue plus de poussin.
  • Interdiction de certains mélanges de caractères de directionnalité différentes. Il y a des écritures qui vont de gauche à droite et d'autres de droite à gauche, et leur combinaison peut entrainer des surprises à l'affichage. Dans certains cas, un profil peut vouloir limiter ce mélange de directionnalités.

Un profil peut également interdire certains caractères normalement autorisés (mais pas l'inverse).

Au passage, pour la comparaison, le RFC (section 7) impose un ordre à ces opérations. Par exemple, la mise en correspondance de la version large sur la version normale doit se faire avant l'éventuel changement de casse. C'est important car ces opérations ne sont pas commutatives entre elles.

Les profils sont enregistrés à l'IANA. Le RFC met bien en garde contre leur multiplication : toute l'idée de PRECIS est d'éviter que chaque protocole ne gère l'internationalisation des identificateurs de manière différente, ce qui empêcherait la réutilisation de code, et perturberait les utilisateurs. Si on avait un cadre commun mais des dizaines de profils différents, on ne pourrait pas dire qu'on a atteint cet objectif. Par exemple, en matière d'interface utilisateur, PRECIS essaie de s'en tenir au POLA (Principle of Least Astonishment) et ce principe serait certainement violé si chaque application trouvait rigolo d'interdire un caractère ou l'autre, sans raison apparente. Le RFC estime d'ailleurs (section 5.1) qu'il ne devrait y avoir idéalement que deux ou trois profils. Mais ce n'est pas possible puisque les protocoles existent déjà, avec leurs propres règles, et qu'on ne peut pas faire table rase de l'existant (tous les protocoles qui ont déjà définis des caractères interdits, comme IRC, NFS, SMTP, XMPP, iSCSI, etc).

Un petit point en passant, avant de continuer avec les applications : vous avez noté que la classe IdentifierClass interdit les espaces (tous les espaces Unicode, pas seulement le U+0020 d'ASCII), alors que certaines applications acceptent les espaces dans les identificateurs (par exemple, Unix les accepte sans problèmes dans les noms de fichier, Apple permet depuis longtemps de les utiliser pour nommer iTrucs et imprimantes, etc). La section 5.3 explique cette interdiction :

  • Il est très difficile de distinguer tous ces espaces entre eux,
  • Certains interfaces utilisateurs risquent de ne pas les afficher, menant à confondre françoise durand avec françoisedurand.

C'est embêtant (toute contrainte est embêtante) mais le compromis a semblé raisonnable au groupe PRECIS. Tant pis pour les espaces.

Passons maintenant aux questions des développeurs d'applications (section 6 du RFC). Que doivent-ils savoir pour utiliser PRECIS correctement ? Idéalement, il suffirait de lier son code aux bonnes bibliothèques bien internationalisées et tout se ferait automatiquement. En pratique, cela ne se passera pas comme ça. Sans être obligé de lire et de comprendre tout le RFC, le développeur d'applications devra quand même réflechir un peu à l'internationalisation de son programme :

  • Il est très déconseillé de créer son propre profil. Non seulement c'est plus compliqué que ça n'en a l'air, mais ça risque de dérouter les utilisateurs, si votre application a des règles légèrement différentes des règles des autres applications analogues.
  • Précisez bien quel partie de l'application va être responsable pour la préparation, l'application et la comparaison. Par exemple, le travail d'application sera-t-il fait par le client ou par le serveur ? Demandez-vous aussi à quel stade les applications devront avoir fait ce travail (par exemple, en cas de login, avant de donner un accès).
  • Définissez bien, pour chaque utilisation d'un identificateur (chaque slot, dit le RFC), quel profil doit être utilisé. Par exemple, « le nom du compte doit être conforme au profil UsernameCaseMapped de la classe IdentifierClass » (cf. RFC 8265).
  • Dressez la liste des caractères interdits (en plus de ceux déjà interdits par le profil) en fonction des spécificités de votre application. Par exemple, un @ est interdit dans la partie gauche d'une adresse de courrier électronique.

Sur ce dernier point, il faut noter que la frontière est mince entre « interdire plusieurs caractères normalement autorisés par le profil » et « définir un nouveau profil ». La possibilité d'interdire des caractères supplémentaires est surtout là pour s'accomoder des protocoles existants (comme dans l'exemple du courrier ci-dessus), et pour éviter d'avoir un profil par application existante.

Votre application pourra avoir besoin de constructions au-dessus des classes existantes. Par exemple, si un nom d'utilisateur, dans votre programme, peut s'écrire « Prénom Nom », il ne peut pas être une instance de la classe IdentifierClass, qui n'accepte pas les espaces pour la raison indiquée plus haut. Il faudra alors définir un concept « nom d'utilisateur », par exemple en le spécifiant comme composé d'une ou plusieurs instances de IdentifierClass, séparées par des espaces. En ABNF :

username   = userpart *(1*SP userpart)
userpart   = ... ; Instance d'IdentifierClass

La même technique peut être utilisée pour spécifier des identificateurs qui ne seraient normalement pas autorisés par IdentifierClass comme stéphane@maçonnerie-générale.fr ou /politique/séries/Game-of-Thrones/saison05épisode08.mkv.

On a vu plus haut qu'un des principes de PRECIS était de définir les caractères autorisés de manière algorithmique, à partir de leur propriétés Unicode, et non pas sous la forme d'une liste figée (qu'il faudrait réviser à chaque nouvelle version d'Unicode). Les catégories de caractères utilisées par cet algorithme sont décrites en section 9. Par exemple, on y trouve :

  • LettersDigits qui rassemble les chiffres et les lettres. (Rappelez-vous qu'on utilise Unicode : ce ne sont pas uniquement les lettres et les chiffres d'ASCII.)
  • ASCII7, les caractères d'ASCII, à l'exception des caractères de contrôle,
  • Spaces, tous les espaces possibles (comme le U+200A, HAIR SPACE, ainsi appelé en raison de sa minceur),
  • Symbols, les symboles, comme U+20A3 (FRENCH FRANC SIGN, ₣) ou U+02DB (OGONEK, ˛),
  • Etc.

Plusieurs registres IANA sont nécessaires pour stocker toutes les données nécessaires à PRECIS. La section 11 les recense tous. Le plus important est le PRECIS Derived Property Value, qui est recalculé à chaque version d'Unicode. Il indique pour chaque caractère s'il est autorisé ou interdit dans un identificateur PRECIS. Voici sa version pour Unicode 6.3 (on attend avec impatience une mise à jour…).

Les deux autres registres stockent les classes et les profils (pour l'instant, ils sont quatre). Les règles d'enregistrement (section 11) dans le premier sont strictes (un RFC est nécessaire) et celles dans le second plus ouvertes (un examen par un expert est nécessaire). La section 10 explique aux experts en question ce qu'ils devront bien regarder. Elle note que l'informaticien ordinaire est en général très ignorant des subtilités d'Unicode et des exigences de l'internationalisation, et que l'expert doit donc se montrer plus intrusif que d'habitude, en n'hésitant pas à mettre en cause les propositions qu'il reçoit. Dans beaucoup de RFC, les directives aux experts sont d'accepter, par défaut, les propositions, sauf s'il existe une bonne raison de les rejeter. Ici, c'est le contraire : le RFC recommande notamment de rejeter les profils proposés, sauf s'il y a une bonne raison de les accepter.

La section 12 est consacrée aux problèmes de sécurité qui, comme toujours lorsqu'il s'agit d'Unicode, sont plus imaginaires que réels. Un des problèmes envisagés est celui du risque de confusion entre deux caractères qui sont visuellement proches. Le problème existe déjà avec le seul alphabet latin (vous voyez du premier coup la différence entre google.com et goog1e.com ?) mais est souvent utilisé comme prétexte pour retarder le déploiement d'Unicode. PRECIS se voulant fidèle au principe POLA, le risque de confusion est considéré comme important. Notez que le risque réel dépend de la police utilisée. Unicode normalisant des caractères et non pas des glyphes, il n'y a pas de solution générale à ce problème dans Unicode (les écritures humaines sont compliquées : c'est ainsi). Si le texte ᏚᎢᎵᎬᎢᎬᏒ ressemble à STPETER, c'est que vous utilisez une police qui ne distingue pas tellement l'alphabet cherokee de l'alphabet latin. Est-ce que ça a des conséquences pratiques ? Le RFC cite le risque accru de hameçonnage, sans citer les nombreuses études qui montrent le contraire (cf. le Unicode Technical Report 36, section 2, « the use of visually confusable characters in spoofing is often overstated », et la FAQ de sécurité d'Unicode).

Quelques conseils aux développeurs concluent cette partie : limiter le nombre de caractères ou d'écritures qu'on accepte, interdire le mélange des écritures (conseil inapplicable : dans la plupart des alphabets non-latins, on utilise des mots entiers en alphabet latin)... Le RFC conseille aussi de marquer visuellement les identificateurs qui utilisent plusieurs écritures (par exemple en utilisant des couleurs différentes), pour avertir l'utilisateur.

C'est au nom de ce principe POLA que la classe IdentifierClass est restreinte à un ensemble « sûr » de caractères (notez que ce terme « sûr » n'est jamais expliqué ou défini dans ce RFC). Comme son nom l'indique, FreeformClass est bien plus large et peut donc susciter davantage de surprises.

PRECIS gère aussi le cas des mots de passe en Unicode. Un bon mot de passe doit être difficile à deviner ou à trouver par force brute (il doit avoir beaucoup d'entropie). Et il faut minimiser les risques de faux positifs (un mot de passe accepté alors qu'il ne devrait pas : par exemple, des mots de passe insensibles à la casse seraient agréables pour les utilisateurs mais augmenteraient le risque de faux positifs). L'argument de l'entropie fait que le RFC déconseille de créer des profils restreints de FreeformClass, par exemple en excluant des catégories comme la ponctuation. Unicode permet des mots de passe vraiment résistants à la force brute, comme « 𝃍🐬ꢞĚΥਟዶᚦ⬧ »... D'un autre côté, comme le montre l'exemple hypothétique de mots de passe insensibles à la casse, il y a toujours une tension entre la sécurité et l'utilisabilité. Laisser les utilisateurs choisir des mots de passe comportant des caractères « exotiques » peut poser bien des problèmes par la suite lorsqu'utilisateur tentera de les taper sur un clavier peu familier. Il faut noter aussi que les mots de passe passent parfois par des protocoles intermédiaires (comme SASL, RFC 4422, ou comme RADIUS, RFC 2865) et qu'il vaut donc mieux que tout le monde utilise les mêmes règles pour éviter des surprises (comme un mot de passe refusé par un protocole intermédiaire).

Enfin, la section 13 de notre RFC se penche sur l'interopérabilité. Elle rappele qu'UTF-8 est l'encodage recommandé (mais PRECIS est un cadre, pas un protocole complet, et un protocole conforme à PRECIS peut utiliser un autre encodage). Elle rappelle aussi qu'il faut être prudent si on doit faire circuler ses identificateurs vers d'autres protocoles : tous ne savent pas gérer Unicode, hélas.

Il existe une mise en œuvre de PRECIS en Go : https://godoc.org/golang.org/x/text/secure/precis et une en JavaScript, precis-js.

Les changements depuis le précédent RFC, le RFC 7564, sont résumés dans l'annexe A. Rien de crucial, le principal étant le remplacement de toCaseFold() par toLower() pour les opérations insensibles à la casse. Ces deux fonctions sont définies dans la norme Unicode (section 4.1). La principale différence est que toCaseFold() ne convertit pas forcément en minuscules (l'alphabet cherokee est converti en majuscules). Grâce à Python, dont la version 3 fournit casefold en plus de lower() et upper(), voici d'abord la différence sur l'eszett :

>>> "ß".upper()
'SS'
>>> "ß".lower()
'ß'
>>> "ß".casefold()
'ss'
    

Et sur l'alphabet cherokee (pour le cas, très improbable, où vous ayiez la police pour les minuscules, ajoutées récemment) :

>>> "ᏚᎢᎵᎬᎢᎬᏒ".upper()
'ᏚᎢᎵᎬᎢᎬᏒ'
>>> "ᏚᎢᎵᎬᎢᎬᏒ".lower()
'ꮪꭲꮅꭼꭲꭼꮢ'
>>> "ᏚᎢᎵᎬᎢᎬᏒ".casefold()
'ᏚᎢᎵᎬᎢᎬᏒ'
    

Téléchargez le RFC 8264


L'article seul

RFC 8266: Preparation, Enforcement, and Comparison of Internationalized Strings Representing Nicknames

Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : P. Saint-Andre (Jabber.org)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF precis
Première rédaction de cet article le 6 octobre 2017


Bien des protocoles Internet manipulent des noms qui doivent être parlants pour les utilisateurs et donc, de nos jours, doivent pouvoir être en Unicode. Les noms purement ASCII appartiennent à un passé révolu. Le groupe de travail PRECIS de l'IETF établit des règles pour ces noms, de manière à éviter que chaque protocole, chaque application, ne soit obligé de définir ses propres règles. Ce RFC contient les règles pour un sous-ensemble de ces noms : les noms qui visent plutôt à communiquer avec un utilisateur humain (par opposition aux noms qui sont indispensables aux protocoles réseaux, traités dans le RFC 8265). Il remplace le RFC 7700 (mais il y a peu de changements).

Ces noms « humains » sont typiquement ceux qui sont présentés aux utilisateurs. Ils doivent donc avant tout être « parlants » et il faut donc qu'ils puissent utiliser la plus grande part du jeu de caractères Unicode, sans restrictions arbitraires (contrairement aux identificateurs formels du RFC 8265, pour lesquels on accepte des limites du genre « pas d'espaces » ou « pas d'emojis »).

Le terme utilisé par le RFC pour ces noms « humains » est nicknames, terme qui vient du monde de la messagerie instantanée. Il n'y a pas de terme standard pour les désigner, certains protocoles (comme le courrier) parlent de display names (par opposition au login name ou account name), d'autres utilisent encore d'autres termes (l'article « An Introduction to Petname Systems » peut vous intéresser). Par exemple, dans un message électronique, on pourrait voir :

From: Valérie Pécresse <vp@les-républicains.fr>
    

Et, dans cet exemple, vp serait le nom formel (mailbox name dans le courrier, login name pour se connecter), alors que Valérie Pécresse est le nickname, le nom montrable aux humains. (Le concept de display name dans le courrier est normalisé dans la section 3.4.1 du RFC 5322, son exact équivalent XMPP, le nickname, est dans XEP-0172.)

Autre exemple, le réseau social Mastodon où mon nom formel est bortzmeyer@mastodon.gougere.fr alors que la description, le terme affiché est « S. Bortzmeyer 🗸 » (avec un symbole à la fin, le ‎U+1F5F8).

Comme l'illustre l'exemple ci-dessus, on veut évidemment que le nom puisse être en Unicode, sauf pour la petite minorité des habitants de la planète qui utilisent une langue qui peut s'écrire uniquement en ASCII.

Ces noms « parlants », ces nicknames, ne servent pas qu'à désigner des humains, ils peuvent aussi être utilisés pour des machines, des sites Web (dans les signets), etc.

On pourrait penser qu'il n'y a rien à spécifier pour permettre leur internationalisation. On remplace juste ASCII par Unicode comme jeu de caractères autorisé et vas-y, poupoule. Mais Unicode recèle quelques surprises et, pour que les nicknames fonctionnent d'une manière qui paraitra raisonnable à la plupart des utilisateurs, il faut limiter légèrement leur syntaxe.

Ces limites sont exposées dans la section 2 de notre RFC, qui définit un profil de PRECIS. PRECIS, Preparation, Enforcement, and Comparison of Internationalized Strings est le sigle qui désigne le projet « Unicode dans tous les identificateurs » et le groupe de travail IETF qui réalise ce projet. PRECIS définit (RFC 8264) plusieurs classes d'identificateurs et les nicknames sont un cas particulier de la classe FreeformClass (RFC 8264, section 4.3), la moins restrictive (celle qui permet le plus de caractères).

Outre les restrictions de FreeformClass (qui n'est pas complètement laxiste : par exemple, cette classe ne permet pas les caractères de contrôle), le profil Nickname :

  • Convertit tous les caractères Unicode de type « espace » (la catégorie Unicode Zs) en l'espace ASCII (U+0020),
  • Supprime les espaces en début et en fin du nickname, ce qui fait que " Thérèse" et "Thérèse" sont le même nom,
  • Fusionne toutes les suites d'espaces en un seul espace,
  • Met tout en minuscules (donc les nicknames sont insensibles à la casse),
  • Normalise en NFKC, plus violent que NFC, et réduisant donc les possibilités que deux nicknames identiques visuellement soient considérés comme distincts (cf. section 6, qui prétend à tort que ce serait un problème de sécurité ; comme souvent à l'IETF, le groupe de travail a passé beaucoup de temps sur un faux problème de « confusabilité », cf. UTS#39).

À noter qu'un nickname doit avoir une taille non nulle, après l'application des ces règles (autrement, un nickname de trois espaces serait réduit à... zéro).

Une fois ce filtrage et cette canonicalisation faite, les nicknames peuvent être comparés par une simple égalité bit à bit (s'ils utilisent le même encodage, a priori UTF-8). Un test d'égalité est utile si, par exemple, un système veut empêcher que deux utilisateurs aient le même nickname.

La section 3 de notre RFC fournit quelques exemples amusants et instructifs de nicknames :

  • "Foo" et "foo" sont acceptables, mais sont le même nom (en application de la régle d'insensibilité à la casse),
  • "Foo Bar" est permis (les espaces sont autorisés, avec les quelques restrictions indiquées plus haut),
  • "Échec au roi ♚" est permis, rien n'interdit les symboles comme cette pièce du jeu d'échecs, le caractère Unicode U+265A,
  • "Henri Ⅳ" est permis (ouvrez l'œil : c'est le chiffre romain à la fin, U+2163) mais la normalisation NFKC (précédée du passage en minuscules) va faire que ce nom est équivalent à "henri iv" (avec, cette fois, deux caractères à la fin).

Notez que ces règles ne sont pas idempotentes et le RFC demande qu'elles soient appliquées répétitivement jusqu'à la stabilité (ou, au maximum, jusqu'à ce que trois applications aient été faites).

Comme le rappelle la section 4 de notre RFC, les applications doivent maintenant définir l'utilisation de ces noms parlants, où peut-on les utiliser, etc. L'application peut donc avoir des règles supplémentaires, comme une longueur maximale pour ces nicknames, ou des caractères interdits car ils sont spéciaux pour l'application.

L'application ou le protocole applicatif a également toute latitude pour gérer des cas comme les duplicatas : j'ai écrit plus haut que "Foo Bar" et "foo bar" étaient considérés comme le même nickname mais cela ne veut pas dire que leur coexistence soit interdite : certaines applications permettent à deux utilisateurs distincts d'avoir le même nickname. Même chose avec d'autres règles « métier » comme la possibilité d'interdire certains noms (par exemple parce qu'ils sont grossiers).

Le profil Nickname est désormais ajouté au registre IANA (section 5 du RFC).

L'annexe A décrit les changements depuis le RFC 7700. Le principal est le remplacement de l'opération Unicode CaseFold() par toLower() pour assurer l'insensibilité à la casse. La différence est subtile, et ne change rien pour la plupart des écritures. Sinon, l'erreur notée #4570 a été corrigée. Le reste n'est que de la maintenance.


Téléchargez le RFC 8266


L'article seul

Supervision de serveurs Web .onion (« darquenette »)

Première rédaction de cet article le 4 octobre 2017


Un certain nombre de webmestres configurent leur serveur Web pour être accessible via un nom en .onion. Le but principal est de résister aux tentatives de censure. Un tel service, si on est soucieux de qualité, doit être supervisé comme n'importe quel autre service. J'explique ici comment j'ai configuré cette supervision, avec le logiciel Icinga, et quelques leçons apprises à l'occasion.

Tout site Web peut être accédé via le réseau Tor si le visiteur veut garder son légitime anonymat, ou bien s'il veut échapper à certaines censures (comme un blocage de l'adresse IP du serveur). Il lui suffit d'installer le Tor Browser et hop. Mais cette technique ne protège que le client, elle ne protège pas le serveur. Des attaques côté serveur (saisie du nom de domaine, ou bien saisie du serveur physique) peuvent couper l'accès. D'où les noms de domaines en .onion (ce qu'on appelle parfois, bien à tort, « service caché », alors que ces services sont complètement publics et que même le Googlebot y passe). Avec une telle configuration (utilisée, par exemple, par mon blog), le risque de censure est bien plus faible (mais pas nul, attention, vous courrez quand même des risques). Les journalistes ont inventé le terme sensationnaliste de darknet (qui désignait à l'origine tout à fait autre chose) pour ces services, et les politiciens français l'ont traduit par « Internet clandestin », une traduction d'état d'urgence… (Et vraiment grotesque puisque tout le monde, vous, moi, la NSA, Google, peut visiter ces sites.)

De tels services sont de plus en plus nécessaires, et pas seulement si on est dissident en Iran, en Chine ou en Arabie saoudite. Les pays censés être démocratiques utilisent également la censure de l'Internet comme récemment en Espagne (contre le référendum catalan et donc en partie contre le .cat) ou en France avec la pérennisation de l'état d'urgence.

Donc, il est tout à fait justifié de configurer des services en .onion. Mais si on veut vérifier que ces services marchent bien ? Pour les services plus traditionnels (mettons un site Web classique), on utilise un logiciel de supervision. Mettons qu'on utilise, comme moi, Icinga (mais la technique expliquée ici devrait marcher avec n'importe quel logiciel de supervision compatible avec l'API de Nagios). Le script de test des serveurs HTTP est check_http, qui peut se tester depuis la ligne de commande :

% /usr/lib/nagios/plugins/check_http  -H www.elysee.fr        
HTTP OK: HTTP/1.1 200 OK - 684876 bytes in 0.791 second response time |time=0.791354s;;;0.000000;10.000000 size=684876B;;;0
    

Il est appelé régulièrement par Icinga pour tester le serveur, et déclencher une alarme si quelque chose va mal. Ce script utilise les bibliothèques « normales » du système, et ne connait pas le TLD « spécial » .onion :

% /usr/lib/nagios/plugins/check_http  -H 7j3ncmar4jm2r3e7.onion
Name or service not known
HTTP CRITICAL - Unable to open TCP socket
    

Si on fait tourner le client Tor sur sa machine, il exporte par défaut une interface SOCKS (paramètres SOCKSPort et SOCKSPolicy dans le fichier de configuration de Tor). Cela permet à certains programmes (ici, curl) d'accéder aux services en .onion :

    

% curl -v  7j3ncmar4jm2r3e7.onion 
...
curl: (6) Could not resolve host: 7j3ncmar4jm2r3e7.onion

Et avec la bonne option (Tor écoute par défaut sur le port 9050) :


% curl -v --socks5-hostname localhost:9050 7j3ncmar4jm2r3e7.onion 
...
* SOCKS5 communication to 7j3ncmar4jm2r3e7.onion:80
* SOCKS5 request granted.
* Connected to localhost (127.0.0.1) port 9050 (#0)
> GET / HTTP/1.1
> Host: 7j3ncmar4jm2r3e7.onion
> User-Agent: curl/7.55.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Fri, 22 Sep 2017 09:31:32 GMT
< Server: Apache
< Content-Type: text/html
< 
{ [3245 bytes data]
  0  425k    0  3245    0     0   3245      0  0:02:14  0:00:01  0:02:13  2414<?xml version="1.0" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xml:lang="fr" lang="fr" xmlns="http://www.w3.org/1999/xhtml">
<head>
...

(Si vous avez un message d'erreur du genre « * connect to 127.0.0.1 port 9050 failed: Connection refused », c'est que le client Tor ne tourne pas sur votre machine - il ne s'agit pas du Tor Browser, mais d'un démon - ou bien qu'il écoute sur un autre port, vérifiez la configuration.)

Petit problème, le script check_http ne semble pas disposer d'une option SOCKS. Il faut donc utiliser une autre méthode, le programme torsocks qui redirige divers appels système pour que l'application parle à SOCKS en croyant parler directement au serveur HTTP. Pour curl, on aurait pu faire :

% torsocks curl -v 7j3ncmar4jm2r3e7.onion
    

On peut donc lancer le script de test des Monitoring Plugins :

    
% torsocks /usr/lib/nagios/plugins/check_http -H 7j3ncmar4jm2r3e7.onion
HTTP OK: HTTP/1.1 200 OK - 436330 bytes in 3.649 second response time |time=3.648528s;;;0.000000;10.000000 size=436330B;;;0

Maintenant que le test marche en ligne de commande, il n'y a plus qu'à configurer Icinga pour l'utiliser. On écrit un petit script shell :

    
% cat /usr/local/lib/nagios/plugins/check_http_onion
#!/bin/sh

/usr/bin/torsocks /usr/lib/nagios/plugins/check_http $*

Et on dit à Icinga de l'utiliser. Dans commands.conf :

object CheckCommand "http-onion" {

	command = [ PluginContribDir + "/check_http_onion" ]

	arguments = {
		"-H" = {
			value = "$http_vhost$"
			description = "Host name argument for servers using host headers (virtual host)"
		}
[On reprend verbatim les options de check_http, qui sont, sur une
Debian, dans /usr/share/icinga2/include/command-plugins.conf]

	vars.http_address = "$check_address$"
	vars.http_ssl = false
	vars.http_sni = false
	vars.http_linespan = false
	vars.http_invertregex = false
	vars.check_ipv4 = "$http_ipv4$"
	vars.check_ipv6 = "$http_ipv6$"
	vars.http_link = false
	vars.http_verbose = false
}

Et dans services.conf :

apply Service for (http_vhost => config in host.vars.http_onion_vhosts) {
  import "generic-service"

  check_command = "http-onion"

  vars += config
}
   

Avec ces définitions, je peux décider de superviser un service en mettant une entrée dans le vars.http_onion_hosts de la machine :

object Host "serveur-secret" {
  import "generic-host"

  ... [Autres tests]

  vars.http_onion_vhosts["onion"] = {
    http_uri = "/"
    http_vhost = "7j3ncmar4jm2r3e7.onion"
    http_ssl = false
    http_timeout = 15
    }
}
   

Ou bien en créant un host spécial :

object Host "internet-libre" {
  import "generic-host"

  check_command = "always_true"  /* No ping in Tor */

vars.http_onion_vhosts["aeris"] = {
    http_uri = "/"
    http_vhost = "aerisryzdvrx7teq.onion"
    http_ssl = false
    http_timeout = 15
    }

vars.http_onion_vhosts["amaelle"] = {
    http_uri = "/"
    http_vhost = "ozawuyxtechnopol.onion"
    http_ssl = false
    http_timeout = 15
    }

}
   

Ainsi configuré, Icinga va régulièrement tester ces services via Tor, et prévenir de la manière habituelle : icinga-onion.png

Alors, à l'usage, est-ce que ça marche ? Oui, au sens où je suis bien prévenu lorsqu'il y a un problème et Icinga teste bien et enregistre l'historique des pannes. Moins bien au sens où cette supervision révèle que Tor n'est pas parfaitement fiable, en tout cas vu de mon ADSL : les pannes temporaires sont relativement fréquentes (ce qui est logique, la requête étant acheminée par plusieurs nœuds Tor parfois lointains, et pas toujours stables). Cela peut donc valoir la peine de configurer le logiciel de supervision pour être moins prompt à lever une alarme pour ces services. Disons en gros une ou deux pannes d'environ dix minutes par jour, ce qui ne serait pas acceptable pour un serveur Web « normal ».

Ce qui est amusant est que, parfois, pendant une panne, le Tor Browser depuis le même réseau, arrive à se connecter au service. C'est probablement parce que le Tor Browser et le démon Tor utilisé par Icinga ont établi des circuits Tor différents (passant par des nœuds différents), et qu'un seul des circuits déconne.

Mille mercis à Lunar pour m'avoir tout expliqué patiemment (et, en plus, ça marche). Et merci aussi à Amaelle et Aeris pour m'avoir permis d'utiliser leurs serveurs pour les tests. Voici les trois serveurs en .onion qui ont été utilisé (les liens ne marcheront que si vous regardez cet article via Tor) :


L'article seul

Deuxième lien Internet pour la Corée du Nord, ça se voit où ?

Première rédaction de cet article le 3 octobre 2017
Dernière mise à jour le 4 octobre 2017


Le premier octobre 2017, le site d'information spécialisé dans la Corée du Nord, 38 North, a publié un excellent article sur la deuxième connexion Internet de la Corée du Nord. Où peut-on vérifier ce genre d'informations ?

L'article est un bon exemple d'OSINT (ou de ROSO ?), ne s'appuyant que sur des sources publiques, et sur l'analyse des experts de Dyn research (l'ancien Renesys, qui a publié son propre article, une lecture très recommandée). Mais, sur le sujet de la Corée du Nord, la propagande tourne à plein régime et énormément d'informations fausses sont diffusées, d'autant plus que la dictature délirante de Kim Jong-un ne permet pas de vérifications sur place. Si les fake news ne sont pas une nouveauté (le mensonge est aussi ancien que le langage), le caractère assez technique de l'information (« la Corée du Nord a désormais un deuxième lien Internet, via la Russie ») rend les vérifications plus difficiles, pour les gens non spécialisés.

Donc, l'article de 38 North dit qu'un opérateur Internet russe connecte désormais la RDPC, qui n'était auparavant connectée que par la Chine. Vu le caractère… spécial de la Corée du Nord, cette connexion n'est pas une pure affaire de business et a sans doute nécessité une approbation par Moscou. Comment la vérifier ?

Car c'est tout l'intérêt de l'OSINT : le but n'est pas uniquement de faire gagner du temps aux espions (au prix d'une certaine perte de glamour : lire le Wikipédia russophone est moins spectaculaire que de pénétrer dans une chambre forte au Kremlin, après avoir séduit une belle espionne russe). L'intérêt politique de l'OSINT est de permettre des vérifications indépendantes : si on n'est pas sûr de l'information, on peut refaire une partie du travail pour la recouper.

Donc, première chose, récolter quelques informations techniques. Quelles sont les adresses IP de l'(unique) opérateur Internet nord-coréen ? On peut le savoir en partant des serveurs de noms de leur TLD, .kp :


% dig NS kp

; <<>> DiG 9.10.3-P4-Debian <<>> NS kp
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44497
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 3

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;kp.			IN NS

;; ANSWER SECTION:
kp.			429731 IN NS ns3.kptc.kp.
kp.			429731 IN NS ns1.kptc.kp.
kp.			429731 IN NS ns2.kptc.kp.

;; ADDITIONAL SECTION:
ns1.kptc.kp.		429731 IN A 175.45.176.15
ns2.kptc.kp.		429731 IN A 175.45.176.16

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Tue Oct 03 11:53:28 CEST 2017
;; MSG SIZE  rcvd: 122

    

Un petit coup de whois pour vérifier :

    
% whois 175.45.176.15
% [whois.apnic.net]
% Whois data copyright terms    http://www.apnic.net/db/dbcopyright.html

% Information related to '175.45.176.0 - 175.45.179.255'

% Abuse contact for '175.45.176.0 - 175.45.179.255' is 'postmaster@star-co.net.kp'

inetnum:        175.45.176.0 - 175.45.179.255
netname:        STAR-KP
descr:          Ryugyong-dong
descr:          Potong-gang District
country:        KP
org:            ORG-SJVC1-AP
admin-c:        SJVC1-AP
tech-c:         SJVC1-AP
status:         ALLOCATED PORTABLE
mnt-by:         APNIC-HM
mnt-lower:      MAINT-STAR-KP
mnt-routes:     MAINT-STAR-KP
...
organisation:   ORG-SJVC1-AP
org-name:       Star Joint Venture Co. Ltd.
country:        KP
address:        Ryugyong-dong
address:        Potong-gang District
phone:          +850-2-3812321
fax-no:         +850-2-3812100
e-mail:         postmaster@star-co.net.kp
...

OK, on a bien trouvé Star JV, l'opérateur coréen. Quel est son numéro d'AS ? Il existe plein de moyens de le trouver, à partir de la base APNIC mais, ayant l'habitude du DNS, j'utilise le service DNS de RouteViews :

%  dig +short TXT 0.176.45.175.aspath.routeviews.org
"54728 6939 701 4837 131279" "175.45.176.0" "24"    
   

Il nous apprend que le préfixe 175.45.176.0/24 est annoncé par l'AS 131279 (rappelez-vous que les chemins d'AS se lisent de droite à gauche). Le second AS (rappelez-vous que les chemins d'AS se lisent de droite à gauche), 4837, est celui du fournisseur chinois habituel, China Unicom.

L'article de 38 North dit que cela a changé le dimanche 1 octobre. En effet, l'excellent service RIPE stat, quand on l'interroge sur le routage de 175.45.176.0/24, confirme une activité importante : star-jv-transtelecom-ripe-stat.png

(Vous pouvez aussi regarder sur le site original.) Cette activité indique qu'un changement a eu lieu, puis s'est propagé par BGP à tout l'Internet, déclenchant en cascade d'autres changements. (Les tables BGP sont publiques, l'Internet est très ouvert. Exercice : cherchez les autres préfixes annoncés par Star JV.)

Bon, en quoi consistait cette nouveauté BGP du dimanche matin ? On va se tourner vers le service de RouteViews qui archive les annonces BGP et les met ensuite à la disposition du public. Les fichiers sont au format MRT (RFC 6396), qui s'analyse avec le programme bgpdump. Et on connait l'heure approximative de l'annonce (dans l'article de 38 North et grâce à RIPE stat) donc on peut télécharger le bon fichier (il change tous les quarts d'heure, regardez le nom de fichier, au format YYYYMMDD) :

% wget http://archive.routeviews.org/bgpdata/2017.10/UPDATES/updates.20171001.0900.bz2
% bunzip2 updates.20171001.0900.bz2 
% bgpdump updates.20171001.0900 > updates.20171001.0900.txt                         
2017-10-03 11:22:24 [info] logging to syslog
%
    

On charge le updates.20171001.0900.txt dans un éditeur, on cherche "131279" (l'AS de Star JV) et on trouve l'annonce cherchée :

TIME: 10/01/17 09:07:51
TYPE: BGP4MP/MESSAGE/Update
FROM: 202.73.40.45 AS18106
TO: 128.223.51.102 AS6447
ORIGIN: INCOMPLETE
ASPATH: 18106 20485 131279
NEXT_HOP: 202.73.40.45
COMMUNITY: 4635:2000
ANNOUNCE
  175.45.176.0/24
  175.45.178.0/24
  175.45.179.0/24      
    

Ici, l'annonce BGP émise par 131279 est transmise à l'AS 20485, le nouveau transitaire de Star JV. Qui est-il ?

% whois AS20485
% This is the RIPE Database query service.
...
aut-num:        AS20485
as-name:        TRANSTELECOM
org:            ORG-CJSC19-RIPE
descr:          Moscow, Russia
...
organisation:   ORG-CJSC19-RIPE
org-name:       Closed Joint Stock Company TransTeleCom
org-type:       LIR
address:        Testovskayia str., 8 , enterance 3
address:        123317
address:        Moscow
address:        RUSSIAN FEDERATION
phone:          +74957846670
fax-no:         +74957846671
...
    

Il s'agit donc bien d'un russe, TransTelecom.

Maintenant, est-ce que cet opérateur russe est vraiment utilisé ? Certes, il reçoit les annonces BGP des Nord-Coréens et les relaie (c'est ainsi qu'elles sont reçues par RouteViews). Mais, comme disent les opérateurs réseaux, « The data plane does not always follow the control plane », ce qui veut dire que les paquets ne passent pas forcément par le chemin des AS qui ont relayé l'annonce. Testons depuis TransTelecom, grâce aux sondes RIPE Atlas, en utilisant le programme atlas-traceroute. On demande à trois sondes dans l'AS de TransTelecom d'aller visiter 175.45.176.15 :

% atlas-traceroute --format --as 20485 --requested 3 175.45.176.15

Measurement #9412526 Traceroute 175.45.176.15 from AS #20485 uses 3 probes

3 probes reported
Test #9412526 done at 2017-10-03T10:08:23Z
From:  79.140.108.66    20485    TRANSTELECOM Moscow, Russia, RU
Source address:  79.140.108.66
Probe ID:  1257
1    79.140.108.65    20485    TRANSTELECOM Moscow, Russia, RU    [8.027, 1.63, 1.634]
2    217.150.57.50    20485    TRANSTELECOM Moscow, Russia, RU    [5.27, 5.079, 5.072]
3    212.73.250.154    3356    LEVEL3 - Level 3 Communications, Inc., US    [30.465, 30.464, 31.473]
4    ['*', '*', '*']
5    ['*', '*', '*']
6    204.255.173.53    701    UUNET - MCI Communications Services, Inc. d/b/a Verizon Business, US    [132.757, 132.524, 132.329]
7    ['*', '*', '*']
8    152.63.114.26    701    UUNET - MCI Communications Services, Inc. d/b/a Verizon Business, US    [205.82, 205.77, 206.587]
9    ['*', '*', '*']
10    219.158.101.197    4837    CHINA169-BACKBONE CHINA UNICOM China169 Backbone, CN    [380.779, 380.674, 380.586]
11    219.158.3.130    4837    CHINA169-BACKBONE CHINA UNICOM China169 Backbone, CN    [385.401, 385.712, 385.569]
12    ['*', '*', '*']
13    219.158.39.42    4837    CHINA169-BACKBONE CHINA UNICOM China169 Backbone, CN    [268.096, '*', '*']
...

(Vous pouvez aussi regarder une représentation graphique de cette mesure.) Et on voit bien que, même depuis TransTelecom, fournisseur de connectivité de Star JV, le trafic n'est pas envoyé via le pont de l'Amitié, mais à un opérateur états-unien, Level 3, qui transmettra ensuite à China Unicom. (Exercice : faites pareil depuis d'autres AS, comme 18106, pour voir qu'ils envoient bien à TransTelecom - ils ont bien reçu l'annonce BGP - mais que celui-ci ne transmet pas ensuite directement aux Coréens.) Le lien tout neuf n'est donc pas utilisé pour l'instant. Ceci dit, la situation change régulièrement. Le 4 octobre, il n'y avait plus qu'un seul préfixe annoncé via TransTelecom, le 175.45.178.0/24 et lui est bien routé directement vers Star JV :

% atlas-traceroute --format --as 20485 --requested 3 --proto icmp 175.45.178.1 

Measurement #9675151 Traceroute 175.45.178.1 from AS #20485 uses 3 probes

3 probes reported
Test #9675151 done at 2017-10-04T12:21:50Z
From:  79.140.108.66    15774    TTK-RTL Retail, RU
Source address:  79.140.108.66
Probe ID:  1257
1    79.140.108.65    15774    TTK-RTL Retail, RU    [8.441, 1.936, 1.936]
2    217.150.57.50    20485    TRANSTELECOM Moscow, Russia, RU    [5.432, 5.497, 5.441]
3    ['*', '*', '*']
4    188.43.225.153    20485    TRANSTELECOM Moscow, Russia, RU    [131.737, 132.065, 131.744]
5    ['*', '*', '*']
6    175.45.178.1    131279    STAR-KP Ryugyong-dong, KP    [130.062, 130.123, 129.827]
...   
   

[Version graphique disponible en ligne. Notez l'utilisation du protocole ICMP, UDP étant bloqué quelque part.] Si votre FAI reçoit l'annonce via TransTelecom, vous pouvez aussi faire un traceroute depuis chez vous (ici depuis Free) (188.43.225.153 est TransTelecom) :

% traceroute -I 175.45.178.1  
traceroute to 175.45.178.1 (175.45.178.1), 30 hops max, 60 byte packets
...
 4  rke75-1-v900.intf.nra.proxad.net (78.254.255.42)  40.085 ms  40.079 ms  40.078 ms
 5  cev75-1-v902.intf.nra.proxad.net (78.254.255.46)  40.070 ms  40.083 ms  40.084 ms
 6  p16-6k-1-po12.intf.nra.proxad.net (78.254.255.50)  40.043 ms  25.274 ms  25.259 ms
 7  bzn-crs16-1-be1024.intf.routers.proxad.net (212.27.56.149)  29.072 ms  23.159 ms  16.617 ms
 8  194.149.165.206 (194.149.165.206)  31.358 ms  31.041 ms  31.054 ms
 9  * * *
10  tenge10-1.br01.frf06.pccwbtn.net (63.218.232.41)  30.996 ms  30.990 ms  32.615 ms
11  63-218-233-6.static.pccwglobal.net (63.218.233.6)  32.616 ms  34.156 ms  34.162 ms
12  * * *
13  188.43.225.153 (188.43.225.153)  191.185 ms  191.210 ms  180.775 ms
14  * * *
15  175.45.178.1 (175.45.178.1)  185.670 ms  185.680 ms  186.084 ms
   

Peut-être le lien vers TransTelecom est-il un lien de secours, prévu pour faire face à d'éventuelles sanctions chinoises ? Ou peut-être tout simplement le nouveau lien est en cours de configuration et que la situation n'est pas tout à fait stable ?

Notez que, si les tables et les annonces BGP sont publiques (ainsi que l'utilisation des sondes RIPE Atlas), la capacité est bien plus difficile à connaitre et des affirmations comme quoi le nouveau lien représenterait « 60 % de la capacité Internet nord-coréenne » (dans cet article) sont probablement de la pure spéculation.

Merci à Dyn pour des explications supplémentaires.


L'article seul

RFC 8205: BGPsec Protocol Specification

Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : M. Lepinski (NCF), K. Sriram (NIST)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF sidr
Première rédaction de cet article le 28 septembre 2017


Ce RFC s'inscrit dans la longue liste des efforts de normalisation d'une solution de sécurité pour BGP. Actuellement, il est trop facile de détourner le routage Internet via BGP (cf. mon article « La longue marche de la sécurité du routage Internet »). Le RFC 6482 introduisait une solution partielle, les ROA (Route Origin Authorizations). S'appuyant sur une infrastructure de clés publiques et de certificats prouvant que vous êtes détenteur légitime d'une ressource Internet (comme un numéro d'AS ou bien un préfixe IP), infrastructure nommée la RPKI (Resource Public Key Infrastructure), ces ROA permettent de valider l'origine d'une annonce BGP, c'est-à-dire le premier AS du chemin. C'est insuffisant pour régler tous les cas. Ainsi, lors de la fuite de Telekom Malaysia en juin 2015, les annonces avaient une origine normale. Avec les ROA, on ne détectait donc pas le problème. D'où cette nouvelle étape, BGPsec. Il s'agit cette fois de valider le chemin d'AS complet de l'annonce BGP, chaque routeur intermédiaire signant cryptographiquement l'annonce avant de la transmettre. BGPsec permet donc de détecter bien plus d'attaques, mais il est aussi bien plus lourd à déployer.

Donc, pour résumer BGPsec en une phrase : un nouvel attribut BGP est créé, BGPsec_Path, il est transporté dans les annonces de route BGP, il offre la possibilité de vérifier, via les signatures, que chaque AS listé dans ce chemin a bien autorisé l'annonce. BGPsec ne remplace pas les ROA, il les complète (et, comme elles, il s'appuie sur la RPKI). Deux bonnes lectures : le modèle de menace de BGPsec, dans le RFC 7132, et le cahier des charges de BGPsec, dans le RFC 7353.

Le reste, ce n'est que du détail technique. Contrairement aux ROA, qui sont transportés en dehors de BGP, BGPsec est une modification du protocole BGP. Deux routeurs qui font du BGPsec doivent donc le négocier lors de l'établissement de la session (section 2 de notre RFC). Cela se fait par le mécanisme des capacités du RFC 5492. La capacité annonçant qu'on sait faire du BGPsec est BGPsec Capability, code n° 7. Notez que cette capacité dépend de celle de gérer des AS de quatre octets (normalisée dans le RFC 6793).

Le nouvel attribut est décrit en section 3. (Le concept d'attributs BGP est dans la section 5 du RFC 4271.) Les annonces BGP avaient traditionnellement un attribut nommé AS_PATH, qui indiquait le chemin d'AS suivi par l'annonce. (Deux choses importantes à se rappeler : lorsqu'on écrit un chemin d'AS, il se lit de droite à gauche, et vous n'avez aucune garantie que les paquets IP suivront effectivement ce chemin « the data plane does not always follow the control plane ».)

Le nouvel attribut remplace AS_PATH. Il se nomme BGPsec_path (code 33) et est optionnel et non-transitif, ce qui veut dire qu'il ne sera pas passé tel quel aux pairs. Au contraire, le routeur est supposé créer un nouvel attribut BGPsec_path , ajoutant son AS (signé) et celui de l'AS à qui il transmet l'annonce. On n'envoie une annonce portant cet attribut à un pair que si celui-ci a signalé sa capacité à gérer BGPsec. (Sinon, on lui envoie un AS_PATH ordinaire, non sécurisé, formé en extrayant les AS du BGPsec_path, cf. section 4.4.)

La signature contient entre autres un identifiant de l'algorithme utilisé. La liste des algorithmes possibles est dans un registre IANA (RFC 8208).

Pour pouvoir mettre sa signature dans le BGPsec_path, le routeur doit évidemment disposer d'une clé privée. La clé publique correspondante doit être dans un certificat de la RPKI, certificat valable pour l'AS qui signe.

Un routeur qui transmet une annonce à un pair BGPsec ne garantit pas forcément qu'il a validé le chemin sécurisé. (Même s'il le garantissait, pourquoi lui faire confiance ?) C'est à chaque routeur de vérifier l'intégrité et l'authenticité du chemin d'AS (section 5 du RFC).

C'est dans cette section 5 qu'est le cœur du RFC, les détails de la validation d'un chemin d'AS. Cette validation peut être faite par le routeur, ou bien par une machine spécialisée dans la validation, avec laquelle le routeur communique via le protocole RTR (RPKI To Router protocol, cf. RFC 8210). Bien sûr, si on valide, c'est sans doute pour que la validation ait des conséquences sur la sélection de routes, mais notre RFC n'impose pas de politique particulière : ce que l'on fait avec les annonces BGPsec mal signées est une décision locale, par chaque routeur (et peut-être, sur un routeur donné, différente pour chaque pair BGP).

L'algorithme de validation est simple :

  • Contrôles syntaxiques de l'annonce,
  • Vérification que l'AS le plus récemment ajouté est bien celui du pair qui nous l'a envoyé (à part le cas particulier des serveurs de route, traités en section 4.2),
  • Test de la présence d'une signature pour chaque AS listé dans BGPsec_path.

À ce stade, on peut déjà rejeter les annonces qui ont échoué à l'un de ces tests, et les traiter comme signifiant un retrait de la route citée (RFC 7606). Ensuite, on passe aux vérifications cryptographiques :

  • Vérification que toutes les signatures sont faites avec un algorithme cryptographique qu'on accepte (voir la section 6 et aussi le RFC 8208),
  • Pour chaque AS listé dans le chemin, recherche d'un certificat correspondant à cet AS, et ayant la clé publique avec laquelle l'AS dans le chemin a été signé,
  • Test de la signature.

Notez qu'on ne négocie pas les algorithmes cryptographiques au moment de l'établissement de la session BGP, comme cela se fait, par exemple, en TLS. En effet, il ne suffit pas de connaitre l'algorithme de ses pairs, il faut valider tout le chemin, y compris des AS lointains. C'est pour cela que le RFC 8208 liste des algorithmes obligatoires, que tout le monde est censé connaître (actuellement ECDSA avec la courbe P-256 et SHA-256).

On note que l'attribut BGPsec_path avec ses signatures n'est « transmis » qu'aux routeurs qui comprennent BGPsec (pour les autres, on envoie un AS_PATH classique). Au début du déploiement, ces attributs ne survivront donc pas longtemps, et les îlots BGPsec seront donc rares (section 7 du RFC).

Notez que ce protocole est en développement depuis plus de six ans, et qu'il s'est parfois nommé PathSec dans le passé.

BGPsec est apparemment mis en œuvre au moins dans BIRD et dans Quagga (mais pas dans les versions officielles).

Quelques bonnes lectures sur BGPsec :

Et merci à Guillaume Lucas pour m'avoir rappelé que BGPsec est compliqué et que je suis loin de le maitriser.


Téléchargez le RFC 8205


L'article seul

RFC 8207: BGPsec Operational Considerations

Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : R. Bush (Internet Initiative Japan)
Réalisé dans le cadre du groupe de travail IETF sidr
Première rédaction de cet article le 28 septembre 2017


Le mécanisme de sécurisation de BGP nommé BGPsec, normalisé dans le RFC 8205 n'a pas encore de déploiement important. Mais le bon sens, l'expérience d'autres mécanismes de sécurité, et les expérimentations déjà faites avec BGPsec permettent de dégager quelques bonnes pratiques opérationnelles, que ce RFC décrit. Le RFC est court : on manque encore de pratique pour être sûr de toutes les bonnes pratiques.

Bien sûr, ces pratiques évolueront. À l'heure de la publication de ce RFC, il y a encore très peu d'expérience concrète avec BGPsec. Au fait, c'est quoi, BGPsec ? Le protocole BGP, la norme pour les annonces de routes sur l'Internet, souffre d'une faiblesse de sécurité : par défaut, n'importe qui peut annoncer n'importe quelle route, même vers un préfixe IP qu'il ne contrôle pas. Il existe plusieurs systèmes pour limiter les dégâts : les IRR, les systèmes d'alarme, et, plus récemment, les ROA (Route Origin Authorizations, cf. RFC 6482). BGPsec est la technique la plus efficace, mais aussi la plus difficile à déployer. Comme les ROA, il repose sur la RPKI. Contrairement aux ROA, il ne valide pas que l'AS d'origine mais tout le chemin d'AS, par une signature de chaque AS. Il introduit donc la cryptographie dans le routage, ce qui causera sans doute quelques perturbations.

Donc, avant de lire ce RFC 8207, c'est sans doute une bonne idée de lire au moins les RFC 4271 sur BGP et RFC 6480 sur la RPKI. Comme BGPsec dépend de la RPKI, il est également bon de lire le RFC 7115, qui parle des questions opérationnelles des ROA (qui reposent également sur la RPKI). Et enfin il faut lire le RFC sur le protocole BGPsec, le RFC 8205.

Avec BGPsec, chaque routeur de bordure de l'AS va avoir une paire {clé privée, clé publique}. On peut avoir une seule paire pour tous ses routeurs (et donc publier un seul certificat dans la RPKI) ou bien une paire par routeur (section 4 du RFC). La première solution est plus simple mais, si un routeur est piraté, il faudra changer la configuration de tous les autres routeurs. Le RFC suggère aussi de pré-publier des clés de secours dans la RPKI, pour qu'elles soient bien distribuées partout le jour où on en aura besoin.

Tous vos routeurs ne sont pas censés parler BGPsec, seulement ceux au bord de l'AS, face au grand monde inconnu du méchant Internet (section 5). Si vous n'avez pas le temps ou l'envie d'activer BGPsec sur tous vos routeurs, commencez par ceux qui font face aux « maillons faibles », les pairs ou les transitaires que vous suspectez d'être le plus capable d'envoyer ou de relayer des annonces erronées ou mensongères. Évidemment, ce sont aussi ceux qui ont le moins de chance de déployer BGPsec…

Si vous ne vous contentez pas de vérifier les signatures BGPsec, mais que vous agissez sur la base de cette vérification (en ignorant les annonces invalides), attendez-vous, comme avec toute technique de sécurité BGP, à ce que le trafic se déplace, par exemple vers d'autres pairs. Un pré-requis au déploiement de BGPsec est donc sans doute un bon système de métrologie.

Attention aux limites de BGPsec : la signature couvre le chemin d'AS, pas les communautés (RFC 1997). Ne vous fiez donc pas à elles.

Si BGPsec vous semble bien compliqué à déployer, et que vous hésitez devant le travail que cela représente, la section 6 vous rassurera : la majorité des AS sont des stubs, ils ne fournissent de connectivité à personne et n'ont donc pas forcément besoin de valider les annonces. Par contre, vous pouvez demander à vos transitaires s'ils utilisent BGPsec (début 2017, la réponse était forcément non).

Les lect·eur·rice·s subtil·e·s ont peut-être noté une différence avec les ROA. Alors que l'état de la validation ROA (RFC 6811) donne un résultat ternaire (pas de ROA, un ROA et valide, un ROA et invalide), BGPsec est binaire (annonce valide, ou invalide). C'est parce que les chemins d'AS sécurisés BGPsec_path ne sont propagés qu'entre routeurs BGPsec, qui signent et, a priori, valident. Si on reçoit une annonce BGPsec, c'est forcément que tout le chemin d'AS gérait BGPsec. L'équivalent du « pas de ROA » est donc une annonce BGP traditionnelle, avec AS_PATH mais sans BGPsec_path.

Quant à la décision qu'on prend lorsqu'une annonce est invalide, c'est une décision locale. Ni l'IETF, ni l'ICANN, ni le CSA ne peuvent prendre cette décision à votre place. Notre RFC recommande (mais c'est juste une recommandation) de jeter les annonces BGPsec invalides. Certains opérateurs peuvent se dire « je vais accepter les annonces invalides, juste leur mettre une préférence plus basse » mais cela peut ne pas suffire à empêcher leur usage même lorsqu'il existe des alternatives. Imaginons un routeur qui reçoive une annonce valide pour 10.0.0.0/16 et une invalide pour 10.0.666.0/24 (oui, je sais que ce n'est pas une adresse IPv4 correcte, mais c'est le RFC qui a commencé, ce n'est pas moi). Le routeur installe les deux routes, avec une préférence basse pour 10.0.666.0/24. Mais les préférences ne jouent que s'il s'agit de préfixes identiques. Ici, lorsqu'il faudra transmettre un paquet, c'est la route la plus spécifique qui gagnera, donc la mauvaise (666 = chiffre de la Bête). Notez que l'article « Are We There Yet? On RPKI’s Deployment and Security » contient plusieurs autres exemples où le rejet d'une annonce invalide a des conséquences surprenantes.

La même section 7 couvre aussi le cas des serveurs de routes, qui est un peu particulier car ces routeurs n'insèrent pas leur propre AS dans le chemin, ils sont censés être transparents.

Enfin, la section 8 traite de quelques points divers, comme le rappel que la RPKI (comme d'ailleurs le DNS) n'est pas cohérente en permanence, car pas transmise de manière synchrone partout. Par exemple, si vous venez d'obtenir un certificat pour un AS, ne l'utilisez pas tout de suite : les annonces BGP se propagent plus vite que les changements dans la RPKI et vos annonces signées risquent donc d'être considérées comme invalides par certains routeurs.


Téléchargez le RFC 8207


L'article seul

RFC 8210: The Resource Public Key Infrastructure (RPKI) to Router Protocol, Version 1

Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : R. Bush (Internet Initiative Japan), R. Austein (Dragon Research Labs)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF sidr
Première rédaction de cet article le 28 septembre 2017


Le protocole décrit dans ce RFC fait partie de la grande famille des RFC sur la RPKI, un ensemble de mécanismes permettant de sécuriser le routage sur l'Internet. Il traite un problème bien particulier : comme la validation des annonces de route, potentiellement complexe en cryptographie, se fait typiquement sur une machine spécialisée, le cache validateur, comment communiquer le résultat de ces validations au routeur, qui va devoir accepter ou rejeter des routes ? C'est le rôle du protocole RTR (RPKI/Router Protocol), un protocole très simple. Ce RFC décrit une nouvelle version de RTR, la version 1, la première (version 0) était dans le RFC 6810.

L'opérateur Internet a donc deux acteurs qui font du RTR entre eux, le cache validateur qui récupère les ROA (Route Origin Authorization, cf. RFC 6482) par rsync, les valide cryptographiquement et garde en mémoire le résultat de cette validation (pour des exemples d'utilisation de deux programmes mettant en œuvre cette fonction, voir mon article sur les logiciels de la RPKI), et le routeur (Cisco, Juniper, Unix avec Quagga, etc). Ce dernier joue un rôle critique et on ne souhaite pas trop charger la barque en lui imposant les opérations assez lourdes du validateur. Dans la plupart des déploiements de la RPKI, le routeur ne fait donc que récupérer des résultats simples (« ce préfixe IP peut être annoncé par cet AS »). Le jeu de données que connait le cache validateur a un numéro de série, qui est incrémenté dès qu'il y a une modification (RFC 1982). Le cache peut donc facilement savoir si un de ses clients routeur est à jour en comparant son numéro de série avec celui du routeur. Le numéro de série est spécifique à un cache donné, il n'a pas de signification globale. Le routeur ne validant pas, le cache qu'il utilise doit donc être une machine de confiance.

Un autre acteur, externe à l'opérateur, est la RPKI, l'ensemble des informations de routage publiées (RFC 6481), et qui sont transportées en général en rsync et validées par les signatures cryptographiques qu'elles portent.

Le routeur va ouvrir une connexion avec un ou plusieurs caches et transmettre le numéro de série de ses données. Le cache va lui envoyer les données plus récentes que ce numéro (ou bien rien, si le routeur est à jour). Le cache dispose aussi d'un mécanisme de notification pour dire à tous les routeurs connectés « j'ai du nouveau ». La section 10 décrit plus en détail le processus avec plusieurs caches.

La section 5 décrit le format des paquets : chacun, outre le classique champ version (1 dans ce RFC, c'était 0 dans le RFC précédent), a un type, indiqué par un entier de 8 bits (requête, réponse, etc), le numéro de série de l'envoyeur et un identifiant de session, qui identifie le serveur du cache (pour détecter une remise à zéro d'un cache, par exemple), la longueur du préfixe annoncé et bien sûr le préfixe lui-même.

Les types de paquets les plus utiles (la liste complète est dans un registre IANA) :

  • Serial Notify (type 0) qui sert au cache à envoyer une notification non sollicitée par les routeurs « J'ai du nouveau ».
  • Serial Query (type 1) où le routeur demande au cache validateur quelles informations il possède, depuis le numéro de série indiqué par le routeur.
  • Cache Response (type 3), la réponse à la requête précédente, composée de :
  • IPv4 Prefix (type 4) et IPv6 Prefix (type 6) qui indiquent un préfixe IP avec les numéros de systèmes autonomes autorisés à l'annoncer.
  • End of Data (type 7) : comme son nom l'indique.
  • Error (type 10), envoyé lorsque le précédent message avait déclenché un problème.

Le dialogue typique entre un routeur et un cache validateur est décrit en section 8 (le RFC détaille aussi le dialogue de démarrage, ici, je ne montre que celui de fonctionnement régulier) :

  • Le routeur demande des données avec un Serial Query ou Reset Query),
  • Le validateur envoie successivement un Cache Response, plusieurs IPvX Prefix, puis un End of Data.

À noter que puisqu'il y a désormais deux versions de RTR dans la nature, il a fallu mettre en place un système de négociation, décrit dans la section 7. Lorsqu'un cache/validateur connait la version 1 (celle de notre nouveau RFC), et reçoit une requête de version 0 du routeur, il doit se rabattre sur la version 0 (s'il la connait, et veut bien la parler), ou bien envoyer un message d'erreur (code 4 « Unsupported Protocol Version »).

Si c'est l'inverse (routeur récent parlant à un vieux cache/validateur), deux cas, qui dépendent de la politique du validateur. S'il répond avec une erreur, cela peut être intéressant de réessayer avec le protocole 0, si le routeur le connait. Si, par contre, le validateur répond avec des messages de version 0, le routeur peut décider de continuer en version 0.

Les codes d'erreur possibles sont décrits dans la section 12 et font l'objet d'un registre IANA. On y trouve par exemple 0 (données corrompues), 1 (erreur interne dans le client ou le serveur), 2 (pas de nouvelles données ; ce n'est pas à proprement parler une erreur).

La question du transport des données entre le cache et le routeur est traitée dans la section 9. Le principal problème est le suivant : comme le but de l'opération RPKI+ROA est d'augmenter la sécurité du routage, il faut que chaque maillon de la chaîne soit sécurisé. Il ne servirait pas à grand'chose de déployer une telle usine à gaz de validation si la liaison entre le routeur et le cache permettait à un intermédiaire de changer les données en cours de route. Il était donc tentant de normaliser une technique de sécurité particulière pour le transport, comme par exemple TLS. Malheureusement, il n'existe pas à l'heure actuelle de technique accessible sur tous les routeurs. L'IETF ayant toujours soin de normaliser des protocoles réalistes, qui puissent être mis en œuvre et déployés, le choix a été fait de ne pas imposer une technique de sécurisation particulière. L'interopérabilité en souffre (un cache et un routeur peuvent ne pas avoir de protocole de sécurité en commun) mais c'était la seule option réaliste. (J'ajoute que, si le but de la séparation du routeur et du validateur était de dispenser ce dernier des calculs cryptographiques, il ne serait pas logique de lui imposer ensuite une session protégée cryptographiquement avec le validateur.)

Le RFC se contente donc de donner une préférence à AO (TCP Authentication Option, cf. RFC 5925) en signalant qu'elle sera la technique préférée, dès lors qu'elle sera disponible sur tous les routeurs (ce qui est très loin d'être le cas en 2017, les progrès sont faibles depuis le précédent RFC sur RTR). Les algorithmes du RFC 5926 doivent être acceptés.

En attendant AO, et pour s'assurer qu'il y aura au moins un protocole commun, le RFC spécifie que routeurs et caches doivent pouvoir se parler en utilisant du TCP nu, sans sécurité, vers le port 323. Dans ce cas, le RFC spécifie que routeur et cache validateur doivent être sur le même réseau (idéalement, sur le même segment, cf. section 13) et que des mécanismes, par exemple de contrôle d'accès physiques, empêchent les méchants de jouer avec ce réseau.

Au cas où AO ne soit pas disponible (la quasi-totalité des routeurs aujourd'hui) et où le transport non sécurisé soit jugé nettement trop faible, le RFC présente plusieurs transports sécurisés possibles. Par exemple, SSH (RFC 4252), avec une sécurisation des clés obtenue par un moyen externe (entrée manuelle des clés, par exemple). Le sous-système SSH à utiliser est nommé rpki-rtr. Notez que, si SSH n'a pas été rendu obligatoire, alors que quasiment tous les routeurs ont un programme SSH, c'est parce que ce programme n'est pas toujours disponible sous forme d'une bibliothèque, accessible aux applications comme RTR.

Autre alternative de sécurisation évidente, TLS. Le RFC impose une authentification par un certificat client, comportant l'extension subjectAltName du RFC 5280, avec une adresse IP (celle du routeur qui se connecte, et que le serveur RTR, la cache validateur, doit vérifier). Le client RTR, lui (le routeur), utilisera le nom de domaine du serveur RTR (le cache) comme entrée pour l'authentification (cf. RFC 6125). N'importe quelle AC peut être utilisée mais notre RFC estime qu'en pratique, les opérateurs créeront leur propre AC, et mettront son certificat dans tous leurs routeurs.

Notez que ce problème de sécurité ne concerne que le transport entre le routeur et le cache validateur. Entre les caches, ou bien entre les caches et les serveurs publics de la RPKI, il n'y a pas besoin d'une forte sécurité, l'intégrité des données est assurée par la validation cryptographique des signatures sur les objets.

Ce RFC spécifie un protocole, pas une politique. Il pourra y avoir plusieurs façons de déployer RTR. La section 11 donne un exemple partiel de la variété des scénarios de déploiement, notamment :

  • Le petit site (« petit » est relatif, il fait quand même du BGP) qui sous-traite la validation à un de ses opérateurs. Ses routeurs se connectent aux caches validateurs de l'opérateur.
  • Le grand site, qui aura sans doute plusieurs caches en interne, avec peut-être un repli vers les caches de l'opérateur en cas de panne.
  • L'opérateur qui aura sans doute plusieurs caches dans chaque POP (pour la redondance).

Pour limiter la charge sur les serveurs rsync de la RPKI (voir RFC 6480), il est recommandé d'organiser les caches de manière hiérarchique, avec peu de caches tapant sur les dépôts rsync publics, et les autres caches se nourrissant (et pas tous en même temps) à partir de ces caches ayant un accès public.

Bien que ce protocole RTR ait été conçu dans le cadre de la RPKI, il peut parfaitement être utilisé avec d'autres mécanismes de sécurisation du routage, puisque RTR se contente de distribuer des autorisations, indépendemment de la manière dont elles ont été obtenues.

Et puis un petit point opérationnel : sur le validateur/cache, n'oubliez pas de bien synchroniser l'horloge, puisqu'il faudra vérifier des certificats, qui ont une date d'expiration…

Quels sont les changements par rapport au RFC original, le RFC 6810, qui normalisait la version 0 de RTR ? Ces changements sont peu nombreux, mais, du fait du changement de version, les versions 0 et 1 sont incompatibles. La section 1.2 résume ce qui a été modifié entre les deux RFC :

  • Un nouveau type de paquet, Router Key (type 9),
  • Des paramètres temporels explicites (durée de vie des informations, etc),
  • Spécification de la négociation de la version du protocole, puisque les versions 0 et 1 vont coexister pendant un certain temps.

Aujourd'hui, qui met en œuvre RTR ? Il existe en logiciel libre une bibliothèque en C, RTRlib. Elle a fait l'objet d'un bon article de présentation (avec schéma pour expliquer RTR). Elle peut tourner sur TCP nu mais aussi sur SSH. RTRlib permet d'écrire, très simplement, des clients RTR en C, pour déboguer un cache/validateur, ou bien pour extraire des statistiques. Un client simple en ligne de commande est fourni avec, rtrclient. Il peut servir d'exemple (son code est court et très compréhensible) mais aussi d'outil de test simple. rtrclient se connecte et affiche simplement les préfixes reçus. Voici (avec une version légèrement modifiée pour afficher la date) son comportement (en face, le RPKI Validator du RIPE-NCC, dont il faut noter qu'il ne marche pas avec Java 9, il faut reculer vers une version plus vieille, et qu'il ne gère pas encore la version 1 de RTR) :

% rtrclient tcp localhost 8282
...
2012-05-14T19:27:42Z + 195.159.0.0         16-16        5381
2012-05-14T19:27:42Z + 193.247.205.0       24-24       15623
2012-05-14T19:27:42Z + 37.130.128.0        20-24       51906
2012-05-14T19:27:42Z + 2001:1388::         32-32        6147
2012-05-14T19:27:42Z + 2001:67c:2544::     48-48       44574
2012-05-14T19:27:42Z + 178.252.36.0        22-22        6714
2012-05-14T19:27:42Z + 217.67.224.0        19-24       16131
2012-05-14T19:27:42Z + 77.233.224.0        19-19       31027
2012-05-14T19:27:42Z + 46.226.56.0         21-21        5524
2012-05-14T19:27:42Z + 193.135.240.0       21-24         559
2012-05-14T19:27:42Z + 193.247.95.0        24-24       31592
...

Autre bibliothèque pour développer des clients RTR, cette fois en Go, ma GoRTR. Le programmeur doit fournir une fonction de traitement des données, qui sera appelée chaque fois qu'il y aura du nouveau. GoRTR est fournie avec deux programmes d'exemple, un qui affiche simplement les préfixes (comme rtrclient plus haut), l'autre qui les stocke dans une base de données, pour étude ultérieure. Par exemple, on voit ici dans cette base de données que la majorité des préfixes annoncés autorisent une longueur de préfixe égale à la leur (pas de tolérance) :

essais=> SELECT count(id) FROM Prefixes WHERE serialno=(SELECT max(serialno) FROM Prefixes) AND 
   maxlength=masklen(prefix);
 count 
-------
  2586
(1 row)

essais=> SELECT count(id) FROM Prefixes WHERE serialno=(SELECT max(serialno) FROM Prefixes) AND 
   maxlength>masklen(prefix);
 count 
-------
  1110
(1 row)

Et voici une partie de évenements d'une session RTR, stockée dans cette base:

 tests=> select * from events;
 id |            time            |             server              |              event              | serialno 
----+----------------------------+---------------------------------+---------------------------------+----------
 ...
 12 | 2017-06-24 13:31:01.445709 | rpki-validator.realmv6.org:8282 | (Temporary) End of Data         |    33099
 13 | 2017-06-24 13:31:01.447959 | rpki-validator.realmv6.org:8282 | Cache Response, session is 4911 |    33099
 14 | 2017-06-24 13:31:25.932909 | rpki-validator.realmv6.org:8282 | (Temporary) End of Data         |    33099
 15 | 2017-06-24 13:31:38.64284  | rpki-validator.realmv6.org:8282 | Serial Notify #33099 -> #33100  |    33099
 16 | 2017-06-24 13:31:38.741937 | rpki-validator.realmv6.org:8282 | Cache reset                     |    33099
 17 | 2017-06-24 13:31:38.899752 | rpki-validator.realmv6.org:8282 | Cache Response, session is 4911 |    33099
 18 | 2017-06-24 13:32:03.072801 | rpki-validator.realmv6.org:8282 | (Temporary) End of Data         |    33100
 ...

Notez que GoRTR gère désormais les deux versions du protocole mais ne connait pas la négociation de version de la section 7 du RFC : il faut indiquer explicitement quelle version on utilise.

Autrement, Cisco et Juniper ont tous les deux annoncé que leurs routeurs avaient un client RTR, et qu'il a passé des tests d'interopérabilité avec trois mises en œuvre du serveur (celles de BBN, du RIPE et de l'ISC). Ces tests d'interopérabilité ont été documentés dans le RFC 7128. Quant aux serveurs, un exemple est fourni par le logiciel RPKI du RIPE-NCC qui fournit un serveur RTR sur le port 8282 par défaut.

En 2013, un client RTR récupèrait 3696 préfixes (ce qui est plus que le nombre de ROA puisqu'un ROA peut comporter plusieurs préfixes). C'est encore très loin de la taille de la table de routage globale et il reste donc à voir si l'infrastructure suivra. (Sur ma station de travail, le validateur, écrit en Java, rame sérieusement.)

Pour les curieux, pourquoi est-ce que l'IETF n'a pas utilisé Netconf (RFC 6241) plutôt que de créer un nouveau protocole ? Les explications sont dans une discussion en décembre 2011. Le fond de la discussion était « les informations de la RPKI sont des données ou de la configuration » ? La validation est relativement statique mais reste quand même plus proche (fréquence de mise à jour, notamment) des données que de la configuration.

Si vous voulez tester un client RTR, il existe un serveur public rpki-validator.realmv6.org:8282. Voici le genre de choses qu'affiche rtrclient (test fait avec un vieux serveur, qui n'est plus disponible) :

% rtrclient tcp rtr-test.bbn.com 12712
...
2012-05-14T19:36:27Z + 236.198.160.184     18-24  4292108787
2012-05-14T19:36:27Z + 9144:8d7d:89b6:e3b8:dff1:dc2b:d864:d49d 105-124 4292268291
2012-05-14T19:36:27Z + 204.108.12.160       8-29  4292339151
2012-05-14T19:36:27Z + 165.13.118.106      27-28  4293698907
2012-05-14T19:36:27Z + 646:938e:20d7:4db3:dafb:6844:f58c:82d5   8-82  4294213839
2012-05-14T19:36:27Z + fd47:efa8:e209:6c35:5e96:50f7:4359:35ba  20-31  4294900047
...

Question mise en œuvre, il y a apparemment plusieurs logiciels de validateurs/cache qui mettent en œuvre cette version 1, mais je n'ai pas trouvé lesquels. (Le RPKI Validator du RIPE-NCC ne le fait pas, par exemple, ni le serveur public indiqué plus haut, tous les deux ne connaissent que la version 0.)

Wireshark ne semble pas décoder ce protocole. Si vous voulez vous y mettre, un pcap est disponible en rtr.pcap.


Téléchargez le RFC 8210


L'article seul

Version 10 d'Unicode

Première rédaction de cet article le 26 septembre 2017


Le 20 juin (oui, je suis en retard), la nouvelle version d'Unicode est sortie, la 10.0. Une description officielle des principaux changements est disponible mais voici ceux qui m'ont intéressé particulièrement. (Il n'y a pas de changement radical.)

Pour explorer plus facilement la grande base Unicode, j'utilise un programme qui la convertit en SQL et permet ensuite de faire des analyses variées. Faisons quelques requêtes SQL :

ucd=> SELECT count(*) AS Total FROM Characters;
 total  
--------
 136755

Combien caractères sont arrivés avec la version 10 ?


ucd=> SELECT version,count(version) FROM Characters GROUP BY version ORDER BY version::float;
...
 8.0     |  7716
 9.0     |  7500
 10.0    |  8518

8 518 nouveaux. Lesquels ?

ucd=> SELECT To_U(codepoint) AS Codepoint, name FROM Characters WHERE version='10.0';
 codepoint |                                    name                                    
-----------+----------------------------------------------------------------------------
...
U+20BF    | BITCOIN SIGN
...
U+11A00   | ZANABAZAR SQUARE LETTER A
...
U+11D01   | MASARAM GONDI LETTER AA
...
U+1F6F8   | FLYING SAUCER
...
U+1F96A   | SANDWICH

Le premier cité va évidemment faire plaisir aux possesseurs de bitcoins. Mais on trouve aussi quatre écritures entièrement nouvelles comme le zanabazar ou le masaram gondi cités en exemple plus haut. Et il y a bien sûr l'habituel lot d'emojis pour faire rire les réseaux sociaux. Tiens, d'ailleurs, combien de caractères sont des symboles (il n'y a pas que les emojis parmi eux, mais Unicode n'a pas de catégorie « emoji ») :

 ucd=> SELECT count(*) FROM Characters  WHERE category IN ('Sm', 'Sc', 'Sk', 'So');
 count 
-------
  6978

Ou, en plus détaillé, et avec les noms longs des catégories :

ucd=> SELECT description,count(category) FROM Characters,Categories WHERE Categories.name = Characters.category AND category IN ('Sm', 'Sc', 'Sk', 'So') GROUP BY category, description;
   description   | count 
-----------------+-------
 Currency_Symbol |    54
 Other_Symbol    |  5855
 Modifier_Symbol |   121
 Math_Symbol     |   948
(4 rows)

Si vous avez les bonnes polices de caractères, voici les caractères pris en exemple plus haut : , 𑨀, 𑴁, 🛸, 🥪… (Si vous n'avez pas les bonnes polices, chaque lettre est un lien vers Uniview.)


L'article seul

RFC 8248: Security Automation and Continuous Monitoring (SACM) Requirements

Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : N. Cam-Winget (Cisco), L. Lorenzin (Pulse Secure)
Pour information
Réalisé dans le cadre du groupe de travail IETF sacm
Première rédaction de cet article le 25 septembre 2017


Le groupe de travail SACM de l'IETF bosse sur une architecture, un modèle de données, et des protocoles pour suivre l'état de la sécurité de ses machines. Ceci est son deuxième RFC, qui vise à définir le cahier des charges de la future solution.

Le cœur de cette idée est l'évaluation de la sécurité sur chaque machine du réseau. Il faut la déterminer, et acheminer le résultat jusqu'aux responsables de la sécurité. Cela nécessite donc un modèle de données (comment on décrit « la sécurité d'une machine » ?) puis un protocole permettant de gérer un très grand nombre de machines. Parmi les informations à récolter sur chaque machine, on trouvera évidemment la liste des logiciels installés, les logiciels serveurs en activité qui écoutent sur le réseau, l'état de la mise à jour (« pas pu joindre le dépôt officiel de logiciels depuis N jours »), etc. Le cahier des charges décrit dans ce RFC part des scénarios d'usage du RFC 7632. Ce sont souvent de grandes généralités. On est loin de mécanismes concrets.

La section 2 forme l'essentiel de ce RFC, avec la liste des exigences du cahier des charges. Je ne vais en citer que quelques unes. La première, nommée G-001 (« G » préfixe les exigences générales sur l'ensemble du système SACM), demande que toute la solution SACM soit extensible, pour les futurs besoins. G-003 est la possibilité de passage à l'échelle. Par exemple, les messages échangés peuvent aller de quelques lignes à, peut-être plusieurs gigaoctets si on fait une analyse détaillée d'une machine. Et il peut y avoir beaucoup de machine avec des échanges fréquents (le RFC n'est pas plus précis sur les chiffres).

Les données traitées sont évidemment sensibles, et G-006 demande que la solution permette évidemment de préserver la confidentialité des données.

Les exigences sur l'architecture de la future solution sont préfixées de « ARCH ». Ainsi, ARCH-009 insiste sur l'importance d'une bonne synchronisation temporelle de toutes les machines. Un journal des connexions/déconnexions des utilisateurs, par exemple, n'aurait guère d'intérêt si l'horloge qui sert à l'estampiller n'est pas exacte.

D'autres sections décrivent les exigences pour le modèle de données et pour les protocoles, je vous laisse les découvrir.


Téléchargez le RFC 8248


L'article seul

RFC 7632: Endpoint Security Posture Assessment: Enterprise Use Cases

Date de publication du RFC : Septembre 2015
Auteur(s) du RFC : D. Waltermire, D. Harrington
Pour information
Première rédaction de cet article le 25 septembre 2017


La sécurité est évidemment une préoccupation courante quand on gère un réseau (« enterprise network » dit ce document, mais cela s'applique à n'importe quel réseau, capitaliste ou pas). Parmi les problèmes, déterminer l'état de la sécurité de chaque machine du réseau (son logiciel est à jour ? Pas de RAT installé ?) est un des plus importants. Ce RFC décrit des scénarios où on cherche à analyser cet état de la sécurité.

Disons-le tout de suite, une grande partie du RFC est du jargon « process » (quand on remplace l'intelligence par la paperasserie). Si vous n'êtes pas manager chargé d'obtenir une certification ISO 27001, cela ne vous intéressera guère. La seule partie utile est, dans section 2.2 du RFC, quelques scénarios intéressants.

J'ai bien aimé le scénario de la section 2.2.5, où un groupe de chercheurs (ah, justement, ce n'est plus du « enterprise network ») travaille dans la station polaire Zebra (oui, c'est une référence au film) et doit maintenir à jour ses ordinateurs, malgré une liaison Internet intermittente, chère (et la recherche publique n'a jamais d'argent) à faible capacité et énorme latence. Non seulement les conditions sont très dures, mais ils doivent en outre se plier à la « conformité » (compliance), le buzz word à la mode. Une partie du temps de recherche sera donc consommé à suivre les process.


Téléchargez le RFC 7632


L'article seul

On peut tout mettre dans le DNS, même les codes postaux

Première rédaction de cet article le 24 septembre 2017


Petit service juste pour s'amuser : mettre l'ensemble des codes postaux français dans le DNS avec pour chaque code, la ou les communes concernées, leur longitude et leur latitude.

À quoi ça sert ? Pas à grand'chose, je voulais juste illustrer l'utilisation de deux types d'enregistrements DNS peu connus, LOC (RFC 1876) et URI (RFC 7553). (Au passage, si vous lisez que le DNS sert à traduire des noms en adresses IP, allez lire autre chose : c'est faux, ou, en tout cas, très réducteur.) Et traiter un peu de JSON en prévision de la prochaine sortie du nouveau RFC sur JSON. Voyons d'abord le résultat avec le traditionnel client DNS dig :

	   
% dig +short +nodnssec TXT 34000.cp.bortzmeyer.fr
"MONTPELLIER"

	 

L'enregistrement TXT permet d'indiquer le nom de la ville concernée. Parfois, il y en a plusieurs :

% dig +short +nodnssec TXT 52100.cp.bortzmeyer.fr
"HALLIGNICOURT"
"VALCOURT"
"CHANCENAY"
"SAPIGNICOURT"
"ST DIZIER"
"PERTHES"
"ST EULIEN"
"BETTANCOURT LA FERREE"
"LANEUVILLE AU PONT"
"VILLIERS EN LIEU"
"MOESLAINS"

Le même domaine cp.bortzmeyer.fr contient également la position de la ville (ou plus probablement celle du bureau de poste principal), avec le type LOC, normalisé dans le RFC 1876) :

% dig +short +nodnssec LOC 34000.cp.bortzmeyer.fr
43 36 47.108 N 3 52 9.113 E 0.00m 1m 10000m 10m
    

Nous voyons ici que Montpellier est à 43° 36' 47'' de latitude Nord et 3° 52' 9'' de longitude Est. Si vous voulez voir tout de suite où ça se trouve, vous avez également un URI (type d'enregistrement DNS normalisé dans le RFC 7553) qui pointe vers OpenStreetMap (merci à OpenStreetMap de permettre de fabriquer des URL simples) :

   
% dig +short +nodnssec URI 52100.cp.bortzmeyer.fr
10 1 "http://www.openstreetmap.org/?mlat=48.646559&mlon=4.912187&zoom=12"

(L'enregistrement URI étant relativement récent, il ne faut pas utiliser un dig trop vieux. Par exemple, avec la version 9.8.3, on n'a pas de résultat avec cette commande.) Ce n'est pas très joli avec dig ? Pas convivial ? Vous pouvez aussi essayer un DNS Looking Glass. Par exemple, toutes les données pour 73370, la localisation de 73700 ou les noms associés à 97434.

Comment est-ce que ce service a été réalisé ? Je suis parti d'un fichier JSON contenant les informations nécessaires, un petit programme Python le transforme en fichier de zone DNS (section 5 du RFC 1035). Que du très simple. Le plus dur était certainement de trouver les données. En France, il y a une tradition régalienne de ne pas donner les informations au citoyen. L'État préfère les garder pour lui, ce qui obligeait à des contorsions compliquées. Le mouvement « données ouvertes » a heureusement changé les choses, même si c'est encore loin d'être parfait.

La source que j'ai utilisée est la « Base officielle des codes postaux » (trouvée via data.gouv.fr). Je ne dis pas que cette base est meilleure que les autres, mais son côté « officiel » me permet de répondre « c'est pas ma faute » si quelqu'un y trouve une erreur ou une approximation. J'ai utilisé la version GeoJSON du fichier. Elle n'est pas parfaite. Ainsi il y a un certain nombre de fautes d'orthographe comme Marolles-sous-Lignières écrit MAROLLES SOUS LIGNIERES, sans l'accent (les tirets et les apostrophes manquent également). D'autre part, certaines communes, notamment en Polynésie française, n'ont pas d'information de localisation. Ici, à Fakarava :

% dig +short +nodnssec LOC 98764.cp.bortzmeyer.fr
%     
   

Dernier problème, la liste n'est pas à jour. La commune de Meaulne-Vitray (code postal 03360), créée le 1er janvier 2017, n'apparait pas encore dans la liste, téléchargée le 24 septembre 2017. (Et elle n'est pas la seule.)

D'autres sources possibles de données avaient été considérées (et merci à Sabine Blanc, Olivier Macchioni, Pascal Corpet, Laurent Penou, Adrien Clerc, Ainiton et tous les autre qui m'ont fait d'excellentes suggestions) :

  • On trouve à certains endroits des fichiers structurés contenant cette information, mais ils ne sont pas forcément à jour (les codes postaux changent rarement mais ils changent). Ainsi, http://www.aito.fr/maps_ville.sql est une excellente base MySQL, où les noms sont bien orthographiés, mais qui est vieille de nombreuses années. D'autre part, elle semble incomplète (Lille n'y a qu'un seul code postal alors qu'il en existe plusieurs en pratique).
  • Geofla a remplacé l'ancien RGC de l'IGN mais il ne semble pas accessible dans un format ouvert. (Et, de toute façon, il a les longitude et latitude mais pas les codes postaux, il aurait donc fallu faire une jointure sur les noms des communes.) À l'IGN, il y a aussi Admin Express, dans un format que je ne connais pas.
  • Souvent, les meilleures bases sont celles gérées par des initiatives citoyennes, comme NosDonnées. Le fichier des communes semble une piste intéressante , et où les noms sont correctement orthographiés (mais je préfère JSON à CSV).
  • À une époque, la Poste, pourtant service public, vendait la base des codes postaux. Il semble qu'il existe toujours un service commercial à ce sujet.
  • Les données nécessaires se trouvent dans Wikipédia (regardez par exemple la fiche de Saint-Dizier, code postal 52100, postition 48° 38′ 21″ nord, 4° 57′ 09″ est). On pourrait l'extraire à partir de liste des communes. Mais peut-être que Wikidata offre désormais une solution plus simple (un exemple de requête SPARQL).
  • La base d'adresses nationale est téléchargeable sous forme d'un gros fichier JSON, apparemment à jour, où on trouve les longitudes et latitudes de chaque rue (et associées au code postal). Donc, cela pourrait être une meilleure alternative (mais je ne suis pas sûr de la licence).
  • Le site http://www.galichon.com/codesgeo/ permet de trouver codes postaux et positions géographiques, et sa base (à jour ? correcte ?) est téléchargeable (au format Microsoft Excel).
  • La base de données de Geonames semble prometteuse.

Si vous voulez proposer d'autres bases, n'oubliez pas de regarder ces points :

  • Format ouvert,
  • Base légalement téléchargeable et utilisable,
  • Base complète (les COM…),
  • Disponibilité de la longitude et de la latitude,
  • Téléchargeable en bloc (pas limité à une API),
  • Base à jour (les communes changent, et les codes postaux aussi, d'ailleurs, si quelqu'un connait une liste des récents changements, afin de pouvoir tester la fraîcheur des bases… Je trouve des informations non officielles ou anciennes, Christian Quest me signale que la bonne est apparemment à la Poste),
  • Orthographe correcte (respect des accents, pas d'abréviation genre ST pour Saint).

Un peu de technique pour finir. Le script lancé par cron tous les mois fait :

wget -O laposte_hexasmal.geojson 'https://datanova.legroupe.laposte.fr/explore/dataset/laposte_hexasmal/download/?format=geojson'
./laposte2dns.py laposte_hexasmal.geojson > cp.zone
ods-signer sign bortzmeyer.fr
  

La première commande télécharge le fichier au format JSON. La seconde (le script laposte2dns.py) convertit le JSON en fichier de zone DNS qui ressemble donc à :

  

10130.cp IN TXT "MAROLLES SOUS LIGNIERES"
   IN LOC 48 3 7.201 N 3 55 56.876 E 0
   IN URI 10 1 "http://www.openstreetmap.org/?mlat=48.052000&mlon=3.932466&zoom=12"

10170.cp IN TXT "LES GRANDES CHAPELLES"
   IN LOC 48 29 29.856 N 3 56 0.604 E 0
   IN URI 10 1 "http://www.openstreetmap.org/?mlat=48.491627&mlon=3.933501&zoom=12"

10110.cp IN TXT "MERREY SUR ARCE"
   IN LOC 48 6 57.679 N 4 25 51.357 E 0
   IN URI 10 1 "http://www.openstreetmap.org/?mlat=48.116022&mlon=4.430933&zoom=12"

...

Le fichier ainsi produit (dans les onze mégaoctets) est chargé depuis le fichier de zone principal de bortzmeyer.fr, via une directive d'inclusion :

   
; Codes Postaux
$INCLUDE /etc/nsd/primary/cp.incl

Enfin, la troisième commande utilise OpenDNSSEC pour signer la zone, et recharger le serveur de noms.

Un tel service dans le DNS existe déjà pour le Royaume-Uni, via le domaine find.me.uk.


L'article seul

RFC 8222: Selecting Labels for Use with Conventional DNS and Other Resolution Systems in DNS-Based Service Discovery

Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : A. Sullivan (Dyn)
Pour information
Réalisé dans le cadre du groupe de travail IETF dnssd
Première rédaction de cet article le 22 septembre 2017


DNS-SD (DNS-based Service Discovery, normalisé dans le RFC 6763) permet de découvrir des services dans le réseau, via le DNS, mais aussi via d'autres systèmes de résolution de noms, comme mDNS (RFC 6762). Ces différents systèmes de résolution ayant des propriétés différentes, il peut se poser des problèmes d'interopérabilité. Ce RFC documente ces problèmes et est donc une lecture recommandée pour les développeurs DNS-SD.

Rappelons un peu de contexte (section 1 du RFC) : les applications qui utilisent le DNS imposaient fréquemment une syntaxe réduite aux noms manipulés, la syntaxe « LDH » ([ASCII] Letters, Digits and Hyphen), décrite dans le RFC 952. (Contrairement à ce qu'on lit souvent, le DNS n'impose pas une telle limite, elle est purement dans les applications.) Du fait de ces règles des applications, il a fallu, pour utiliser des lettres Unicode, un système spécial, IDN (RFC 5890). IDN ajoute ses propres contraintes, comme le fait que les « non-lettres » (par exemple les emojis) ne sont pas acceptés.

Le DNS accepte tout à fait du binaire quelconque dans les noms (donc, par exemple, des caractères non-ASCII avec des encodages comme Latin-1). mDNS (RFC 6762) autorise également les caractères non-ASCII, mais impose l'encodage UTF-8. mDNS autorise des caractères qui ne sont pas permis en IDN, du banal espace aux symboles les plus rigolos. DNS-SD recommande même leur usage (RFC 6763, section 4.1.3). La seule contrainte est de se limiter au format Unicode du réseau, décrit dans le RFC 5198.

Bien des développeurs d'application ne sont pas au courant de ces subtilités. Ils manipulent des noms de services comme des chaînes de caractères, et ne se soucient pas du système de résolution sous-jacent et de ses particularités. Or, potentiellement, un même nom peut être accepté par certains de ces systèmes de résolution et refusés par d'autres.

Si le développeur est au courant, et s'il prend soin de faire en sorte que les noms « avancés » ne soient pas envoyés aux systèmes de résolution les plus anciens, ça peut marcher. Mais il ne faut pas se faire d'illusion, les fuites se produisent et tout nom utilisé sur une machine connectée à l'Internet finira tôt ou tard par arriver au DNS, comme le montre le trafic que reçoivent les serveurs de noms de la racine.

Notez enfin que les utilisateurs de DNS-SD, encore plus que ceux des noms classiques, ne tapent que très rarement les noms : presque toujours, ils les choisissent dans une liste.

Maintenant, pourquoi y a-t-il un problème ? (Section 2 du RFC.) Comme je l'ai indiqué plus haut, le DNS accepte n'importe quoi dans un nom. Pourquoi ne pas juste dire « envoyons le nom "Joe's printer, first floor" directement dans le DNS, et ça marchera » ? Il y a deux problèmes avec cette approche. D'abord, l'application qui ne ferait pas attention et enverrait un nom non-ASCII en disant « le DNS s'en tirera de toute façon », cette application oublie qu'il peut y avoir sur le trajet de sa requête une couche logicielle qui fait de l'IDN et va donc encoder en Punycode (transformation du U-label en A-label). Le « vrai » système de résolution ne verra donc pas le nom original. Un problème du même genre arrive avec certains logiciels qui se mêlent de politique, par exemple les navigateurs Web comme Firefox qui se permettent d'afficher certains noms en Unicode et d'autres en ASCII, selon la politique du TLD. Bref, le trajet d'un nom dans les différents logiciels est parsemé d'embûches si on est en Unicode.

En outre, l'approche « j'envoie de l'UTF-8 sans me poser de questions » (suggérée par la section 4.1.3 du RFC 6763) se heurte au fait que la racine du DNS et la plupart des TLD ne permettent de toute façon par d'enregistrer directement de l'UTF-8. (Au passage, le RFC oublie un autre problème de l'UTF-8 envoyé directement : les serveurs DNS ne font pas de normalisation Unicode, et l'insensibilité à la casse du DNS n'est pas évidente à traduire en Unicode.)

La section 3 de notre RFC propose donc de travailler à un profil permettant une meilleure interopérabilité. Un profil est une restriction de ce qui est normalement permis (un dénominateur commun), afin de passer à peu près partout. Un exemple de restriction serait l'interdiction des majuscules. En effet, le DNS est insensible à la casse mais IDN interdit les majuscules (RFC 5894, sections 3.1.3 et 4.2) pour éviter le problème de conversion majuscules<->minuscules, qui est bien plus difficile en Unicode qu'en ASCII.

Notez que notre RFC ne décrit pas un tel profil, il propose sa création, et donne quelques idées. Il y a donc encore du travail.

Pour rendre le problème plus amusant, les noms utilisés par DNS-SD sont composés de trois parties, avec des règles différentes (section 4 de notre RFC, et RFC 6763, section 4.1) :

  • « Instance » qui est la partie présentée à l'utilisateur (« Imprimante de Céline »), et où donc tous les caractères sont acceptés,
  • « Service », qui indique le protocole de transport, et qui se limite à un court identificateur technique en ASCII, jamais montré à l'utilisateur et qui, commençant par un tiret bas, n'est pas un nom de machine légal et est donc exclu de tout traitement IDN,
  • « Domaine », qui est le domaine où se trouve l'instance. Il est conçu pour être tôt ou tard envoyé au DNS, et il suit donc les règles IDN.

L'instance risque d'être « interceptée » par le traitement IDN et, à tort, transformée en Punycode. Limiter le nom de l'instance aux caractères acceptés par IDN serait horriblement restrictif. La seule solution est probablement de mettre en place du code spécifique qui reconnait les noms utilisés par DNS-SD, pour ne jamais les passer au DNS. Quant à la partie « Domaine », l'idée de la traiter comme un nom de domaine normal, et donc sujet aux règles des RFC 5890 et suivants, est restrictive :

  • Cela revient à abandonner la stratégie de « on essaie UTF-8, puis Punycode » du RFC 6763, section 4.1.3 (mais cette stratégie a l'inconvénient de multiplier les requêtes, donc le trafic et les délais),
  • Elle ne tient pas compte du fait que certains administrateurs de zones DNS peuvent avoir envie de mettre de l'UTF-8 directement dans la zone (ce qui est techniquement possible, mais va nécessiter de synchroniser dans la zone la représentation UTF-8 et la représentation Unicode du même nom, ou qu'il utilise les DNAME du RFC 6672).

Téléchargez le RFC 8222


L'article seul

Non, BGP ne préfère pas les annonces les plus spécifiques

Première rédaction de cet article le 18 septembre 2017


On lit souvent dans les articles sur la sécurité du routage Internet, ou dans les articles qui décrivent un détournement BGP, volontaire ou non, une phrase du genre « la route pour le préfixe de l'attaquant, un /24, a été acceptée car elle est plus spécifique que le préfixe normal /22, et BGP préfère les routes plus spécifiques ». Ce n'est pas vraiment exact.

La question est importante, car ces détournements BGP arrivent (cf. le rapport de l'ANSSI à ce sujet), le cas le plus célèbre étant Pakistan Telecom contre YouTube, et le plus récent la détournement accidentel par Google, notamment au Japon. Si une organisation annonce un /22, et que l'attaquant publie un /24 mensonger, est-ce que cela aura davantage d'effet que s'il l'attaquant avait publié un /22 ? Et, pour le titulaire légitime du préfixe, est-ce que annoncer des /24 en plus est une bonne défense ? On lit souvent des Oui aux deux questions, avec comme justification que BGP préférerait le préfixe le plus spécifique, et qu'il ne faut donc pas le laisser à l'attaquant. La technique est bonne, mais cette justification n'est pas complètement correcte. (Bien qu'on la trouve même dans des articles sérieux.)

BGP (RFC 4271, notamment la section 9.1), en effet, n'a pas d'opinion sur la longueur des routes. S'il voit un /22 et un /24 plus spécifique, il les considère comme deux préfixes différents, et les garde en mémoire et les passe à ses voisins. (Les experts BGP noteront que j'ai un peu simplifié, BGP ayant plusieurs listes de routes, les RIB - Routing Information Base. La Loc-RIB ne garde que le préfixe spécifique mais l'Adj-RIBs-Out, ce qui est transmis aux voisins, a bien les deux routes.)

C'est IP, pas BGP, qui a une règle « longest prefix » (préfixe le plus spécifique) et qui, lui, choisira le /24. Comme le dit la maxime des opérateurs, « The data plane [IP] does not always follow the control plane [BGP]. »

Vous pouvez facilement voir cela sur un looking glass. Prenons par exemple celui de Level 3, en http://lg.level3.net/. Le préfixe 2405:fd80::/32 a un plus-spécifique, 2405:fd80:100::/40 (il en a même plusieurs, mais je simplifie). Les deux sont transportés par BGP. Le looking glass affiche (j'ai fait deux requêtes séparées, car l'option Longer prefixes de ce looking glass ne marche pas pour moi) :

BGP Routing table entry for 2405:fd80::/32
  3491 135391

...

BGP Routing table entry for 2405:fd80:100::/40
  2914 135391
    

Le routeur connait donc bien les deux préfixes, le générique et le spécifique. (Il s'agit du comportement par défaut de BGP ; il existe des configurations de routeurs qui ne font pas cela, par exemple parce qu'elles agrègent les préfixes les plus spécifiques en un préfixe moins spécifique. Voir par exemple celle de Juniper.)

Mais, si je tape une adresse IP (pas un préfixe) dans un looking glass ou un routeur, je n'obtiens bien que le préfixe plus spécifique ? Oui, mais ce n'est pas un choix de BGP, c'est le choix du routeur sous-jacent de chercher le préfixe immédiatement englobant cette adresse. Tapez un préfixe au lieu d'une adresse IP et vous aurez bien le préfixe demandé.

[Le reste est plus technique, avec du code qui tourne.]

Si vous voulez vous-même analyser la table de routage mondiale, sans recourir à un service cloud comme RIPE Stat, voici deux façons de faire, l'une avec PostgreSQL, et l'autre avec un programme en C. Les deux utilisent la même source de données, RouteViews. RouteViews distribue des mises à jour et des tables complètes (RIB = Routing Information Base). On télécharge la RIB et on la décomprime :

      
% wget http://archive.routeviews.org/bgpdata/2017.09/RIBS/rib.20170905.1200.bz2
% bunzip2 rib.20170905.1200.bz2

    

Elle est au format MRT (RFC 6396). On utilise bgpdump pour la traduire en texte :

    
% bgpdump rib.20170905.1200>rib.20170905.1200.txt

De manière grossière et sauvage, on ne garde que les lignes indiquant un préfixe, et on les dédoublonne (attention, ça va manger de la RAM) :

% grep PREFIX: rib.20170905.1200.txt | sort | uniq > rib.20170905.1200.sorted

À partir de là, on va utiliser soit PostgreSQL, soit le programme en C.

D'abord, avec PostgreSQL, qui a depuis longtemps d'excellents types de données pour les adresses IP, avec plein d'opérations possibles. Pour importer le fichier texte dans PostgreSQL, on utilise un tout petit script Python :

    
% ./dump2psql.py rib.20170905.1200.sorted

Et tout est maintenant dans la base de données, qu'on peut explorer. Par exemple, trouver tous les préfixes plus générique que 192.134.0.0/24 :


% psql bgp
bgp=> SELECT * FROM Prefixes WHERE pfx >> '192.134.0.0/24';
   id    |      pfx       
---------+----------------
 1135437 | 192.134.0.0/16
 1135438 | 192.134.0.0/22
(2 rows)

Et si on veut la liste de toutes les annonces plus spécifiques qu'une annonce existante, ici en IPv6 :

   
bgp=> SELECT gen.pfx AS Generic, spec.pfx AS Specific FROM Prefixes gen,  Prefixes spec
      WHERE family(spec.pfx) = 6 AND  masklen(spec.pfx) < 48 AND spec.pfx << gen.pfx;
       generic       |      specific       
---------------------+---------------------
 2001:1280::/32      | 2001:1280:8000::/36
 2001:128c::/32      | 2001:128c:6000::/36
 2001:128c::/32      | 2001:128c:7000::/40
 2001:12f0::/36      | 2001:12f0:100::/42
...

 

Et si on n'aime pas les SGBD et qu'on veut le faire en C ? On va se servir de la bibliothèque libcidr. Installons :

    
%  wget http://www.over-yonder.net/~fullermd/projects/libcidr/libcidr-1.2.3.tar.xz
% unxz libcidr-1.2.3.tar.xz
% tar xvf libcidr-1.2.3.tar
% cd libcidr-1.2.3
% ./configure
% make
% sudo make install

Puis écrivons un petit programme C qui va lire le fichier texte avec la liste des préfixes et afficher les préfixes plus spécifiques (ou égaux) qu'un préfixe donné (option -s) ou les plus génériques (option -g) :

  
% ./read-mrt rib.20170905.1200.sorted -s 72.249.184.0/21
72.249.184.0/21 (line 700376)
72.249.184.0/24 (line 700377)

% ./read-mrt rib.20170905.1200.sorted -g 72.249.184.0/21
72.249.128.0/18 (line 700369)
72.249.184.0/21 (line 700376)

Merci à Bruno Spiquel pour ses conseils.


L'article seul

RFC 8215: Local-Use IPv4/IPv6 Translation Prefix

Date de publication du RFC : Août 2017
Auteur(s) du RFC : T. Anderson (Redpill Linpro)
Chemin des normes
Première rédaction de cet article le 18 septembre 2017


Ce très court RFC documente la réservation du préfixe IPv6 64:ff9b:1::/48, pour les divers mécanismes de traduction entre IPv6 et IPv4.

L'adressage de ces mécanismes de traduction est décrit dans le RFC 6052. Il réservait le préfixe 64:ff9b::/96, qui s'est avéré insuffisant et est désormais complété par notre RFC 8215.

Ouvrez bien l'œil : ces deux préfixes sont différents, même si leurs quatre premiers octets sont identiques (c'est un problème fréquent en IPv6 : il peut être difficile de distinguer deux adresses du premier coup.)

La section 3 de notre RFC expose le problème : depuis la publication du RFC 6052, plusieurs mécanismes de traduction IPv4<->IPv6 ont été proposés (par exemple dans les RFC 6146 et RFC 7915). Ils ne sont pas inconciliables (ils couvrent parfois des scénarios différents ) et un opérateur peut donc vouloir en déployer plusieurs à la fois. Le seul préfixe 64:ff9b::/96 ne va alors plus suffire. Il faut donc un préfixe plus grand, et dont l'usage n'est pas restreint à une seule technologie de traduction.

Pourquoi 64:ff9b:1::/48, d'ailleurs ? La section 4 répond à la question. La longueur de 48 bits a été choisie pour permettre plusieurs mécanismes de traduction, chacun utilisant un préfixe plus spécifique. Par exemple, si chaque mécanisme utilise un /64, le préfixe réservé pour les englober tous devait être un /48, ou plus général (on se limite aux multiples de 16 bits, car ils permettent que les préfixes se terminent sur un deux-points, facilitant la vie de l'administrateur réseaux qui devra les manipuler).

Ensuite, pourquoi cette valeur 64:ff9b:1:: ? Parce qu'elle est proche (presque adjacente) du 64:ff9b::/96, minimisant la consommation d'adresses IPv6. (64:ff9a:ffff::/48 est complètement adjacent, de l'autre côté, mais l'agrégation avec 64:ff9b::/96 en un seul préfixe est beaucoup moins efficace. Et puis 64:ff9b:1:: est plus court.)

À part son usage, 64:ff9b:1::/48 est un préfixe IPv6 normal et les routeurs, ou autres machines qui manipulent des adresses IPv6, ne doivent pas le traiter différemment des autres.

Le nouveau préfixe est désormais enregistré dans le registre des adresses IPv6 spéciales (section 7 de notre RFC).


Téléchargez le RFC 8215


L'article seul

RFC 8246: HTTP Immutable Responses

Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : P. McManus (Mozilla)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 16 septembre 2017


Lorsqu'un serveur HTTP renvoie une réponse à un client, il peut indiquer une durée de vie maximale de la ressource transmise, avec l'en-tête Cache-Control: (RFC 7234). Mais cela ne donne qu'une durée maximale. La ressource peut quand même être modifiée avant la fin de cette période. Un client HTTP prudent va donc revalider la fraîcheur de cette ressource de temps en temps. L'extension décrite dans ce RFC permet de se dispenser de cette revalidation, en indiquant une durée minimale, pendant laquelle on est sûrs et certains que la ressource ne sera pas modifiée.

Le RFC prend l'exemple (section 1) d'un journal dont la page d'accueil indique une durée de vie maximale d'une heure :

Cache-Control: max-age=3600
    

Le client HTTP qui reçoit cet en-tête dans la réponse sait qu'il doit recontacter le serveur au bout d'une heure. Mais la page sera peut-être modifiée (par exemple en raison d'un évènement soudain et imprévu) avant qu'une heure soit écoulée. Le client est donc partagé entre la revalidation (qui risque de consommer des ressources pour rien, même si elle est moins coûteuse qu'un téléchargement complet, grâce aux trucs du RFC 7232) et le risque de servir à son utilisateur une ressource qui n'est désormais plus à jour. Le problème est d'autant plus ennuyeux que la revalidation d'une bête page Web qui comprend des photos peut générer beaucoup de requêtes, dont la plupart produiront sans doute un 304 (ressource non modifiée, la revalidation était inutile).

Notez que dans certains cas, le contenu pointé par un URL peut changer, mais dans d'autres cas, il est réellement immuable. Par exemple, si on publie chaque version de sa feuille de style sous un URL différent, la CSS n'a pas besoin d'être revalidée.

Bref, il y avait un besoin de pouvoir indiquer l'immuabilité d'une ressource. C'est désormais fait (section 2 de ce RFC) avec une extension à Cache-Control: :

Cache-Control: max-age=3600, immutable
    

Avec le mot-clé immutable, le serveur indique que la ressource ne sera pas modifiée pendant la durée de vie indiquée, le client peut donc se dispenser de vérifier. (« Client » ici désignant aussi bien le client final, par exemple le navigateur Web, qu'un intermédiaire.)

Par exemple, comme les RFC sont immuables (on ne les change jamais même d'une virgule, même en cas d'erreur), la ressource qui désigne ce RFC, https://www.rfc-editor.org/rfc/rfc8246.txt, pourrait parfaitement renvoyer cet en-tête (elle ne le fait pas). Cela pourrait être :

Last-Modified: Thu, 14 Sep 2017 23:11:35 GMT
Cache-Control: max-age=315576000, immutable 
    

(Oui, dix ans…)

Voilà, c'est tout, la directive a été ajoutée au registre IANA. Mais la section 3 du RFC se penche encore sur quelques questions de sécurité. Indiquer qu'une ressource est immuable revient à la fixer pour un temps potentiellement très long, et cela peut donc servir à pérenniser une attaque. Si un méchant pirate un site Web, et sert son contenu piraté avec un en-tête d'immuabilité, il restera bien plus longtemps dans les caches. Pire, sans HTTPS, l'en-tête avec immutable pourrait être ajouté par un intermédiaire (le RFC déconseille donc de tenir compte de cette option si on n'a pas utilisé HTTPS).

Les navigateurs Web ont souvent deux options pour recharger une page, « douce » et « dure » (F5 et Contrôle-F5 dans Firefox sur Unix). Le RFC conseille que, dans le second cas (rechargement forcé), le immutable soit ignoré (afin de pouvoir recharger une page invalide).

Enfin, toujours question sécurité, le RFC recommande aux clients HTTP de ne tenir compte de l'en-tête d'immuabilité que s'ils sont sûrs que la ressource a été transmise proprement (taille correspondant à Content-Length: par exemple).

Si vous voulez comparer deux ressources avec et sans immutable, regardez http://www.bortzmeyer.org/files/maybemodified.txt (sans immutable) et http://www.bortzmeyer.org/files/forever.txt (avec). Si le client HTTP gère l'extension d'immuabilité, un rechargement « doux » ne fera pas de requête HTTP. Sinon, il y aura revalidation et le serveur HTTP renverra un 304 :

[2001:db8:abcd:1234:acd8:9bd0:27c9:3a7f]:45452 - - [16/Sep/2017:17:53:29 +0200] "GET /files/forever.txt HTTP/1.1" 304 - "-" "Mozilla/5.0 (X11; Linux i686; rv:52.0) Gecko/20100101 Firefox/52.0" www.bortzmeyer.org
    

Apparemment, Firefox gère cette extension mais, ici, le Firefox était sans doute trop vieux (normalement, cela aurait dû arriver avec la version 49, puisque Mozilla était premier intéressé). Les auteurs de Squid ont annoncé qu'ils géreraient cette extension. Côté serveur, notons que Facebook envoie déjà cette extension pour, par exemple, le code JavaScript (qui est versionné et donc jamais changé) :


% curl -v https://www.facebook.com/rsrc.php/v3iCKe4/y2/l/fr_FR/KzpL-Ycd5sN.js
* Connected to www.facebook.com (2a03:2880:f112:83:face:b00c:0:25de) port 443 (#0)
...
< HTTP/2 200 
< content-type: application/x-javascript; charset=utf-8
< cache-control: public,max-age=31536000,immutable
...

    

Idem pour une mise en œuvre d'IPFS.


Téléchargez le RFC 8246


L'article seul

RFC 8187: Indicating Character Encoding and Language for HTTP Header Field Parameters

Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : J. Reschke (greenbytes)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 14 septembre 2017
Dernière mise à jour le 16 septembre 2017


Les requêtes et réponses du protocole HTTP incluent des en-têtes (comme User-Agent: ou Content-Disposition:) avec des valeurs, qui, il y a longtemps, ne pouvaient se représenter directement qu'avec les caractères du jeu ISO 8859-1, voire seulement avec ASCII (c'était compliqué). Comme MIME, dans le RFC 2231, prévoyait un mécanisme très riche pour encoder les en-têtes du courrier électronique, ce RFC 8187 réutilise ce mécanisme pour HTTP (il remplace le RFC 5987, qui avait été le premier à le faire). Pour le corps du message (voir par exemple le RFC 7578), rien ne change.

Cette ancienne restriction à Latin-1 (qui n'est plus d'actualité) vient de la norme HTTP, le RFC 2616, dans sa section 2.2, qui imposait l'usage du RFC 2047 pour les caractères en dehors de ISO 8859-1. Le RFC 7230 a changé cette règle depuis (sa section 3.2) mais pas dans le sens d'une plus grande internationalisation (ISO 8859-1 ne convient qu'aux langues européennes), plutôt en supprimant le privilège d'ISO 8859 et en restreignant à ASCII. Et il ne précise pas vraiment comment faire avec d'autres jeux de caractère comme Unicode. Il ne reste donc que la solution du RFC 2231.

Notre nouveau RFC peut être résumé en disant qu'il spécifie un profil du RFC 2231. Ce profil est décrit en section 3, qui liste les points précisés par rapport au RFC 2231. Tout ce RFC n'est pas utilisé, ainsi le mécanisme en section 3 du RFC 2231, qui permettait des en-têtes de plus grande taille, n'est pas importé (section 3.1 de notre RFC).

En revanche, la section 4 du RFC 2231, qui spécifiait comment indiquer la langue dans laquelle était écrite la valeur d'un en-tête est repris pour les paramètres dans les en-têtes. Ainsi, (section 3.2), voici un en-tête (imaginaire : Information: n'a pas été enregstré), avec un paramètre title traditionnel en pur ASCII :

Information: news; title=Economy

et en voici un avec les possibilités de notre RFC pour permettre les caractères £ et € (« Sterling and euro rates ») :

Information: news; title*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates

Par rapport au RFC 2231 (qui était silencieux sur ce point), un encodage de caractères est décrété obligatoire (c'est bien sûr UTF-8), et il doit donc être géré par tous les logiciels. La mention de l'encodage utilisé est également désormais obligatoire (section 3.2 de notre RFC). La langue elle-même est indiquée par une étiquette, selon la syntaxe du RFC 5646. Du fait de ces possibilités plus riches que celles prévues autrefois pour HTTP, les paramètres qui s'en servent doivent se distinguer, ce qui est fait avec un astérisque avant le signe égal (voir l'exemple ci-dessus). Notez que l'usage de l'astérisque n'est qu'une convention : si on trouve un paramètre inconnu dont le nom se termine par un astérisque, on ne peut pas forcément en déduire qu'il est internationalisé.

La valeur du paramètre inclut donc le jeu de caractères et l'encodage (obligatoire), la langue (facultative, elle n'est pas indiquée dans l'exemple ci-dessus) et la valeur proprement dite.

Voici un exemple incluant la langue, ici l'allemand (code de, la phrase est « Mit der Dummheit kämpfen Götter selbst vergebens », ou « contre la bêtise, les dieux eux-mêmes luttent en vain », tirée de la pièce « La pucelle d'Orléans ») :

Quote: theater; 
    sentence*=UTF-8'de'Mit%20der%20Dummheit%20k%C3%A4mpfen%20G%C3%B6tter%20selbst%20vergebens.

La section 4 couvre ensuite les détails pratiques pour les normes qui décrivent un en-tête qui veut utiliser cette possibilité. Par exemple, la section 4.2 traite des erreurs qu'on peut rencontrer en décodant et suggère que, si deux paramètres identiques sont présents, celui dans le nouveau format prenne le dessus. Par exemple, si on a :

Information: something; title="EURO exchange rates";
               title*=utf-8''%e2%82%ac%20exchange%20rates

le titre est à la fois en ASCII pur et en UTF-8, et c'est cette dernière version qu'il faut utiliser, même si normalement il n'y a qu'un seul paramètre title.

Ces paramètres étendus sont mis en œuvre dans Firefox et Opera ainsi que, dans une certaine mesure, dans Internet Explorer.

Plusieurs en-têtes HTTP se réfèrent formellement à cette façon d'encoder les caractères non-ASCII :

  • Authentication-Control:, dans le RFC 8053 (« For example, a parameter "username" with the value "Renee of France" SHOULD be sent as username="Renee of France". If the value is "Renée of France", it SHOULD be sent as username*=UTF-8''Ren%C3%89e%20of%20France instead »),
  • Authorization: (pour l'authentification HTTP, RFC 7616, avec également un paramètre username pour l'ASCII et username* pour l'encodage défini dans ce RFC),
  • Content-Disposition:, RFC 6266, qui indique sous quel nom enregistrer un fichier et dont le paramètre filename* permet tous les caractères Unicode,
  • Link:, normalisé dans le RFC 5988, où le paramètre title* permet des caractères non-ASCII (title étant pour l'ASCII pur).

Les changements depuis le RFC 5987, sont expliqués dans l'annexe A. Le plus spectaculaire est le retrait d'ISO 8859-1 (Latin-1) de la liste des encodages qui doivent être gérés obligatoirement par le logiciel. Cela fera plaisir aux utilisateurs d'Internet Explorer 9, qui avait déjà abandonné Latin-1. Autrement, rien de crucial dans ces changements. Le texte d'introduction a été refait pour mieux expliquer la situation très complexe concernant la légalité (ou pas) des caractères non-ASCII dans les valeurs d'en-tête.

Si vous voulez voir un exemple, essayez de télécharger le fichier http://www.bortzmeyer.org/files/foobar.txt. Si votre client HTTP gère l'en-tête Content-Disposition: et le paramètre internationalisé filename*, le fichier devrait être enregistré sous le nom föbàr.txt.La configuration d'Apache pour envoyer le Content-Disposition: est :


<Files "foobar.txt">
    Header set Content-Disposition "attachment; filename=foobar.txt; filename*=utf-8''f%%C3%%B6b%%C3%%A0r.txt"
</Files>

Par exemple, Safari ou Firefox enregistrent bien ce fichier sous son nom international.

Ah, et puisque ce RFC parle d'internationalisation, on notera que c'est le premier RFC (à part quelques essais ratés au début) à ne pas comporter que des caractères ASCII. En effet, suivant les principes du RFC 7997, il comporte cinq caractères Unicode : dans les exemples (« Extended notation, using the Unicode character U+00A3 ("£", POUND SIGN) » et « Extended notation, using the Unicode characters U+00A3 ("£", POUND SIGN) and U+20AC ("€", EURO SIGN) »), dans l'adresse (« Münster, NW 48155 ») et dans les noms des contributeurs (« Thanks to Martin Dürst and Frank Ellermann »).


Téléchargez le RFC 8187


L'article seul

RFC 8240: Report from the Internet of Things (IoT) Software Update (IoTSU) Workshop 2016

Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : H. Tschofenig (ARM Limited), S. Farrell (Trinity College Dublin)
Pour information
Première rédaction de cet article le 12 septembre 2017


La mode des objets connectés a mené à l'existence de nombreux « objets », qui sont en fait des ordinateurs (avec leurs problèmes, comme le fait que leur logiciel ait des failles de sécurité), mais qui ne sont pas considérés comme des ordinateurs par leurs propriétaires, et ne sont donc pas gérés (pas d'administrateur système, pas de mises à jour des logiciels). L'attaque contre Dyn du 21 octobre 2016, apparemment menée par ces objets, a bien illustré le risque que cette profusion irresponsable crée. Ce nouveau RFC est le compte-rendu d'un atelier sur la mise à jour des objets connectés, atelier de réflexion qui s'est tenu à Dublin les 13 et 14 juin 2016. Il fait le point sur la (difficile) question.

La question de base était « que peut-on faire pour que ces foutus objets soient mis à jour, au moins pour patcher leurs failles de sécurité ? » L'atelier réuni à Trinity College n'a évidemment pas fourni une réponse parfaite, mais a au moins permis de clarifier le problème. Il me semble toutefois, d'après le compte-rendu, qu'il y avait un gros manque : les droits du propriétaire de l'objet. La plupart des solutions discutées tournaient autour de l'idée de mises à jour systématiques et automatiques du logiciel via les serveurs du vendeur, et les éventuelles conséquences néfastes pour le propriétaire (comme l'arrivée de nouvelles fonctions de surveillance, par exemple) ne sont guère mentionnées.

Aujourd'hui, un grand nombre des machines ayant accès, même indirect, à l'Internet, est composé de trucs qualifiés d'objets, qui ont en commun qu'on ne les appelle pas « ordinateurs » (alors que c'est bien cela qu'ils sont). Ces objets sont très divers, allant d'engins qui ont les capacités matérielles d'un ordinateur (une télévision connectée, par exemple) à des petits machins très contraints (CPU et mémoire limités, batterie à la capacité finie, etc). Le risque que font peser ces objets « irresponsables » (pas gérés, pas supervisés) sur l'Internet est connu depuis longtemps. Le RFC cite l'article de Schneier, « The Internet of Things Is Wildly Insecure And Often Unpatchable », qui tirait la sonnette d'alarme en 2014. Il notait que le logiciel de ces objets n'était déjà plus à jour quand l'objet était sorti de sa boite la première fois (les fabricants d'objets connectés aiment bien utiliser des versions antédiluviennes des bibliothèques). Or, des objets peuvent rester branchés et actifs pendant des années, avec leur logiciel dépassé et jamais mis à jour, plein de failles de sécurité que les craqueurs ne vont pas manquer d'exploiter (cf. le logiciel Mirai).

Donc, le problème est connu depuis des années. Mais il n'est pas facile à résoudre :

  • Le mécanisme de mise à jour peut lui-même être une faille de sécurité. Par exemple, un certain nombre d'objets font la mise à jour de leur logiciel en HTTP, sans signature sur le code récupéré, rendant ainsi une attaque de l'homme du milieu triviale.
  • Le RFC n'en parle guère, mais, même si on vérifie le code récupéré, celui-ci peut être malveillant. De nombreuses compagnies (par exemple Sony) ont déjà délibérement distribué du malware à leurs clients, et sans conséquences pénales. Une mise à jour automatique pourrait par exemple installer une porte dérobée, ou bien mettre du code DRM nouveau.
  • Les problèmes opérationnels ne manquent pas. Par exemple, si les objets vérifient le code récupéré via une signature, et qu'ils comparent à une clé publique qu'ils détiennent, que faire si la clé privée correspondante est perdue ou copiée ? (Cf. « Winks Outage Shows Us How Frustrating Smart Homes Could Be. »)
  • Si les objets se connectent automatiquement au serveur pour chercher des mises à jour, cela pose un sérieux problème de vie privée. (Le RFC ne rappelle pas qu'en outre, la plupart de ces objets sont de terribles espions qu'on installe chez soi.)
  • Pour une société à but lucratif, gérer l'infrastructure de mise à jour (les serveurs, la plate-forme de développement, les mises à jour du code, la gestion des éventuels problèmes) a un coût non nul. Il n'y a aucune motivation financière pour le faire, la sécurité ne rapportant rien. (Le RFC s'arrête à cette constatation, ne mentionnant pas la solution évidente : des règles contraignantes obligeant les entreprises à le faire. Le Dieu Marché ne sera certainement pas une solution.)
  • Si l'entreprise originale défaille, qui va pouvoir et devoir assurer ses mises à jour ? (Repensons à l'ex-chouchou des médias, le lapin Nabaztag, et à son abandon par la société qui l'avait vendu.) Si le RFC ne le mentionne pas, il va de soi que le logiciel libre est la solution au problème du « pouvoir » mais pas à celui du « devoir » (dit autrement, le logiciel libre est nécessaire mais pas suffisant).

Le risque n'est donc pas seulement de l'absence de système de mise à jour. Il peut être aussi d'un système de mise à jour bogué, vulnérable, ou non documenté, donc non maintenable.

Le problème de la mise à jour automatique des logiciels est évidemment ancien, et plusieurs systèmes d'exploitation ont des solutions opérationnelles depuis un certain temps (comme pacman ou aptitude). Le RFC se focalise donc sur les objets les plus contraints, ceux limités dans leurs capacités matérielles, et donc dans les logiciels qu'ils peuvent faire tourner.

Après cette introduction, le RFC fait un peu de terminologie, car le choix des mots a suscité une discussion à l'atelier. D'abord, la notion de classe. Les « objets connectés » vont de systèmes qui ont les capacités matérielles et l'alimentation électrique d'un ordinateur de bureau (une télévision connectée, par exemple) et qui peuvent donc utiliser les mêmes techniques, jusqu'à des objets bien plus contraints dans leurs capacités. Pour prendre un exemple chez les systèmes populaires auprès des geeks, un Raspberry Pi fait tourner un système d'exploitation « normal » et se met à jour comme un ordinateur de bureau, un Arduino ne le peut typiquement pas. Il faudra donc sans doute développer des solutions différentes selon la classe. Ou, si on veut classer selon le type de processeur, on peut séparer les objets ayant plus ou moins l'équivalent d'un Cortex-A (comme le Pi) de ceux ayant plutôt les ressources du Cortex-M (comme l'Arduino, mais notez que cette catégorie est elle-même très variée).

Le RFC définit également dans cette section 2 la différence entre mise à jour du logiciel et mise à jour du firmware. La distinction importante est que les engins les plus contraints en ressources n'ont typiquement qu'une mise à jour du firmware, qui change tout, système et applications, alors que les engins de classe « supérieure » ont des mises à jour modulaires (on peut ne mettre à jour qu'une seule application).

Dernier terme à retenir, hitless (« sans impact »). C'est une propriété des mises à jour qui ne gênent pas le fonctionnement normal. Par exemple, s'il faut arrêter l'engin pour une mise à jour, elle ne sera pas hitless. Évidemment, la mise à jour du logiciel d'une voiture ne sera probablement pas hitless et nécessitera donc des précautions particulières.

Maintenant, le gros morceau du RFC, la section 3, qui regroupe notamment les exigences issues de l'atelier. C'est assez dans le désordre, et il y a davantage de questions que de réponses. Pour commencer, que devraient faire les fabricants d'objets connectés (en admettant qu'ils lisent les RFC et aient le sens des responsabilités, deux paris hasardeux) ? Par exemple, le RFC note que les mises à jour globales (on remplace tout) sont dangereuses et recommande que des mises à jour partielles soient possibles, notamment pour limiter le débit utilisé sur le réseau (comme avec bsdiff ou courgette). Idéalement, on devrait pouvoir mettre à jour une seule bibliothèque, mais cela nécessiterait du liage dynamique et de code indépendant de la position, ce qui peut être trop demander pour les plus petits objets. Mais au moins un système d'exploitation plutôt conçu pour les « classe M », les objets les plus contraints (équivalents au Cortex-M), le système Contiki, a bien un lieur dynamique.

Certains dispositifs industriels ont plusieurs processeurs, dont un seul gère la connexion avec le reste du monde. Il peut être préférable d'avoir une architecture où ce processeur s'occupe de la mise à jour du logiciel de ses collègues qui ne sont pas directement connectés.

Un problème plus gênant, car non exclusivement technique, est celui des engins qui ont plusieurs responsables potentiels. Si une machine sert à plusieurs fonctions, on risque d'avoir des cas où il n'est pas évident de savoir quel département de l'entreprise a « le dernier mot » en matière de mise à jour. Pour les ordinateurs, cette question sociale a été réglée depuis longtemps dans la plupart des organisations (par exemple en laissant le service informatique seul maître) mais les objets connectés redistribuent les cartes. Si le service Communication a une caméra connectée, qui peut décider de la mettre à jour (ou pas) ?

Un cas proche est celui où un appareil industriel inclut un ordinateur acheté à un tiers (avec le système d'exploitation). Qui doit faire la mise à jour ? Qui est responsable ? Pensons par exemple au problème d'Android où les mises à jour doivent circuler depuis Google vers le constructeur du téléphone, puis (souvent) vers l'opérateur qui l'a vendu. En pratique, on constate qu'Android est mal mis à jour. Et ce sera pire pour un réfrigérateur connecté puisque sa partie informatique sera fabriquée indépendamment, et avant le frigo complet. Lorsque celui-ci démarrera pour la première fois, son logiciel aura sans doute déjà plusieurs mois de retard.

Et la sécurité ? Imaginons un objet connecté dont le fabricant soit assez incompétent et assez irresponsable pour que les mises à jour se fassent en récupérant du code en HTTP, sans aucune authentification d'aucune sorte. Un tel objet serait très vulnérable à des attaques visant à remplacer le code authentique par un code malveillant. D'où l'exigence de DAO (Data Origin Authentication). Sans DAO, tout serait fichu. Deux solutions évidentes utilisent la cryptographie, authentifier le serveur qui distribue les mises à jour (par exemple avec HTTPS) ou bien authentifier le code téléchargé, via une signature numérique (notez que l'IETF a un format CMS pour cela, décrit dans le RFC 4108, mais qui semble peu utilisé.) Mais signer et vérifier est plus facile à dire qu'à faire ! D'abord, si la cryptographie n'est pas un problème pour les engins « classe A », comme une télévision connectée ou bien une voiture connectée, elle peut être bien coûteuse pour des objets plus limités. La cryptographie symétrique est typiquement moins coûteuse, mais bien moins pratique. Le problème de fond est que la cryptographie symétrique n'authentifie pas un envoyeur mais une classe d'envoyeurs (tous ceux qui ont la clé). Il faudrait donc une clé différente par objet, ce qui semble ingérable. Faut-il lister comme exigence de base d'utiliser la cryptographie asymétrique ?

Son coût en opérations de calcul n'est pas le seul problème. Par exemple, la clé privée qui signe les mises à jour peut être volée, ou bien tout simplement devenir trop faible avec les progrès de la cryptanalyse (certains objets peuvent rester en production pendans dix ans ou plus). Il faut donc un moyen de mettre la clé publique à jour. Comment le faire sans introduire de nouvelles vulnérabilités (imaginez un attaquant qui trouverait le moyen de subvertir ce mécanisme de remplacement de clé, et changerait la clé de signature pour la sienne). Bref, les méthodes « il n'y a qu'à… » du genre « il n'y a qu'à utiliser des signatures cryptographiques » ne sont pas des solutions magiques.

Et si l'objet utilise du code venant de plusieurs sources ? Faut-il plusieurs clés, et des règles compliquées du genre « l'organisation A est autorisée à mettre à jour les composants 1, 3 et 4 du système, l'organisation B peut mettre à jour 2 et 3 » ? (Les systèmes fermés sont certainement mauvais pour l'utilisateur, mais présentent des avantages en matière de sécurité : une seule source. Pensez à tous les débats autour de l'utilisation du magasin libre F-Droid, par exemple l'opinion de Whisper.)

Un problème plus fondamental est celui de la confiance : à qui un objet connecté doit-il faire confiance, et donc quelle clé publique utiliser pour vérifier les mises à jour ? Faire confiance à son propriétaire ? À son fabricant ? Au sous-traitant du fabricant ?

On n'en a pas fini avec les problèmes, loin de là. Les objets sont conçus pour être fabriqués et distribués en grande quantité. Cela va donc être un difficile problème de s'assurer que tous les objets dont on est responsable soient à jour. Imaginez une usine qui a des centaines, voire des milliers d'objets identiques, et qui veut savoir s'ils sont tous à la même version du logiciel.

On a parlé plus haut de confiance. On peut décider de faire confiance au fabricant d'un objet, parce qu'on juge l'organisation en question honnête et compétente. Mais cette évaluation ne s'étend pas forcément à la totalité de ses employés. Comment s'assurer qu'un employé méchant ne va pas compromettre le processus de mise à jour du logiciel, par exemple en se gardant une copie de la clé privée, ou bien en ajoutant aux objets produits une clé publique supplémentaire (il aurait alors une porte dérobée dans chaque objet, ce qui illustre bien le fait que la mise à jour est elle-même source de vulnérabilités).

On a parlé des risques pour la sécurité de l'objet connecté. Les précautions prises sont actuellement proches de zéro. Espérons que cela va s'améliorer. Mais, même en prenant beaucoup de précautions, des piratages se produiront parfois. Ce qui ouvre la question de la récupération : comment récupérer un objet piraté ? Si on a dix grille-pains compromis, va t-il falloir les reflasher tous les dix, manuellement ? (Avant cela, il y a aussi l'intéressante question de savoir comment détecter qu'il y a eu piratage, sachant que la quasi-totalité des organisations ne lisent pas les messages leur signalant des comportements suspects de leurs machines.)

La cryptographie apporte clairement des solutions intéressantes à bien des problèmes de sécurité. Mais elle nécessite en général une horloge bien à l'heure (par exemple pour vérifier la date d'expiration d'un certificat). Avoir une horloge maintenue par une batterie pendant l'arrêt de la machine et pendant les coupures de courant n'est pas trivial :

  • Cela coûte cher (la batterie et l'horloge peuvent coûter bien plus cher que le reste de l'objet),
  • C'est encombrant (certains objets sont vraiment petits),
  • Les batteries sont des équipements dangereux, en raison du risque d'incendie ou d'explosion, et leur présence impose donc de fortes contraintes au processus de fabrication, d'autant plus que les batteries vieillissent vite (pas question d'utiliser de vieux stocks),
  • Les batteries conçues pour la maison peuvent ne pas être adaptées, car, dans un environnement industriel, les conditions sont rudes (la température peut facilement excéder les limites d'une batterie ordinaire),
  • Cela ne dure pas longtemps : la présence d'une batterie peut sérieusement raccourcir la durée de vie d'un objet connecté.

Il n'est donc pas évident qu'il y ait une bonne solution à ce problème. (Le RFC cite Roughtime comme une approche prometteuse.)

Et si on a une bonne solution pour distribuer les mises à jour à des milliers ou des millions d'objets connectés, comment ceux-ci vont-ils être informés de l'existence d'une mise à jour. Push ou pull ? Envoyer les mises à jour depuis le serveur est la solution la plus rapide mais elle peut être délicate si l'objet est derrière un pare-feu qui interdit les connexions entrantes (il faudrait que l'objet contacte le serveur de mise à jour, et reste connecté). Et le cas d'objets qui ne sont pas joignables en permanence complique les choses. De l'autre côté, le cas où l'objet contacte le serveur de temps en temps pour savoir s'il y a une mise à jour pose d'autres problèmes. Par exemple, cela consomme de l'électricité « pour rien ».

Vous trouvez qu'il y a assez de problèmes ? Mais ce n'est pas fini. Imaginez le cas d'une mise à jour qui contienne une bogue, ce qui arrivera inévitablement. Comment revenir en arrière ? Refaire une mise à jour (la bogue risque d'empêcher ce processus) ? L'idéal serait que l'objet stocke deux ou trois versions antérieures de son logiciel localement, pour pouvoir basculer vers ces vieilles versions en cas de problème. Mais cela consomme de l'espace de stockage, qui est très limité pour la plupart des objets connectés.

Le consensus de l'atelier a été que les signatures des logiciels (avec vérification par l'objet), et la possibilité de mises à jour partielles, étaient importants. De même, les participants estimaient essentiels la disponibilité d'une infrastructure de mise à jour, qui puisse fonctionner pour des objets qui n'ont pas un accès complet à l'Internet. Par exemple, il faut qu'une organisation puisse avoir une copie locale du serveur de mise à jour, à la fois pour des raisons de performances, de protection de la vie privée, et pour éviter de donner un accès Internet à ses objets.

En parlant de vie privée (RFC 6973), il faut noter que des objets se connectant à un serveur de mise à jour exposent un certain nombre de choses au serveur, ce qui peut ne pas être souhaitable. Et certains objets sont étroitement associés à un utilisateur (tous les gadgets à la maison), aggravant le problème. Le RFC note qu'au minimum, il ne faut pas envoyer un identificateur unique trop facilement, surtout au dessus d'un lien non chiffré. Mais le RFC note à juste titre qu'il y a un conflit entre la vie privée et le désir des vendeurs de se faire de l'argent avec les données des utilisateurs.

On a surtout parlé jusqu'à présent des risques pour l'objet si le serveur des mises à jour est méchant ou piraté. Certains acteurs du domaine vont ajouter des risques pour le serveur : par exemple, si le logiciel est non-libre, certains peuvent souhaiter authentifier l'objet, pour ne pas distribuer leur précieux logiciel privateur à « n'importe qui ».

On en a déjà parlé mais cela vaut la peine d'y revenir : un gros problème de fond est celui de l'autorisation (section 4 du RFC). Qui peut accepter ou refuser une mise à jour ? Oui, il y a de bonnes raisons de refuser : des mises à jour obligatoires sont une forme de porte dérobée, elles permettent d'ajouter des fonctions qui n'étaient pas là avant et qui peuvent être malveillantes. Le cas avait été cité lors de l'affaire de l'iPhone de San Bernardino (des gens proposaient une mise à jour que le téléphone appliquerait, introduisant une porte dérobée). Et le RFC rappelle le cas d'une mise à jour forcée d'imprimantes HP qui avait supprimé des fonctions utiles mais que HP ne voulait plus (la possibilité d'accepter des cartouches d'encre fournies par les concurrents). Un cas analogue révélé la veille de la publication du RFC est celui de Tesla activant une nouvelle fonction à distance (ici, pour améliorer la voiture, mais il est facile de voir que cela pourrait être en sens inverse). Et il y a bien sûr le cas fameux d'Amazon détruisant des livres à distance.

Prenez un téléphone Android programmé par Google, fabriqué par LG, vendu par Orange à la société Michu qui le confie à son employé M. Dupuis-Morizeau. Qui va décider des mises à jour ?

On peut séparer les mises à jour qui corrigent une faille de sécurité et les autres : seules les premières seraient systématiquement appliquées. (Tous les fournisseurs de logiciel ne font pas cette séparation, qui les oblige à gérer deux lignes de mises à jour.) Ceci dit, en pratique, la distinction n'est pas toujours facile, et la correction d'une faille peut en entrainer d'autres.

D'autre part, certains objets connectés sont utilisés dans un environnement très régulé, où tout, matériel et logiciel, doit subir un processus de validation formel avant d'être déployé (c'est le cas des objets utilisés dans les hôpitaux, par exemple). Il y aura forcément une tension entre « faire la mise à jour en urgence car elle corrige une vulnérabilité » et « ne pas faire la mise à jour avant d'avoir tout revalidé, car cet objet est utilisé pour des fonctions vitales ».

Autre problème rigolo, et qui a de quoi inquiéter, le risque élevé d'une cessation du service de mise à jour logicielle (section 5 du RFC). Les vendeurs de logiciel ne sont pas un service public : ils peuvent décider d'arrêter de fournir un service pour des produits qui ne les intéressent plus (ou qui fait concurrence à un produit plus récent, qui offre une meilleure marge). Ou bien ils peuvent tout simplement faire faillite. Que faire face à cet échec du capitalisme à gérer les produits qu'il met sur le marché ? Le problème est d'autant plus fréquent que des objets connectés peuvent rester en service des années, une éternité pour les directions commerciales. Outre le cas du Nabaztag cité plus haut, le RFC mentionne l'exemple d'Eyefi. Un objet un peu complexe peut incorporer des parties faites par des organisations très différentes, dont certaines seront plus stables que d'autres.

Faut-il envisager un service de reprise de la maintenance, par des organisations spécialisées dans ce ramassage des orphelins ? Si ces organisations sont des entreprises à but lucratif, quel sera leur modèle d'affaires ? (Le RFC ne mentionne pas la possibilité qu'il puisse s'agir d'un service public.) Et est-ce que l'entreprise qui abandonne un produit va accepter ce transfert de responsabilité (aucune loi ne la force à gérer ses déchets logiciels) ?

Pour le logiciel, une grosse différence apparait selon qu'il s'agisse de logiciel libre ou pas. Un projet de logiciel libre peut se casser la figure (par exemple parce que l'unique développeur en a eu marre), mais il peut, techniquement et juridiquement, être repris par quelqu'un d'autre. Ce n'est qu'une possibilité, pas une certitude, et des tas de projets utiles ont été abandonnés car personne ne les a repris.

Néanmoins, si le logiciel libre n'est pas une condition suffisante pour assurer une maintenance sur le long terme, c'est à mon avis une condition nécessaire. Parfois, un logiciel est libéré lorsqu'une société abandonne un produit (ce fut le cas avec Little Printer, qui fut un succès, la maintenance étant repris par des développeurs individuels), parfois la société ne fait rien, et le logiciel est perdu. Bref, un système de séquestre du code, pour pouvoir être transmis en cas de défaillance de l'entreprise originale, serait une bonne chose.

Comme souvent en sécurité, domaine qui ne supporte pas le « ya ka fo kon », certaines exigences sont contradictoires. Par exemple, on veut sécuriser les mises à jour (pour éviter qu'un pirate ne glisse de fausses mises à jour dans le processus), ce qui impose des contrôles, par exemple par des signatures numériques. Mais on veut aussi assurer la maintenance après la fin de la société originelle. Si un prestataire reprend la maintenance du code, mais qu'il n'a pas la clé privée permettant de signer les mises à jour, les objets refuseront celles-ci… Le séquestre obligatoire de ces clés peut être une solution, mais elle introduit d'autres risques (vol des clés chez le notaire).

Une solution possible serait que les appareils connectés soient programmés pour cesser tout service s'ils n'arrivent pas à faire de mise à jour pendant N mois, ou bien si le serveur des mises à jour leur dit que le contrat de support est terminé. De telles bombes temporelles seraient bonnes pour la sécurité, et pour le business des fabricants. Mais elles changeraient le modèle d'achat d'équipement : on ne serait plus propriétaire de sa voiture ou de son grille-pain, mais locataire temporaire. De la vraie obsolescence programmée ! De telles bombes dormantes peuvent aussi interférer avec d'autres fonctions de sécurité. Par exemple, un téléphone doit pouvoir passer des appels d'urgence, même si l'abonnement a expiré. Il serait logique qu'il soit en mesure d'appeler le 112, même si son logiciel n'a pas pu être mis à jour depuis longtemps (cf. RFC 7406 pour une discussion sur un compromis similaire.)

Dans une logique libertarienne, ce RFC parle peu de la possibilité d'obligations légales (comme il y a un contrôle technique pour les voitures, il pourrait y avoir obligation de mesures de sécurité quand on vend des objets connectés). Comme il n'y a aucune chance que des entreprises capitalistes fassent spontanément des efforts pour améliorer la sécurité de tous, si on n'utilise pas de contraintes légales, il reste la possibilité d'incitations, discutée dans la section 6. Certaines sont utopiques (« il est dans l'intérêt de tous d'améliorer la sécurité, donc il faut expliquer aux gentils capitalistes l'intérêt de la sécurité, et ils le feront gratuitement »). Une possibilité est celle d'un abonnement : ayant acheté un objet connecté, on devrait payer régulièrement pour le maintenir à jour. Du point de vue de la sécurité, c'est absurde : il est dans l'intérêt de tous que toutes les machines soient à jour, question sécurité. Avec un abonnement, certains propriétaires choisiront de ne pas payer, et leurs engins seront un danger pour tous. (Comparons avec la santé publiqué : il est dans l'intérêt des riches que les pauvres se soignent, sinon, les maladies contagieuses se répandront et frapperont tout le monde. Les vaccinations gratuites ne sont donc pas de la pure générosité.)

Un des problèmes du soi-disant « Internet des objets » est qu'on ne sait pas grand'chose de ce qui s'y passe. Combien de constructeurs d'objets connectés peuvent dire combien ils ont d'objets encore actifs dans la nature, et quel pourcentage est à jour, question logiciel ? La section 7 du RFC se penche sur les questions de mesures. Un des articles présentés à l'atelier notait que, treize ans après la fin de la distribution d'un objet connecté, on en détectait encore des exemplaires actifs.

Garder trace de ces machines n'est pas trivial, puisque certaines ne seront allumées que de manière très intermittente, et que d'autres seront déployées dans des réseaux fermés. Mais ce serait pourtant utile, par exemple pour savoir combien de temps encore il faut gérer des mises à jour pour tel modèle, savoir quels problèmes ont été rencontrés sur le terrain, savoir quelles machines ont été compromises, etc.

La solution simple serait que l'objet ait un identifiant unique et contacte son fabricant de temps en temps, en donnant des informations sur lui-même. Évidemment, cela soulève de gros problèmes de préservation de la vie privée, si le fabricant de brosses à dents connait vos habitudes d'hygiène (« sa brosse à dents n'a pas été allumée depuis six mois, envoyons-lui des pubs pour un dentiste »). Il existe des techniques statistiques qui permettent de récolter des données sans tout révéler, comme le système RAPPOR ou EPID.

Quelle que soit l'efficacité des mises à jour, il ne fait pas de doute que, parfois, des machines seront piratées. Comment les gérer, à partir de là (section 9) ? Car ce ne sera pas seulement un ou deux objets qu'il faudra reformater avec une copie neuve du système d'exploitation. En cas de faille exploitée, ce seront au moins des centaines ou des milliers d'objets qui auront été piratés par un logiciel comme Mirai. (Notez qu'un travail - qui a échoué - avait été fait à l'IETF sur la gestion des machines terminales et leur sécurité, NEA.)

Rien que signaler à l'utilisateur, sans ouvrir une nouvelle voie d'attaque pour le hameçonnage, n'est pas évident (le RFC 6561 propose une solution, mais peu déployée). Bref, pour l'instant, pas de solution à ce problème. Quand on sait que les organisations à but lucratif ne réagissent jamais quand on leur signale un bot dans leur réseau, on imagine ce que ce sera quand on préviendra M. Michu que son grille-pain est piraté et participe à des DoS (comme celles avec SNMP ou bien celle contre Brian Krebs).

Avant d'arriver à une conclusion très partielle, notre RFC, dans sa section 10, traite quelques points divers :

  • Pas mal de gadgets connectés seront rapidement abandonnés, ne délivrant plus de service utile et n'intéressant plus leur propriétaire, mais dont certains seront toujours connectés et piratables. Qui s'occupera de ces orphelins dangereux ?
  • Que devra faire la machine qui voit qu'elle n'arrive pas à faire ses mises à jour ? Couiner très fort comme le détecteur de fumée dont la batterie s'épuise ? Vous imaginez votre cuisine quand dix appareils hurleront en même temps « Software update failed. Act now! » ? Ou bien les appareils doivent-ils s'éteindre automatiquement et silencieusement ? (Les fabricants adoreront cette solution : pour éliminer les vieilles machines et forcer les clients à acheter les nouvelles, il suffira de stopper le serveur des mises à jour.)
  • La cryptographie post-quantique est pour l'instant inutile (et les mécanismes actuels font des signatures énormes, inadaptées aux objets contraints) mais il est impossible de dire quand elle deviendra indispensable. Cinq ans ? Dix ans ? Jamais ? Le problème est que, les objets connectés restant longtemps sur le terrain, il y a un risque qu'un objet soit livré avec de la cryptographie pré-quantique qui soit cassée par les ordinateurs quantiques pendant sa vie. On sera alors bien embêtés.
  • Enfin, pour ceux qui utilisent des certificats pour vérifier les signatures, il faut noter que peu testent les révocations (RFC 6961 et ses copains). Il sera donc difficile, voire impossible, de rattraper le coup si une clé privée de signature est volée.

La section 11 rassemble les conclusions de l'atelier. On notera qu'il y a peu de consensus.

  • Comme c'est l'IETF, il n'est pas étonnant que les participants soient tombés d'accord sur l'utilité d'une solution normalisée pour la distribution et la signature de logiciels, afin de permettre des mises à jour fiables.
  • Il n'est par contre pas clair de savoir si cette solution doit tout inclure, ou seulement certaines parties du système (la signature, par exemple).
  • Des mécanismes de « transmission du pouvoir » sont nécessaires, par exemple pour traiter le cas où une société fait faillite, et passe la maintenance des objets qu'elle a vendu à une association ou un service public. Passer le mot de passe de root est un exemple (très primitif).

L'annexe B du RFC liste tous les papiers présentés à l'atelier qui sont, évidemment en ligne. Sinon, vous pouvez regarder le site officiel de l'atelier, et un bon résumé de présentation. Je recommande aussi cet article de l'IETF Journal, et un très bon article critique sur les objets connectés.


Téléchargez le RFC 8240


L'article seul

Annonces BGP plus spécifiques : bien ou mal ?

Première rédaction de cet article le 4 septembre 2017


L'article de Geoff Huston « BGP More Specifics: Routing Vandalism or Useful? » aborde une question qui a toujours déclenché de chaudes discussions dans les forums d'opérateurs réseaux : est-ce que les opérateurs qui, au lieu d'annoncer un et un seul préfixe IP très général, annoncent plusieurs sous-préfixes, sont des salauds ou pas ? Est-ce l'équivalent de polluer et de taguer ? Après une étude soignée et pas mal de graphiques, il apporte une réponse originale.

Comme d'habitude, je vais commencer par un petit rappel. Le protocole BGP, normalisé dans le RFC 4271, est le protocole standard de distribution des routes dans l'Internet. Une route est annoncée sous forme d'un préfixe IP, par exemple 2001:db8::/32, et les routeurs avec qui on communique relaieront cette annnonce, jusqu'à ce que tout l'Internet soit au courant. Le préfixe annoncé inclut une longueur (32 bits dans l'exemple ci-dessus). Comme des expressions comme « grand préfixe » sont ambiguës (parle-t-on de la longueur du préfixe, ou bien du nombre d'adresses IP qu'il contient ?), on parle de préfixes plus spécifiques (longueur plus grande, moins d'adresses IP) ou moins spécifiques (longueur plus réduite, davantage d'adresses IP). Ainsi, 2001:db8:42:abcd::/64 est plus spécifique que 2001:db8:42::/48.

Si un opérateur a le préfixe 2001:db8::/32, et qu'il annonce en BGP non seulement ce préfixe mais également 2001:db8:42::/48 et 2001:db8:cafe::/48, on dit qu'il annonce des préfixes plus spécifiques. Comme toute route va devoir être stockée dans tous les routeurs de la DFZ, cet opérateur qui annonce trois routes au lieu d'une seule est vu par certains comme un gougnafier qui abuse du système (et, là, on cite en général la fameuse « Tragedy of the Commons »). Cet opérateur force tous les routeurs de la DFZ à consacrer des ressources (CPU, mémoire) à ses trois préfixes, et cela gratuitement, alors qu'il pourrait se contenter d'un seul préfixe, qui englobe les trois en question.

Le problème est d'autant plus complexe que l'Internet est un réseau décentralisé, sans chef et sans police, et que personne ne peut faire respecter des règles mondiales. Donc, même si on pense que les plus spécifiques sont une mauvaise chose, comment les interdire en pratique ?

En général, les opérateurs estiment que les annonces plus spécifiques sont une mauvaise chose. Néanmoins, pas mal de gens en font. Huston regarde de plus près pourquoi, et quelles sont les conséquences. Les plus spécifiques peuvent être utiles pour leur émetteur, car la transmission de paquets IP est fondée sur le principe du « préfixe le plus spécifique » : si un routeur IP a deux routes pour la destination 2001:db8:cafe::1:443, la première pour le préfixe 2001:db8::/32, et la seconde pour le préfixe 2001:db8:cafe::/48, il utilisera toujours la seconde, la plus spécifique. (Si, par contre, il doit envoyer un paquet à 2001:db8:1337::bad:dcaf, il utilisera la première route, puisque la seconde ne couvre pas cette destination.) L'utilisation de préfixes plus spécifiques peut donc être un moyen de déterminer plus finement le trajet qu'emprunteront les paquets qui viennent vers vous.

Quel est le pourcentage de ces « plus spécifiques », d'ailleurs ? Huston mesure environ 50 % des routes IPv4 et 40 % en IPv6. C'est donc une proportion non négligeable. La question est « peut-on / doit-on en réduire le nombre ? Est-ce possible ? Quels sont les coûts et les bénéfices ? »

Pour répondre à cette question, Huston développe une taxonomie des annonces plus spécifiques, avec des exemples réels. (Les exemples ci-dessous ont été vérifiés mais attention, le looking glass typique n'indique que le préfixe le plus spécifique ; si on n'a pas soi-même une copie de la DFZ et les outils pour l'analyser, il faut utiliser un service qui affiche également les préfixes englobants - je me sers de RIPE Stat et un looking glass - j'utilise celui de Hurricane Electric.) Huston distingue trois cas. Le premier est celui de « percement d'un trou » : l'annonce plus générale n'envoie pas au bon AS et on fait donc une annonce plus spécifique avec un AS d'origine différent. On voit ainsi, pour 72.249.184.0/24, sur RIPE Stat :

Originated by: AS394094 (valid route objects in LEVEL3 and RADB)

Covering less-specific prefixes:
72.249.128.0/18 (announced by AS30496)
72.249.184.0/21 (announced by AS36024)

Showing results for 72.249.184.0/24 as of 2017-09-03 08:00:00 UTC
    

Le /21 est annoncé par l'AS 36024 alors qu'il y a une annonce plus spécifique pour le /24, avec un autre AS, 394094. Huston note que cela pourrait être fait en annonçant un préfixe par /24 mais cela créerait davantage d'entrées dans la table de routage que ce percement de trou avec un plus spécifique.

Le second cas d'annonce plus spécifique est l'ingénierie de trafic. L'AS d'origine est le même, mais l'annonce est différente sur un autre point, par exemple par le prepending (la répétition du même AS dans l'annonce, pour rendre le chemin plus long et donc décourager l'usage de cette route), ou bien en envoyant les deux préfixes, le plus spécifique et le moins spécifique, à des opérateurs différents :

    
Originated by: AS4775 (valid route objects in RADB and NTTCOM)

Covering less-specific prefix: 1.37.0.0/16 (announced by AS4775)

Showing results for 1.37.27.0/24 as of 2017-09-03 08:00:00 UTC

Cette fois, l'AS d'origine est le même. Mais on note sur le looking glass que les annonces sont différentes :

Prefix: 1.37.0.0/16
          AS_PATH: 4766 4775

Prefix: 1.37.27.0/24
         AS_PATH: 4775	  

Le préfixe plus spécifique n'est pas annoncé aux mêmes opérateurs.

Enfin, le troisième et dernier cas est celui du recouvrement complet : l'annonce plus spécifique est apparemment inutile puisque il existe une annonce moins spécifique qui a exactement les mêmes caractéristiques. Pourquoi ferait-on cela ? Il peut s'agir de négligence ou d'incompétence mais il existe aussi une raison rationnelle : limiter les conséquences d'un détournement BGP.

Dans un tel détournement, l'attaquant annonce un préfixe qui n'est pas le sien, pour perturber l'accès à la victime, ou pour détourner son trafic vers l'attaquant (les cas les plus connus sont accidentels, comme le fameux détournement de YouTube par le Pakistan, mais cela peut aussi se faire délibérement). Si l'annonce normale est un /22, et que l'attaquant annonce aussi un /22, une partie de l'Internet verra toujours l'annonce légitime (les routeurs BGP ne propagent, pour un préfixe donné, que la meilleure route) et ne sera donc pas affectée. Pour qu'une attaque réussisse bien, dans ces conditions, il faut que l'attaquant soit très bien connecté, avec de nombreux partenaires BGP. Par contre, avec cette annonce légitime en /22, un attaquant qui enverrait une annonce pour un /24 verrait son annonce propagée partout (puisqu'il s'agirait d'un préfixe différent). Et, au moment de la transmission des paquets, les routeurs utiliseront la route la plus spécifique, donc le /24. Ainsi, un attaquant mal connecté pourra toujours voir son annonce acceptée et utilisée dans tout l'Internet.

Le fait d'annoncer un recouvrement complet (quatre /24 en plus du /22) protège donc partiellement contre cette technique. Huston rejette cet argument en disant qu'il ne s'agit que d'une protection partielle, mais je ne suis pas d'accord : une protection partielle vaut mieux que pas de protection du tout et, en sécurité, il est courant que les solutions soient partielles.

Un exemple est 1.0.4.0/22. On voit les quatre préfixes plus spécifiques, avec exactement le meme contenu :

Prefix: 1.0.4.0/22
          AS_PATH: 4826 38803 56203

Prefix: 1.0.4.0/24
         AS_PATH: 4826 38803 56203
Prefix: 1.0.5.0/24
         AS_PATH: 4826 38803 56203
Prefix: 1.0.6.0/24
         AS_PATH: 4826 38803 56203
Prefix: 1.0.7.0/24
         AS_PATH: 4826 38803 56203
	 

Les annonces plus spécifiques forment donc la moitié (en IPv4) des annonces dans la DFZ. Mais ce pourcentage augmente-t-il ou diminue-t-il ? Est-ce que la situation s'aggrave ? Huston utilise les données BGP accumulées depuis dix ans (si vous voulez faire pareil, les annonces BGP sont archivées et disponibles, entre autres à RouteViews). Eh bien, il n'y a pas de changement en IPv4 : le pourcentage de 50 % des annonces étant des annonces plus spécifiques qu'une annonce existante n'a pas changé depuis dix ans (mais la proportion des trois cas a changé : lisez l'article). En revanche, il est passé de 20 à 40 % en IPv6 dans le même temps, mais je ne suis pas sûr qu'on puisse en tirer des conclusions solides : il y a dix ans, IPv6 était peu présent dans la DFZ.

Ça, c'était le pourcentage des préfixes. Le nombre de préfixes à gérer a un effet négatif sur le routeur, car davantage de préfixes veut dire davantage de mémoire consommée par le routeur. La DFZ a actuellement plus de 700 000 préfixes (IPv4 et IPv6 mêlés). Une table de seulement 700 000 entrées ? Cela peut sembler peu à l'ère de la vidéo de chats en haute définition et du big data. Certes, un routeur du cœur de l'Internet n'a pas la même architecture qu'un PC de bureau, et la mémoire n'y est pas disponible dans les mêmes quantités mais, quand même, est-ce si grave ?

En fait, le plus gênant pour le routeur typique n'est pas la quantité de préfixes mais le rythme de changement (le churn). Davantage de préfixes veut surtout dire davantage de changements à traiter. Huston regarde donc dans la suite de son article l'effet des changements. Il constate que les préfixes plus spécifiques sont un peu plus « bruyants » (davantage de changements), au moins en IPv4. Les préfixes plus spécifiques du deuxième cas (ingénierie de trafic) sont les plus bruyants, ce qui est assez logique. Attention avec ces statistiques, toutefois : Huston rappelle que le rythme de changement varie énormément selon les préfixes. Certains changent tout le temps, d'autres sont très stables.

Maintenant, venons-en aux actions possibles. Est-ce que ces préfixes plus spécifiques, émis par des opérateurs égoïstes et payés par tous, doivent être activement combattus, pour en réduire le nombre ? C'est l'idée qui est derrière des actions comme le CIDR report. En analysant automatiquement les préfixes de la DFZ, et en publiant la liste des opérateurs qui abusent le plus, on espère leur faire honte (cette technique du name and shame est très courante sur Internet, puisqu'il n'existe pas d'État central qui puisse faire respecter des règles et que les opérateurs n'ont, heureusement, pas de police ou d'armée privée), et diminuer avec le temps le pourcentage de préfixes « inutiles ». Par exemple, Huston calcule que la suppression de tous les plus spécifiques du cas 3 (recouvrement complet) diminuerait la table de routage globale de 20 % et le rythme des changements de 10 à 15 %.

En conclusion, Huston estime qu'on ne peut pas parler de tous les préfixes moins spécifiques de la même façon, certains sont réellement utiles. Certes, d'autres ne le sont pas, mais les supprimer serait une tâche longue et difficile, pour un bénéfice limité. Huston résume en disant que les préfixes plus spécifiques sont un problème agaçant, mais pas grave.


L'article seul

Observations about the attack on WikiLeaks

First publication of this article on 1 September 2017


On 30 August, this year, a technical attack was performed against WikiLeaks, leading some visitors of WikiLeaks' Web site to see instead a claim by "OurMine" that they seized control of WikiLeaks' servers. A lot of stupid things, by ignorant people (both WikiLeaks fans and enemies), have been said on the networks, about this attack. Most of the time, they did not bother to check any facts, and they did not demonstrate any knowledge of the technical infrastructure. Here, I want to describe the bare facts, as seen from technical observations. Spoiler: I have no sensational revelations to make.

First, the basic fact: some people saw something which was obviously not WikiLeaks' Web site: screenshots of the page are here or here. Some people deduced from that that WikiLeaks' Web server was cracked and the crackers modified its content (you can find this in The Verge for instance). That was a bold deduction: the complete process from the user's browser to the display of the Web page is quite complicated, with a lot of actors, and many things can go wrong.

In the case of WikiLeaks, it appeared rapidly that the Web server was not cracked but that the attack targeted successfully the wikileaks.org domain name. Observations (more on that later) show that the name wikileaks.org was not resolved into the usual IP address but in another one, located in a different hoster. How is it possible? What are the consequences?

You should remember that investigation of digital incidents on the Internet is difficult. The external analyst does not have all the data. Sometimes, when the analysis starts, it is too late, the data already changed. And the internal analyst almost never publishes everything, and sometimes even lies. There are some organisations that are more open in their communication (see this Cloudflare report or this Gandi report) but they are the exceptions rather than the rule. Here, WikiLeaks reacted like the typical corporation, denying the problem, then trying to downplay it, and not publishing anything of value for the users. So, most of the claims that you can read about network incidents are not backed by facts, specially not publicly-verifiable facts. The problem is obviously worse in that case, because WikiLeaks is at the center of many hot controversies. For instance, some WikiLeaks fans claimed from the beginning "WikiLeaks' servers have not been compromised" while they had zero actual information, and, anyway, not enough time to analyze it.

So, the issue was with the domain name wikileaks.org. To explain what happened, we need to go back to the DNS, both a critical infrastructure of the Internet, and a widely unknown (or underknown) technology. The DNS is a database indexed by domain names (like wikileaks.org or ssi.gouv.fr). When you query the DNS for a given domain name, you get various technical informations such as IP addresses of servers, cryptographic keys, name of servers, etc. When the typical Web browser goes to http://www.okstate.com/, the software on the user's machine performs a DNS query for the name www.okstate.com, and gets back the IP address of the HTTP server. It then connects to the server.

From this very short description, you can see that s·he who controls the DNS controls where the user will eventually go and what s·he will see. And the entire DNS resolution process (from a name to the data) is itself quite complicated, offering many opportunities for an attacker. Summary so far: DNS is critical, and most organisations underestimate it (or, worse, claim it is not their responsability).

And where do the data in the DNS come from? That's the biggest source of vulnerabilities: unlike what many people said, most so-called "DNS attacks" are not DNS attacks at all, meaning they don't exploit a weakness in the DNS protocol. Most of the time, they are attacks against the provisioning infrastructure, the set of actors and servers that domain name holders (such as WikiLeaks for wikileaks.org) use to provision the data. Let's say you are Pat Smith, responsible for the online activity of an organisation named the Foobar Society. You have the domain name foobar.example. The Web site is hosted at Wonderful Hosting, Inc. After you've choosen a TLD (and I recommend you read the excellent EFF survey before you do so), you'll typically need to choose a registrar which will act as a proxy between you and the actual registry of the TLD (here, the fictitious .example). Most of the time, you, Pat Smith, will connect to the Web site of the registrar, create an account, and configure the data which will ultimately appear in the DNS. For instance, when the Web site is created at Wonderful Hosting, Pat will enter its IP address in the control panel provided by the registrar. You can see immediately that this required Pat to log in the said control panel. If Pat used a weak password, or wrote it down under h·is·er desk or if Pat is gullible and believes a phone call asking h·im·er to give the password, the account may be compromised, and the attacker may log in instead of Pat and put the IP address of h·er·is choosing. This kind of attacks is very common, and illustrate the fact that not all attacks are technically complicated.

So, what happened in the WikiLeaks case? (Warning, it will now become more technical.) We'll first use a "passive DNS" base, DNSDB. This sort of databases observes the DNS traffic (which is most of the time in clear, see RFC 7626) and record it, allowing its users to time-travel. DNSDB is not public, I'm sorry, so for this one, you'll have to trust me. (That's why real-time reaction is important: when you arrive too late, the only tools to observe an attack are specialized tools like this one.) What's in DNSDB?

      
;;  bailiwick: org.
;;      count: 9
;; first seen: 2017-08-30 04:28:40 -0000
;;  last seen: 2017-08-30 04:30:28 -0000
wikileaks.org. IN NS ns1.rivalhost.com.
wikileaks.org. IN NS ns2.rivalhost.com.
wikileaks.org. IN NS ns3.rivalhost.com.

;;  bailiwick: org.
;;      count: 474
;; first seen: 2017-08-30 04:20:15 -0000
;;  last seen: 2017-08-30 04:28:41 -0000
wikileaks.org. IN NS ns1.rival-dns.com.
wikileaks.org. IN NS ns2.rival-dns.com.
wikileaks.org. IN NS ns3.rival-dns.com.
    

What does it mean? That during the attack (around 04:30 UTC), the .org registry was replying with the illegitimate set of servers. The usual servers are (we use the dig tool, the best tool to debug DNS issues):


% dig @a0.org.afilias-nst.info. NS wikileaks.org
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21194
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 3
...
;; AUTHORITY SECTION:
wikileaks.org.		86400 IN NS ns1.wikileaks.org.
wikileaks.org.		86400 IN NS ns2.wikileaks.org.

;; ADDITIONAL SECTION:
ns1.wikileaks.org.	86400 IN A 46.28.206.81
ns2.wikileaks.org.	86400 IN A 46.28.206.82
...
;; SERVER: 2001:500:e::1#53(2001:500:e::1)
;; WHEN: Fri Sep 01 09:18:14 CEST 2017
...

    

(And, yes, there is a discrepancy between what is served by the registry and what's inside nsX.wikileaks.org name servers: whoever manages WikiLeaks DNS does a sloppy job. That's why it is often useful to query the parent's name servers, like I did here.)

So, the name servers were changed, for rogue ones. Note there was also a discrepancy during the attack. These rogue servers gave a different set of NS (Name Servers), according to DNSDB:

;;  bailiwick: wikileaks.org.
;;      count: 1
;; first seen: 2017-08-31 02:02:38 -0000
;;  last seen: 2017-08-31 02:02:38 -0000
wikileaks.org. IN NS ns1.rivalhost-global-dns.com.
wikileaks.org. IN NS ns2.rivalhost-global-dns.com.     

Note that it does not mean that the DNS hoster of the attacker, Rival, is an accomplice. They may simply have a rogue customer. Any big service provider will have some rotten apples among its clients.

You can see the date of the last change in whois output, when everything was put back in place:

% whois wikileaks.org
...
Updated Date: 2017-08-31T15:01:04Z
    

Surely enough, the rogue name servers were serving IP addresses pointing to the "false" Web site. Again, in DNSDB:

    
;;  bailiwick: wikileaks.org.
;;      count: 44
;; first seen: 2017-08-30 04:29:07 -0000
;;  last seen: 2017-08-31 07:22:05 -0000
wikileaks.org. IN A 181.215.237.148

The normal IP addresses of WikiLeaks are in the prefixes 95.211.113.XXX, 141.105.XXX and 195.35.109.XXX (dig A wikileaks.org if you want to see them, or use a DNS Looking Glass). 181.215.237.148 is the address of the rogue Web site, hosted by Rival again, as can be seen with the whois tool:

% whois 181.215.237.148
inetnum:     181.215.236/23
status:      reallocated
owner:       Digital Energy Technologies Chile SpA
ownerid:     US-DETC5-LACNIC
responsible: RivalHost, LLC.
address:     Waterwood Parkway, 1015, Suite G, C-1
address:     73034 - Edmond - OK
country:     US
owner-c:     VIG28
tech-c:      VIG28
abuse-c:     VIG28
...
nic-hdl:     VIG28
person:      AS61440 Network Operating Center
e-mail:      noc@AS61440.NET
address:     Moneda, 970, Piso 5
address:     8320313 - Santiago - RM
country:     CL
  

(It also shows that this prefix was allocated in Chile, the world is a complicated place, and the Internet even more so.)

So, this was the modus operandi of the cracker. S·he managed to change the set of name servers serving wikileaks.org, and that gave h·im·er the ability to send visitors to a server s·he controlled. (Note that this HTTP server, 181.215.237.148, no longer serves the cracker's page: it was probably removed by the provider.)

Many people on the social networks claimed that the attack was done by "DNS poisoning". First, a word of warning by a DNS professional: when someone types "DNS poisoning", you can be pretty sure s·he knows next to nothing about DNS. DNS poisoning is a very specific attack, for which we have solutions (DNSSEC, mentioned later), but it does not seem to be very common (read again my warning at the beginning: most attacks are never properly analyzed and documented, so it is hard to be more precise). What is very common are attacks against the domain name provisioning system. This is, for instance, what happened to the New York Times in 2013, from an attack by the infamous SEA (see NYT paper and a technical analysis). More recently, there was the attack against St. Louis Federal Reserve and many others. These attacks don't use the DNS protocol and it is quite a stretch to label them as "DNS attacks" or, even worse, "DNS poisoning".

What are the consequences of such an attack? As explained earlier, once you control the DNS, you control everything. You can redirect users to a Web site (not only the external visitors, but also the employees of the targeted organisation, when they connect to internal services, potentially stealing passwords and other informations), hijack the emails, etc. So, claiming that "the servers were not compromised" (technically true) is almost useless. With an attack on domain names, the cracker does not need to compromise the servers.

Who was cracked in the WikiLeaks case? From the outside, we can say with confidence that the name servers were changed. The weakness could have been at the holder (WikiLeaks), at the registrar (Dynadot, an information you also get with whois), or at the registry (.org, administratively managed by PIR and technically by Afilias). From the information available, one cannot say where the problem was (so, people who publicly shouted that "WikiLeaks is not responsible" were showing their blind faith, not their analytic abilities). Of course, most of the times, the weakest link is the user (weak password to the registrar portal, and not activating 2FA), but some registrars or registries displayed in the past serious security weaknesses. The only thing we can say is that no other domain name appeared to have been hijacked. (When someone takes control of a registrar or registry, s·he can change many domain names.)

I said before that, when you control a domain name, you can send both external and internal visitors to the server you want. That was not entirely true, since good security relies on defence in depth and some measures can be taken to limit the risk, even if your domain name is compromised. One of them is of course having HTTPS (it is the case of WikiLeaks), with redirection from the plain HTTP site, and HSTS (standardized in RFC 6797), to avoid that regular visitors go through the insecure HTTP. Again, WikiLeaks use it:

%  wget --server-response --output-document /dev/null https://wikileaks.org/
...
Strict-Transport-Security: max-age=25920000; includeSubDomains; preload
   

These techniques will at least raise an alarm, telling the visitor that something is fishy. (There is also HPKPRFC 7649 - but it does not seem deployed by Wikileaks; it should be noticed it is more risky.)

In the same way, using Tor to go to a .onion URL would also help. But I have not been able to find a .onion for WikiLeaks (the http://suw74isz7wqzpmgu.onion/ indicated on the wiki does not work, the http://wlupld3ptjvsgwqw.onion seems to be just for uploading).

One can also limit the risk coming from an account compromise by enabling registry lock, a technique offered by most TLD (including .org) to prevent unauthorized changes. When activated, it requires extra steps and checking for any change. I cannot say, from the outside, if WikiLeaks enabled it but sensitive domain names must do it.

Funny enough, with so many people claiming it was "DNS poisoning", the best protection against this specific attack, DNSSEC, is not enabled by WikiLeaks (there is a DNSSEC key in wikileaks.org but no signatures and no DS record in the parent). If wikileaks.org was signed, and if you use a validating DNS resolver (everybody should), you cannot fall for a DNS poisoning attack against WikiLeaks. Of course, if the attack is, instead, a compromise of holder account, registrar or registry, DNSSEC would not help a lot.

A bit of technical fun at the end. WikiLeaks uses glue records for its name servers. They are nameserver names which are under the domain they serve, thus creating a chicken-and-egg problem. To allow the DNS client to query them, the parent has to know the IP address of this name server. This is what is called a glue record. DNSDB shows us that the glue for ns1.wikileaks.org was apparently modified (note that it was several hours after the main attack):

;;  bailiwick: org.
;;      count: 546
;; first seen: 2017-08-31 00:23:13 -0000
;;  last seen: 2017-08-31 06:22:42 -0000
ns1.wikileaks.org. IN A 191.101.26.67
    

This machine is still up and serves a funny value for wikileaks.org (again, you can use a DNS Looking Glass):


% dig @191.101.26.67 A wikileaks.org
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 53887
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
...
;; ANSWER SECTION:
wikileaks.org.		400 IN A 127.0.0.1

    

This IP address, meaning localhost, was indeed seen by some DNSDB sensors:

;;  bailiwick: wikileaks.org.
;;      count: 1
;; first seen: 2017-08-31 09:17:29 -0000
;;  last seen: 2017-08-31 09:17:29 -0000
wikileaks.org. IN A 127.0.0.1	
      

Since the DNS heavily relies on caching, the information was still seen even after the configuration was fixed. Here, we use the RIPE Atlas probes with the atlas-resolve tool to see how many probes still saw the wrong value (pay attention to the date and time, all in UTC, which is the rule when analyzing Internet problems):

% atlas-resolve -r 1000 -t A wikileaks.org
[141.105.65.113 141.105.69.239 195.35.109.44 195.35.109.53 95.211.113.131 95.211.113.154] : 850 occurrences 
[195.175.254.2] : 2 occurrences 
[127.0.0.1] : 126 occurrences 
Test #9261634 done at 2017-08-31T10:03:24Z
    

L'article seul

RFC 8216: HTTP Live Streaming

Date de publication du RFC : Août 2017
Auteur(s) du RFC : R. Pantos (Apple), W. May (Major League Baseball Advanced Media)
Pour information
Première rédaction de cet article le 1 septembre 2017


Ce RFC décrit la version 7 du protocole HTTP Live Streaming, qui permet de distribuer des flux multimédia, typiquement de la vidéo.

Utiliser HTTP, c'est bien, ça passe quasiment tous les pare-feux, et ça permet de réutiliser l'infrastructure existante, comme les serveurs, et les caches. (Au contraire, un protocole comme RTP, RFC 3550, a souvent du mal à passer les différentes middleboxes.) La manière triviale de distribuer une vidéo avec HTTP est d'envoyer un fichier vidéo au bon format, avec des méthodes HTTP standard, typiquement en réponse à un GET. Mais cette méthode a des tas de limites. Par exemple, elle ne permet pas de s'adapter à une capacité variable du réseau. Par contre, le protocole décrit dans ce RFC, quoique plus compliqué qu'un simple GET, permet cet ajustement (et plein d'autres).

HTTP Live Streaming (ou simplement Live Streaming, ou encore HLS) existe depuis longtemps, la première spécification a été publiée (par Apple) en 2009, mais c'est la première fois qu'il se retrouve dans un RFC, ce qui devrait permettre d'améliorer encore l'interopérabilité.

Donc, brève description de HTTP Live Streaming : une ressource multimédia qu'on veut diffuser est représentée par un URI. Ceui-ci désigne une playlist, qui est un fichier texte contenant les URI des données multimedia et diverses informations. Les données multimédia figurent dans plusieurs segments ; en jouant tous les segments successivement, on obtient la vidéo souhaitée. Le client n'a pas ainsi à tout charger, il peut récupérer les segments au fur et à mesure de ses besoins. (Au lieu de « segments », mmu_man suggère de parler d'un « émincé de fichiers ».) Voici l'exemple de playlist que donne le RFC :

#EXTM3U
#EXT-X-TARGETDURATION:10

#EXTINF:9.009,
http://media.example.com/first.ts
#EXTINF:9.009,
http://media.example.com/second.ts
#EXTINF:3.003,
http://media.example.com/third.ts
    

Ce format de playlist est dérivé du format M3U et est généralement nommé « M3U étendu ». On y trouve successivement l'identificateur du format (si ça commence par #EXTM3U, c'est bien une playlist de notre RFC), la durée maximale d'un segment en secondes, puis les trois segments, avec la durée de chacun. Cette durée, indiquée par l'étiquette #EXTINF, est un des rares éléments obligatoires dans une playlist. Il y a bien sûr plein d'autres choses qu'on peut mettre dans un fichier playlist, la section 4 du RFC en donne la liste complète. Ainsi, #EXT-X-BYTERANGE permettra d'indiquer qu'on ne veut jouer qu'une partie d'une vidéo. Des exemples plus complets sont donnés dans la section 8 du RFC.

Ça, c'était la media playlist, qui se limite à lister des ressources multimédias. La master playlist, dont le format est le même, est plus riche, et permet de spécifier des versions alternatives d'un même contenu. Ces variantes peuvent être purement techniques (« variant stream », deux encodages d'une même vidéo avec différents formats) ou porter sur le fond : par exemple deux pistes audio dans des langues différentes, ou deux vidéos d'un même évènement filmé depuis deux points de vue distincts. Les variantes techniques servent au client à s'ajuster aux conditions réelles du réseau, les variantes de fond (nommées renditions) servent au client à donner à l'utilisateur ce qu'il préfère. Ici, par exemple, on a dans cette master playlist trois variantes techniques (trois résolutions, de la plus basse, à la plus élevée, qui va nécessiter une bonne capacité réseau), et trois « renditions », correspondant à trois points de vue (centerfield et dugout sont des termes de baseball, ce qui est logique vu l'employeur d'un des deux auteurs du RFC) :

#EXTM3U
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Main", \
   DEFAULT=YES,URI="low/main/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Centerfield", \
   DEFAULT=NO,URI="low/centerfield/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Dugout", \
   DEFAULT=NO,URI="low/dugout/audio-video.m3u8"

#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS="...",VIDEO="low"
   low/main/audio-video.m3u8

#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Main", \
   DEFAULT=YES,URI="mid/main/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Centerfield", \
   DEFAULT=NO,URI="mid/centerfield/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Dugout", \
   DEFAULT=NO,URI="mid/dugout/audio-video.m3u8"

#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS="...",VIDEO="mid"
   mid/main/audio-video.m3u8

#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Main", \
   DEFAULT=YES,URI="hi/main/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Centerfield", \
   DEFAULT=NO,URI="hi/centerfield/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Dugout", \
   DEFAULT=NO,URI="hi/dugout/audio-video.m3u8"

#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS="...",VIDEO="hi"
   hi/main/audio-video.m3u8
    

Si vous voulez le voir en vrai, et que vous êtes abonné à Free, la Freebox distribue sa playlist sous ce format (mais avec le mauvais type MIME) :


% curl -s http://mafreebox.freebox.fr/freeboxtv/playlist.m3u
#EXTM3U
#EXTINF:0,2 - France 2 (bas débit)
rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=201&flavour=ld
#EXTINF:0,2 - France 2 (HD)
rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=201&flavour=hd
#EXTINF:0,2 - France 2 (standard)
rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=201&flavour=sd
#EXTINF:0,2 - France 2 (auto)
rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=201
#EXTINF:0,3 - France 3 (standard)
rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=202&flavour=sd
...

    

(On me dit qu'avec certaines Freebox, il faut ajouter -L à curl, en raison d'une redirection HTTP.) Un client comme vlc peut prendre comme argument l'URI de la playlist, la télécharger et jouer ensuite les vidéos.

(Tous les moyens sont bons pour télécharger la playlist, le RFC n'impose pas un mécanisme particulier.) Télécharger chacun des segments se fait a priori avec HTTP, RFC 7230, mais vous voyez plus haut que, sur la Freebox, c'est RTSP, RFC 7826. Le serveur, lui, est un serveur HTTP ordinaire. L'auteur a « juste » eu à découper sa vidéo en segments (ou la garder en un seul morceau si elle est assez courte), à fabriquer un fichier playlist, puis à le servir avec le type MIME application/vnd.apple.mpegurl. (La section 6 du RFC décrit plus en détail ce que doivent faire client et serveur.)

Il est recommandé que chaque segment soit auto-suffisant, c'est-à-dire puisse être décodé sans avoir besoin des autres segments. Ainsi, si les segments sont en H.264, chacun doit avoir un Instantaneous Decoding Refresh (IDR).

Plusieurs formats sont évidemment possibles dans une playlist HTTP Live Streaming, MPEG-2, MPEG-4 fragmenté, WebVTT pour avoir les sous-titres, et un format pour l'audio (qui accepte entre autres MP3).

HTTP Live Streaming permet aussi de servir du direct. Prenons par exemple le flux vidéo de l'Assemblée Nationale, qui permet de suivre les débats parlementaires :

    
% curl -s http://videos-diffusion-live.assemblee-nationale.fr/live/live1/stream1.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:NO
#EXT-X-TARGETDURATION:11
#EXT-X-MEDIA-SEQUENCE:382
#EXTINF:9.996,
media_1_4805021_382.ts
#EXTINF:11.483,
media_1_4805021_383.ts
...

Un peu plus tard, les fichiers ont changé, le client HLS doit recharger la playlist régulièrement (section 6.2.2 du RFC), et charger de nouveaux fichiers vidéo :

% curl -s http://videos-diffusion-live.assemblee-nationale.fr/live/live1/stream1.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:NO
#EXT-X-TARGETDURATION:11
#EXT-X-MEDIA-SEQUENCE:390
#EXTINF:10.039,
media_1_4805021_390.ts
#EXTINF:11.242,
media_1_4805021_391.ts
...

(Notez également l'incrémentation du #EXT-X-MEDIA-SEQUENCE.) Ainsi, l'utilisateur a l'impression d'une vidéo continue, alors que le logiciel client passe son temps à charger des segments différents et à les afficher à la suite.

Notez que HTTP Live Streaming n'est pas le seul mécanisme dans ce domaine, son principal concurrent est MPEG-DASH.

Si vous voulez lire davantage :


Téléchargez le RFC 8216


L'article seul

RFC 6844: DNS Certification Authority Authorization (CAA) Resource Record

Date de publication du RFC : Janvier 2013
Auteur(s) du RFC : P. Hallam-Baker (Comodo), R. Stradling (Comodo)
Chemin des normes
Première rédaction de cet article le 31 août 2017


Ce RFC a quelques années mais n'avait connu que peu de déploiement effectif. Cela va peut-être changer à partir du 8 septembre 2017, où les AC ont promis de commencer à vérifier les enregistrements CAA (Certification Authority Authorization) publiés dans le DNS. Ça sert à quoi ? En gros, un enregistrement CAA publié indique quelles AC sont autorisées à émettre des certificats pour ce domaine. Le but est de corriger très partiellement une des plus grosses faiblesses de X.509, le fait que n'importe quelle AC peut émettre des certificats pour n'importe quel domaine, même si ce n'est pas un de ses clients.

C'est donc une technique très différente de DANE (RFC 6698), les seuls points communs étant l'utilisation du DNS pour sécuriser les certificats. DANE est déployé chez le client TLS, pour qu'il vérifie le certificat utilisé, CAA est surtout dans l'AC, pour limiter le risque d'émission d'un certificat malveillant (par exemple, CAA aurait peut-être empêché le faux certificat Gmail du ministère des finances.) Disons que CAA est un contrôle supplémentaire, parmi ceux que l'AC doit (devrait) faire. Les clients TLS ne sont pas censés le tester (ne serait-ce que parce que l'enregistrement CAA a pu changer depuis l'émission du certificat, la durée de vie de ceux-ci étant en général de plusieurs mois). CAA peut aussi servir à des auditeurs qui veulent vérifier les pratiques d'une AC (même avertissement : le certificat a pu être émis alors que l'enregistrement CAA était différent.)

La section 3 de notre RFC présente l'enregistrement CAA (la syntaxe détaillée est en section 5). Il a été ajouté au registre des types d'enregistrements sous le numéro 257. Il comprend une série d'options (flags) et une propriété qui est sous la forme {clé, valeur}. Un nom peut avoir plusieurs propriétés. Pour l'instant, une seule option est définie (un registre existe pour les options futures), « issuer critical » qui indique que cette propriété est cruciale : si on ne la comprend pas, le test doit être considéré comme ayant échoué et l'AC ne doit pas produire de certificat.

Les propriétés possibles sont, à l'heure actuelle (d'autres pourront être ajoutée dans le registre IANA) :

  • issue, la principale, qui indique une AC autorisée à émettre des certificats pour ce domaine (l'AC est indiquée par son nom de domaine),
  • issuewild, idem, mais avec en plus la possibilité pour l'AC d'émettre des certificats incluants des jokers,
  • iodef, qui indique où l'AC doit envoyer d'éventuels rapports d'échec, pour que le titulaire du nom de domaine puisse les corriger. Un URL est indiqué pour cela, et le rapport doit être au format IODEF (RFC 7970).

Voici par exemple l'enregistrement CAA de mon domaine personnel :


% dig CAA bortzmeyer.org
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61450
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 3, AUTHORITY: 7, ADDITIONAL: 7
...
;; ANSWER SECTION:
bortzmeyer.org.		26786 IN CAA 0 issue "cacert.org"
bortzmeyer.org.		26786 IN CAA 0 issuewild "\;"
...

  

Il indique que seule l'AC CAcert peut créer un certificat pour ce domaine (et sans les jokers). Bon, c'est un peu inutile car CAcert ne teste pas les enregistrements CAA, mais c'est juste pour jouer. Je n'ai pas mis d'iodef mais il aurait pu être :

bortzmeyer.org. CAA 0 iodef "mailto:security@bortzmeyer.org"
  

Et, dans ce cas, l'AC peut écrire à security@bortzmeyer.org, avec le rapport IODEF en pièce jointe.

Des paramètres peuvent être ajoutés, le RFC cite l'exemple d'un numéro de client :

example.com.   CAA 0 issue "ca.example.net; account=230123" 
  

Une fois les enregistrements CAA publiés, comment sont-ils utilisés (section 4) ? L'AC est censée interroger le DNS pour voir s'il y a un CAA (on note que DNSSEC est recommandé, mais n'est pas obligatoire, ce qui réduit le service déjà faible qu'offre CAA). S'il n'y en a pas, l'AC continue avec ses procédures habituelles. S'il y a un CAA, deux cas : il indique que cette AC peut émettre un certificat pour le domaine, et dans ce cas-là, c'est bon, on continue avec les procédures habituelles. Second cas, le CAA ne nomme pas cette AC et elle doit donc renoncer à faire un certificat sauf s'il y a une exception configurée pour ce domaine (c'est la deuxième faille de CAA : une AC peut facilement passer outre et donc continuer émettre de « vrais/faux certificats »).

Notez que le RFC ne semble pas évoquer la possibilité d'imposer la présence d'un enregistrement CAA. C'est logique, vu le peu de déploiement de cette technique mais cela veut dire que « qui ne dit mot consent ». Pour la plupart des domaines, la vérification du CAA par l'AC ne changera rien.

Notez que, si aucun enregistrement CAA n'est trouvé, l'AC est censé remonter l'arbre du DNS. (C'est pour cela que SSL [sic] Labs trouve un enregistrement CAA pour mercredifiction.bortzmeyer.org : il a utilisé le CAA du domaine parent, bortzmeyer.org.) Si example.com n'a pas de CAA, l'AC va tester .com, demandant ainsi à Verisign si son client peut avoir un certificat et de qui. Cette erreur consistant à grimper sur l'arbre avait déjà été dénoncée dans le RFC 1535, mais apparemment la leçon n'a pas été retenue.

Enfin, la section 6 du RFC analyse les différents problèmes de sécurité que peut poser CAA :

  • Le respect de l'enregistrement CAA dépend de la bonne volonté de l'AC (et CAA ne remplace donc pas DANE),
  • Sans DNSSEC, le test CAA est vulnérable à des attaques par exemple par empoisonnement (le RFC conseille de ne pas passer par un résolveur normal mais d'aller demander directement aux serveurs faisant autorité, ce qui ne résoud pas le problème : le programme de test peut se faire empoisonner, lui aussi, d'autant plus qu'il prend sans doute moins de précautions qu'un résolveur sérieux),
  • Et quelques autres risques plus exotiques.

Comme indiqué au début, le CA/Browser Forum a décidé que le test des CAA serait obligatoire à partir du 8 septembre 2017. (Cf. la décision.) Parmi les rares enregistrements CAA dans la nature, on trouve celui de Google, qui autorise deux AC :


% dig CAA google.com
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55244
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
...
;; ANSWER SECTION:
google.com.		86400 IN CAA 0 issue "pki.goog"
google.com.		86400 IN CAA 0 issue "symantec.com"
...

  

(Le TLD .goog est apparemment utilisé par Google pour son infrastructure, .google étant plutôt pour les choses publiques.) Notez que gmail.com, lui, pourtant souvent détourné par des gouvernements et des entreprises qui veulent surveiller le trafic, n'a pas d'enregistrement CAA. Le célèbre SSL [sic] Labs teste la présence d'un enregistrement CAA. S'il affiche « DNS Certification Authority Authorization (CAA) Policy found for this domain », c'est bon. Regardez le cas de Google.

Le type CAA est relativement récent (quatre ans quand même), donc il crée des problèmes amusants avec les serveurs DNS bogués comme ceux de BNP Paribas, qui timeoutent lorsque le type demandé n'est pas A :


% dig CAA mabanqueprivee.bnpparibas

; <<>> DiG 9.10.3-P4-Debian <<>> CAA mabanqueprivee.bnpparibas
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 42545
                                       ^^^^^^^^

  

Quelques lectures et ressources pour finir :


Téléchargez le RFC 6844


L'article seul

RFC 8228: Guidance on Designing Label Generation Rulesets (LGRs) Supporting Variant Labels

Date de publication du RFC : Août 2017
Auteur(s) du RFC : A. Freytag
Pour information
Première rédaction de cet article le 23 août 2017


Le RFC 7940 définissait un langage, LGR (Label Generation Ruleset), pour exprimer des politiques d'enregistrement de noms écrits en Unicode, avec les éventuelles variantes. Par exemple, avec LGR, on peut exprimer une politique disant que les caractères de l'alphabet latin et eux seuls sont autorisés, et que « é » est considéré équivalent à « e », « ç » à « c », etc. Ce nouveau RFC donne quelques conseils aux auteurs LGR. (Personnellement, je n'ai pas trouvé qu'il apportait grand'chose par rapport au RFC 7940.)

LGR peut s'appliquer à tous les cas où on a des identificateurs en Unicode mais il a surtout été développé pour les IDN (RFC 5890). Les exemples de ce RFC 8228 sont donc surtout pris dans ce domaine d'application. Un ensemble de règles exprimées dans le langage du RFC 7940 va définir deux choses :

  • Si un nom donné est acceptable ou pas (dans l'exemple donné plus haut, un registre de noms de domaine ouest-européen peut décider de n'autoriser que l'alphabet latin),
  • Et quels noms sont des variantes les uns des autres. Une variante est un nom composé de caractères Unicode différents mais qui est considéré comme « le même » (la définition de ce que c'est que « le même » n'étant pas technique ; par exemple, un anglophone peut considérer que theatre et theater sont « le même » mot, alors que, du point de vue technique, ce sont deux chaînes de caractère différentes). Le RFC dit que deux noms sont « le même » s'ils sont visuellement similaires (google vs. goog1e), mais c'est une notion bien trop floue, et qui dépend en outre de la police de caractères utilisée (avec Courier, google et goog1e sont difficilement distinguables). Le RFC ajoute qu'il y a également le cas de la similarité sémantique (le cas de theatre vs. theater, ou bien celui des sinogrammes traditionnels vs. les simplifiés ou encore, mais c'est compliqué, le cas des accents en français, avec « café » et « cafe » mais aussi avec « interne » et « interné »).

L'idée est que le registre utilise ensuite ces règles pour accepter ou refuser un nom et, si le nom est accepté, pour définir quelles variantes deviennent ainsi bloquées, ou bien attribuées automatiquement au même titulaire. Notez une nouvelle fois qu'il s'agit d'une décision politique et non technique. Le RFC dit d'ailleurs prudemment dans la section 1 qu'il ne sait pas si ce concept de variante est vraiment utile à quoi que ce soit. (J'ajoute qu'en général ce soi-disant risque de confusion a surtout été utilisé comme FUD anti-Unicode, par des gens qui n'ont jamais digéré la variété des écritures humaines.)

J'ai dit qu'une variante était « le même » que le nom de base. Du point de vue mathématique, la relation « est une variante de » doit donc être symétrique et transitive (section 2). « Est une variante de » est noté avec un tiret donc A - B signifie « A est une variante de B » (et réciproquement, vu la symétrie). Notez que le RFC 7940 utilisait un langage fondé sur XML alors que ce RFC 8228 préfère une notation plus compacte. La section 18 de notre RFC indique la correspondance entre la notation du RFC 7940 et celle du nouveau RFC.

Mais toutes les relations entre noms « proches » ne sont pas forcément symétriques et transitives. Ainsi, la relation « ressemble à » (le chiffre 1 ressemble à la lettre l minuscule) est non seulement vague (cela dépend en effet de la police) mais également non transitive (deux noms ressemblent chacun à un nom dont la forme est quelque part entre eux, sans pour autant se ressembler entre eux).

Le RFC 7940 permet de définir des relations qui ne sont ni symétriques, ni transitives. Mais ce nouveau RFC 8228 préfère les relations d'équivalence.

Bon, et maintenant, comment on crée les variantes d'un nom ou d'un composant d'un nom (section 6) ? On remplace chaque caractère du nom originel par toutes ses variantes possibles (le fait qu'un caractère correspond à un autre est noté -->). Ainsi, si on a décidé que « e » et « é » étaient équivalents (é --> e), le nom « interne » aura comme variantes possibles « interné », « intérne » et « intérné » (oui, une seule de ces variantes a un sens en français, je sais). Une fois cette génération de variantes faites, le registre, selon sa politique, pourra l'utiliser pour, par exemple, refuser l'enregistrement d'« interné » si « interne » est déjà pris (je le répète parce que c'est important : ce RFC ne décrit pas de politique, l'exemple ci-dessus n'est qu'un exemple).

En pratique, travailler caractère par caractère n'est pas toujours réaliste. Il y a des cas où c'est un groupe de caractères qui poserait problème. La section 7 introduit une amélioration où on peut étiqueter les correspondances de manière asymétrique. Le caractère x veut dire que l'allocation du nom est interdite (détails en section 9), le a qu'elle est autorisée (détails en section 8). On pourrait donc avoir « e x--> é » et « é a--> e » ce qui veut dire que la réservation de « interne » bloquerait celle d'« interné » mais pas le contraire.

Le monde des identificateurs étant très riche et complexe, on ne s'étonnera pas que des règles trop simples soient souvent prises en défaut. Ainsi, la variante peut dépendre du contexte : dans certaines écritures (comme l'arabe), la forme d'un caractère dépend de sa position dans le mot, et un même caractère peut donc entrainer de la confusion s'il est à la fin d'un mot mais pas s'il est au début. Il faut donc étiqueter les relations avec ce détail (par exemple, « final: C --> D » si le caractère noté C peut se confondre avec le caractère D mais uniquement si C est en fin de mot, cf. section 15 du RFC).

Si vous développez du LGR, au moins deux logiciels peuvent aider, celui de Viagénie, lgr-crore et lgr-django et un logiciel de l'auteur de ce RFC, développé pour le « ICANN Integration Panel work » mais qui ne semble pas publié.


Téléchargez le RFC 8228


L'article seul

RFC 8193: Information Model for Large-Scale Measurement Platforms (LMAPs)

Date de publication du RFC : Août 2017
Auteur(s) du RFC : T. Burbridge, P. Eardley (BT), M. Bagnulo (Universidad Carlos III de Madrid), J. Schoenwaelder (Jacobs University Bremen)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF lmap
Première rédaction de cet article le 22 août 2017


Ce RFC est l'un de ceux du groupe de travail IETF nommé LMAP, pour « Large-Scale Measurement Platforms ». Ce groupe développe des normes pour les systèmes de mesure à grande échelle, ceux où des centaines ou des milliers de sondes quantifient l'Internet. Il décrit le modèle de données utilisé.

Le projet LMAP est décrit dans les RFC 7536 (qui explique à quoi sert LMAP, avec deux exemples concrets) et RFC 7594 (qui normalise le vocabulaire et le cadre général). Pour résumer très vite, LMAP prévoit un Measurement Agent (la sonde) qui parle à un contrôleur, qui lui dit quoi faire, et à un collecteur, qui ramasse les données. Ce nouveau RFC décrit le modèle de données abstrait utilisé dans ces deux communications.

Rappelons que, dans le cadre défini pour LMAP, chaque MA (Measurement Agent) parle à un et un seul contrôleur, qui lui dit ce qu'il doit mesurer. (LMAP sépare le rôle du contrôleur et du collecteur, rôles que beaucoup de systèmes déployés fusionnent.) Il y a donc trois protocoles en jeu, le protocole de contrôle, entre le contrôleur et les MA, le protocole de collecte, entre les collecteurs et les MA, et enfin les protocoles utilisés dans les tests (ICMP ECHO, par exemple, mais aussi HTTP, DNS…) Enfin, il y a aussi la configuration nécessaire dans tous ces équipements. Tous ces points peuvent faire l'objet d'un travail de normalisation, mais ce RFC 8193 présente uniquement un modèle de données adapté au protocole de collecte et au protocole de contrôle.

La notion de modèle de données est expliquée dans le RFC 3444. En gros, il s'agit d'un modèle abstrait (très abstrait) décrivant quel genre de données sont échangées. Il peut se formaliser de manière un peu plus concrète par la suite, par exemple en utilisant le langage YANG (c'est justement ce que fait le RFC d'accompagnement, le RFC 8194). Un tel modèle sert à guider la normalisation, à faciliter la traduction d'un protocole dans un autre s'ils utilisent le même modèle de données, et à définir les données que doit mesurer et conserver le MA.

Le modèle défini ici utilise une notation formelle, utilisant des types de données classiques en programmation (section 3 du RFC) : int (un entier), boolean (une valeur logique), string, mais aussi des types plus riches comme datetime ou uri.

Le modèle lui-même est dans la section 4 du RFC. Je rappelle qu'il suit le cadre du RFC 7594. Il a six parties :

  • Informations de pré-configuration du MA (celle qui est faite en usine),
  • Informations de configuration du MA,
  • Instructions reçues par le MA (« envoie telle requête DNS à tel serveur, de telle heure à telle heure »),
  • Journal du MA, avec des évènements comme le redémarrage de la sonde,
  • État général du MA (« je sais faire des tests IPv6 »),
  • Et bien sûr, le résultat des mesures, ce qui est l'information la plus importante.

Le MA peut également avoir d'autres informations, par exemple des détails sur son type de connectivité.

Pour donner des exemples concrets, je vais souvent citer les sondes RIPE Atlas, même si ce système, conçu avant le RFC, ne suis pas exactement ce modèle. C'est sans doute l'un des plus grands, voire le plus grand réseau de points de mesure dans le monde, avec près de 10 000 sondes connectées. Par exemple, pour la configuration, les informations de pré-configuration d'une sonde Atlas comprennent une adresse MAC et un identificateur, qu'on peut voir sur la sonde : atlas-mac-id.jpg

Les sondes sont également pré-configurées avec le nom du contrôleur (qui sert également de collecteur). Elles acquièrent le reste de leur configuration par DHCP et SLAAC. Regardons par exemple la sonde d'identificateur #21660, installée à Cochabamba, on voit les adresses IP obtenues : sonde-atlas-21660.png

Les sondes Atlas disposent également d'étiquettes (tags) décrivant plus précisement certaines de leurs caractéristiques. Certaines des étiquettes sont attribuées manuellement par leur propriétaire (en bleu sur l'image ci-dessous), d'autres, qui peuvent être déterminées automatiquement, sont mises par le système (en vert sur l'image), et rentrent donc dans le cadre de la partie « état général de la sonde » du modèle : sonde-atlas-21660-tags.png

Les classes utilisées dans le modèle sont, entre autres :

  • Les agendas (schedules) qui indiquent quelle tâche doit être accomplie,
  • Les canaux de communication (channels),
  • les configurations (task configurations),
  • Les évènements (events), qui détaillent le moment où une tâche doit être exécutée.

Première partie, la pré-configuration (section 4.1). On y trouve les informations dont le MA aura besoin pour accomplir sa mission. Au minimum, il y aura un identificateur (par exemple l'adresse MAC), les coordonnées de son contrôleur (par exemple un nom de domaine, mais le RFC ne cite que le cas où ces coordonnées sont un URL, pour les protocoles de contrôle de type REST), peut-être des moyens d'authentification (comme le certificat de l'AC qui signera le certificat du contrôleur). URL et moyens d'authentification, ensemble, forment un canal de communication.

Dans le langage formel de ce modèle, cela fait :


     object {
         [uuid                ma-preconfig-agent-id;]
          ma-task-obj         ma-preconfig-control-tasks<1..*>;
          ma-channel-obj      ma-preconfig-control-channels<1..*>;
          ma-schedule-obj     ma-preconfig-control-schedules<1..*>;
         [uri                 ma-preconfig-device-id;]
          credentials         ma-preconfig-credentials;
     } ma-preconfig-obj;     

    

Cela se lit ainsi : l'information de pré-configuration (type ma-preconfig-obj) comprend divers attributs dont un identificateur (qui est un UUID, on a vu que les Atlas utilisaient un autre type d'identificateurs), des canaux de communication avec le contrôleur, et des lettres de créance à présenter pour s'authentifier. Notez que l'identificateur est optionnel (entre crochets).

Vous avez compris le principe ? On peut passer à la suite plus rapidement (je ne vais pas répéter les déclarations formelles, voyez le RFC). La configuration (section 4.2) stocke les informations volatiles (la pré-configuration étant consacrée aux informations plus stables, typiquement stockées sur une mémoire permanente).

Les instructions (section 4.3) sont les tâches que va accomplir la sonde, ainsi que l'agenda d'exécution, et bien sûr les canaux utilisé pour transmettre les résultats. La journalisation (section 4.4) permet d'enregistrer les problèmes (« mon résolveur DNS ne marche plus ») ou les évènements (« le courant a été coupé »).

Il ne sert évidemment à rien de faire des mesures si on ne transmet pas le résultat. D'où la section 4.6, sur la transmission des résultats de mesure au collecteur. La communication se fait via les canaux décrits en section 4.8.

Une dernière photo, pour clore cet article, une sonde RIPE Atlas en fonctionnement, et une autre sonde, la SamKnows : atlas-et-samknows.jpg

Merci à André Sintzoff pour avoir trouvé de grosses fautes.


Téléchargez le RFC 8193


L'article seul

jq, traiter du JSON en ligne de commande

Première rédaction de cet article le 9 août 2017


Aujourd'hui, énormément de données sont distribuées dans le format JSON. La simplicité de ce format, normalisé dans le RFC 7159, et bien sûr son utilisation dans JavaScript expliquent ce succès. Pour le transport de données (celui du texte est une autre histoire), JSON a largement remplacé XML. Il existe donc des bibliothèques pour traiter le JSON dans tous les langages de programmation. Mais si on veut une solution plus simple, permettant de traiter du JSON depuis la ligne de commande ? C'est là qu'intervient jq, qui permet à la fois de faire des opérations simples en ligne de commande sur des données JSON, et de programmer des opérations complexes, si nécessaires.

jq est parfois présenté comme « l'équivalent de sed pour JSON ». Ce n'est pas faux, puisqu'il permet en effet des opérations simples depuis la ligne de commande. Mais c'est assez court, comme description : jq est bien plus que cela, il inclut même un langage de programmation complet.

Commençons par quelques exemples simples. On va utiliser le JSON produit en utilisant l'outil madonctl pour récupérer des messages (les pouètes) envoyées sur Mastodon. Si je fais :

%  madonctl --output json timeline :afnic
[{"id":3221183,"uri":"tag:mastodon.social,2017-07-20:objectId=13220936:objectType=Status","url":"https://mastodon.soc
ial/users/afnic/updates/3830115","account":{"id":37388,"username":"afnic","acct":"afnic@mastodon.social","display_nam
e":"Afnic","note":"\u003cp\u003eLe registre des noms de domaine en .fr\u003cbr\u003eRdv sur \u003ca href=\"http://www
.afnic.fr/\" rel=\"nofollow noopener\" target=\"_blank\"\u003e   ...
    

J'ai récupéré les pouètes qui parlent de l'AFNIC, sous forme d'une seule ligne de JSON, peu lisible. Alors qu'avec jq :

%  madonctl --output json timeline :afnic | jq . 
[
  {
    "id": 3221183,
    "uri": "tag:mastodon.social,2017-07-20:objectId=13220936:objectType=Status",
    "url": "https://mastodon.social/users/afnic/updates/3830115",
    "account": {
      "id": 37388,
      "username": "afnic",
      "acct": "afnic@mastodon.social",
      "display_name": "Afnic",
      "note": "<p>Le registre des noms de domaine en .fr<br>Rdv sur <a href=\"http://www.afnic.fr/\" rel=\"nofollow n
oopener\" target=\"_blank\"><span class=\"invisible\">http://www.</span><span class=\"\">afnic.fr/</span><span class=
\"invisible\"></span></a></p>",
      "url": "https://mastodon.social/@afnic",

    

J'ai au contraire du beau JSON bien lisible. C'est la première utilisation de jq pour beaucoup d'utilisateurs, dans un monde où les services REST fabriquent en général du JSON très compact (peut-être pour gagner quelques octets), jq peut servir de pretty-printer.

Le point après la commande jq est un filtre. jq fonctionne par enchainement de filtres et, par défaut, produit des données JSON joliment formatées. (Si, par contre, on ne veut pas de pretty-printing, on lance jq avec l'option --compact-output.)

Prenons un exemple où on veut sélectionner une seule information dans un fichier JSON. Mettons qu'on utilise RDAP (RFC 7482) pour trouver de l'information sur une adresse IP. RDAP produit du JSON :

% curl -s  http://rdap.db.ripe.net/ip/2001:1470:8000:53::44
{
  "handle" : "2001:1470:8000::/48",
  "startAddress" : "2001:1470:8000::/128",
  "endAddress" : "2001:1470:8000:ffff:ffff:ffff:ffff:ffff/128",
  "ipVersion" : "v6",
  "name" : "ARNES-IPv6-NET",
  "type" : "ASSIGNED",
  "country" : "SI",
...
    

Si on ne veut que le pays du titulaire d'un préfixe d'adresses IP, c'est simple avec jq, avec le filtre .country qui veut dire « le champ country de l'objet JSON (. étant le texte JSON de plus haut niveau, ici un objet) » :

% curl -s  http://rdap.db.ripe.net/ip/131.111.150.25 | jq .country
"GB"

% curl -s  http://rdap.db.ripe.net/ip/192.134.1.1 | jq .country
"FR"

On n'aurait pas pu utiliser les outils ligne de commande classiques comme grep ou sed car le format JSON n'est pas orienté ligne (et a trop de subtilités pour eux).

Et si on veut interroger son nœud Bitcoin pour connaitre à quel bloc il en est :

% bitcoin-cli getinfo | jq .blocks
478280

Cet exemple particulier n'est pas super-intéressant, on a déjà bitcoin-cli getblockcount mais cela montre bien qu'on peut utiliser jq très simplement, pour des tâches d'administration système.

Au passage, pour trouver le filtre à utiliser, il faut connaitre la structure du fichier JSON. On peut lire la documentation (dans l'exemple RDAP ci-dessus, c'est le RFC 7483) mais c'est parfois un peu compliqué alors que JSON, étant un format texte, fournit une solution plus simple : afficher le début du fichier JSON et repérer les choses qui nous intéressent. (Un format binaire comme CBOR, RFC 7049, ne permet pas cela.) Cette méthode peut sembler du bricolage, mais l'expérience prouve que les services REST n'ont pas toujours de documentation ou, lorsqu'ils en ont, elle est souvent fausse. Donc, savoir explorer du JSON est utile.

Notez que jq produit du JSON, donc les chaînes de caractères sont entre guillemets. Si on veut juste le texte, on utilise l'option --raw-output :

% curl -s  http://rdap.db.ripe.net/ip/2001:1470:8000:53::44 | jq --raw-output .country
SI
    

Et si on veut un membre d'un objet qui est lui-même un membre de l'objet de plus haut niveau ? On crée un filtre avec les deux clés :

%  echo '{"foo": {"zataz": null, "bar": 42}}' | jq .foo.bar
42
    

Un exemple réel d'un tel « double déréférencement » serait avec l'API de GitHub, ici pour afficher tous les messages de commit :

      
% curl -s https://api.github.com/repos/bortzmeyer/check-soa/commits | \
     jq --raw-output '.[].commit.message'
New rules for the Go linker (see <http://stackoverflow.com/questions/32468283/how-to-contain-space-in-value-string-of-link-flag-when-using-go-build>)
Stupid regression to bug #8. Fixed.
Timeout problem with TCP
Query the NN RRset with EDNS. Closes #9
Stupidest bug possible: I forgot to break the loop when we got a reply
TCP support
...

    

Et si le texte JSON était formé d'un tableau et pas d'un objet (rappel : un objet JSON est un dictionnaire) ? jq permet d'obtenir le nième élément d'un tableau (ici, le quatrième, le premier ayant l'indice 0) :

% echo '[1, 2, 3, 5, 8]' | jq '.[3]'
5
    

(Il a fallu mettre le filtre entre apostrophes car les crochets sont des caractère spéciaux pour le shell Unix.)

Revenons aux exemples réels. Si on veut le cours du Bitcoin en euros, CoinDesk nous fournit une API REST pour cela. On a juste à faire un triple accès :

% curl -s https://api.coindesk.com/v1/bpi/currentprice.json | jq .bpi.EUR.rate
"2,315.7568"
    

Notez que le cours est exprimé sous forme d'une chaîne de caractères, pas d'un nombre, et qu'il n'est pas à la syntaxe correcte d'un nombre JSON. C'est un des inconvénients de JSON : ce format est un tel succès que tout le monde en fait, et souvent de manière incorrecte. (Sinon, regardez une solution sans jq mais avec sed.)

Maintenant, on veut la liste des comptes Mastodon qui ont pouèté au sujet de l'ANSSI. Le résultat de la requête madonctl --output json timeline :anssi est un tableau. On va devoir itérer sur ce tableau, ce qui se fait avec le filtre [] (déjà utilisé plus haut dans l'exemple GitHub), et faire un double déréférencement, le membre account puis le membre acct :

% madonctl --output json timeline :anssi | jq '.[].account.acct'
"nschont@mastodon.etalab.gouv.fr"
"ChristopheCazin@mastodon.etalab.gouv.fr"
"barnault@mastodon.etalab.gouv.fr"
...
    

Parfait, on a bien notre résultat. Mais le collègue qui avait demandé ce travail nous dit qu'il faudrait éliminer les doublons, et trier les comptes par ordre alphabétique. Pas de problème, jq dispose d'un grand nombre de fonctions pré-définies, chaînées avec la barre verticale (ce qui ne déroutera pas les utilisateurs Unix) :

    
%  madonctl --output json timeline :anssi | jq '[.[].account.acct] | unique | .[]' 
"ChristopheCazin@mastodon.etalab.gouv.fr"
"G4RU"
"Sangokuss@framapiaf.org"
...

Oulah, ça devient compliqué ! unique se comprend sans difficulté mais c'est quoi, tous ces crochets en plus ? Comme unique travaille sur un tableau, il a fallu en fabriquer un : les crochets extérieurs dans l'expression [.[].account.acct] disent à jq de faire un tableau avec tous les .account.acct extraits. (On peut aussi fabriquer un objet JSON et je rappelle que objet = dictionnaire.) Une fois ce tableau fait, unique peut bien travailler mais le résultat sera alors un tableau :

   
%  madonctl --output json timeline :anssi | jq '[.[].account.acct] | unique'
[
  "ChristopheCazin@mastodon.etalab.gouv.fr",
  "G4RU",
  "Sangokuss@framapiaf.org",
...

Si on veut « aplatir » ce tableau, et avoir juste une suite de chaînes de caractères, il faut refaire une itération à la fin (le .[]).

(Les Unixiens expérimentés savent qu'il existe uniq et sort comme commandes Unix et qu'on aurait aussi pu faire jq '.[].account.acct' | sort | uniq. Chacun ses goûts. Notez aussi qu'il n'y a pas besoin d'un tri explicite en jq : unique trie le tableau avant d'éliminer les doublons.)

J'ai dit un peu plus haut que jq pouvait construire des textes JSON structurés comme les tableaux. Ça marche aussi avec les objets, en indiquant la clé et la valeur de chaque membre. Par exemple, si je veux un tableau dont les éléments sont des objets où la clé href désigné l'URL d'un pouète Mastodon (ici, les pouètes ayant utilisé le mot-croisillon « slovénie ») :

%  madonctl --output json timeline :slovénie | jq "[.[] | { href: .url }]" 
[
  {
    "href": "https://mastodon.gougere.fr/users/bortzmeyer/updates/40282"
  },
  {
    "href": "https://mamot.fr/@Nighten_Dushi/3131270"
  },
  {
    "href": "https://mastodon.cx/users/DirectActus/updates/29810"
  }
]
    

Les accolades entourant la deuxième partie du programme jq indiquent de construire un objet, dont on indique comme clé href et comme valeur le membre url de l'objet original.

Rappelez-vous, jq ne sert pas qu'à des filtrages ultra-simples d'un champ de l'objet JSON. On peut écrire de vrais programmes et il peut donc être préférable de les mettre dans un fichier. (Il existe évidemment un mode Emacs pour éditer plus agréablement ces fichiers source, jq-mode.) Si le fichier accounts.jq contient :

    
# From a Mastodon JSON file, extract the accounts
[.[].account.acct] | unique | .[]

Alors, on pourra afficher tous les comptes (on se ressert de --raw-output pour ne pas avoir d'inutiles guillemets) :

%  madonctl --output json timeline :anssi | jq --raw-output --from-file accounts.jq 
ChristopheCazin@mastodon.etalab.gouv.fr
G4RU
Sangokuss@framapiaf.org
...

Mais on peut vouloir formater des résultats sous une forme de texte, par exemple pour inclusion dans un message ou un rapport. Reprenons notre nœud Bitcoin et affichons la liste des pairs (Bitcoin est un réseau pair-à-pair), en les classant selon le RTT décroissant. On met ça dans le fichier bitcoin.jq :

"You have " + (length | tostring) + " peers. From the closest (network-wise) to the furthest away",
       ([.[] | {"address": .addr, "rtt": .pingtime}] | sort_by(.rtt) | 
       .[] | ("Peer " + .address + ": " + (.rtt|tostring) + " ms"))
    

Et on peut faire :

%   bitcoin-cli getpeerinfo  | jq --raw-output --from-file bitcoin.jq
You have 62 peers. From the closest (network-wise) to the furthest away
Peer 10.105.127.82:38806: 0.00274 ms
Peer 192.0.2.130:8333: 0.003272 ms
Peer [2001:db8:210:5046::2]:33567: 0.014099 ms
...
    

On a utilisé des constructions de tableaux et d'objets, la possibilité de trier sur un critère quelconque (ici la valeur de rtt, en paramètre à sort_by) et des fonctions utiles pour l'affichage de messages : le signe plus indique la concaténation de chaînes, et la fonction tostring transforme un entier en chaîne de caractères (jq ne fait pas de conversion de type implicite).

jq a également des fonctions pré-définies pour faire de l'arithmétique. Utilisons-les pour analyser les résultats de mesures faites par des sondes RIPE Atlas. Une fois une mesure lancée (par exemple la mesure de RTT #9211624, qui a été créée par la commande atlas-reach -r 100 -t 1 2001:500:2e::1, cf. mon article « Using RIPE Atlas to Debug Network Connectivity Problems »), les résultats peuvent être récupérés sous forme d'un fichier JSON (à l'URL https://atlas.ripe.net/api/v2/measurements/9211624/results/). Cherchons d'abord le RTT maximal :

% jq '[.[].result[0].rtt] | max' < 9211624.json 
505.52918
    

On a pris le premier élément du tableau car, pour cette mesure, chaque sonde ne faisait qu'un test. Ensuite, on utilise les techniques jq déjà vues, plus la fonction max (qui prend comme argument un tableau, il a donc fallu construire un tableau). La sonde la plus lointaine de l'amer est donc à plus de 500 millisecondes. Et le RTT minimum ? Essayons la fonction min

% jq '[.[].result[0].rtt] | min' < 9211624.json
null
    

Ah, zut, certaines sondes n'ont pas réussi et on n'a donc pas de RTT, ce que jq traduit en null. Il faut donc éliminer du tableau ces échecs. jq permet de tester une valeur, avec select. Par exemple, (select(. != null) va tester que la valeur existe. Et une autre fonction jq, map, bien connue des programmeurs qui aiment le style fonctionnel, permet d'appliquer une fonction à tous les éléments d'un tableau. Donc, réessayons :

% jq '[.[].result[0].rtt] | map(select(. != null)) | min' < 9211624.json
1.227755
    

C'est parfait, une milli-seconde pour la sonde la plus proche. Notez que, comme map prend un tableau en entrée et rend un tableau, on aurait aussi pu l'utiliser au début, à la place des crochets :

% jq 'map(.result[0].rtt) | map(select(. != null)) | min' < 9211624.json
1.227755
    

Et la moyenne des RTT ? On va utiliser les fonctions add (additionner tous les éléments du tableau) et length (longueur du tableau) :

% jq '[.[].result[0].rtt] | add / length' < 9211624.json 
76.49538228
    

(Il y a une faille dans le programme, les valeurs nulles auraient dû être exclues. Je vous laisse modifier le code.) La moyenne n'est pas toujours très utile quand on mesure des RTT. C'est une métrique peu robuste, que quelques outliers suffisent à perturber. Il est en général bien préférable d'utiliser la médiane. Une façon simple de la calculer est de trier le tableau et de prendre l'élément du milieu (ou le plus proche du milieu) :

% jq '[.[].result[0].rtt] | sort | .[length/2]' < 9211624.json
43.853845

On voit que la médiane est bien plus petite que la moyenne, quelques énormes RTT ont en effet tiré la moyenne vers le haut. J'ai un peu triché dans le filtre jq ci-dessus car il ne marche que pour des tableaux de taille paire. Si elle est impaire, length/2 ne donnera pas un nombre entier et on récupérera null. Corrigeons, ce sera l'occasion d'utiliser pour la première fois la structure de contrôle if. Notez qu'une expression jq doit toujours renvoyer quelque chose (les utilisateurs de langages fonctionnels comme Haskell ne seront pas surpris par cette règle), donc pas de if sans une branche else :

% jq '[.[].result[0].rtt] | sort | if length % 2 == 0 then .[length/2] else .[(length-1)/2] end' < 9211624.json  
43.853845

Et maintenant, si on veut la totale, les quatre métriques avec du texte, on mettra ceci dans le fichier source jq. On a utilisé un nouvel opérateur, la virgule, qui permet de lancer plusieurs filtres sur les mêmes données :

map(.result[0].rtt) | "Median: " + (sort |
				    if length % 2 == 0 then .[length/2] else .[(length-1)/2] end |
				    tostring),
                      "Average: " + (map(select(. != null)) | add/length | tostring),
                      "Min: " + (map(select(. != null)) | min | tostring),
                      "Max: " + (max | tostring)
    

Et cela nous donne :

% jq --raw-output --from-file atlas-rtt.jq < 9211624.json     
Median: 43.853845
Average: 77.26806290909092
Min: 1.227755
Max: 505.52918
    

Peut-on passer des arguments à un programme jq ? Évidemment. Voyons un exemple avec la base des « espaces blancs » décrite dans le RFC 7545. On va chercher la puissance d'émission maximale autorisée pour une fréquence donnée, avec ce script jq, qui contient la variable frequency, et qui cherche une plage de fréquences (entre startHz et stopHz) comprenant cette fréquence :

.result.spectrumSchedules[0].spectra[0].frequencyRanges[] |
     select (.startHz <= ($frequency|tonumber) and .stopHz >= ($frequency|tonumber)) |
     .maxPowerDBm     
    

On l'appelle en passant la fréquence où on souhaite émettre :

% jq --from-file paws.jq --arg frequency  5.2E8  paws-chicago.json 
15.99999928972511
    

(Ne l'utilisez pas telle quelle : par principe, les espaces blancs ne sont pas les mêmes partout. Celui-ci était pour Chicago en juillet 2017.)

Il est souvent utile, quand on joue avec des données, de les grouper par une certaine caractéristique, par exemple pour compter le nombre d'occurrences d'un certain phénomène, ou bien pour classer. C'est le classique GROUP BY de SQL, qui a son équivalent en jq. Revenons à la liste des comptes Mastodon qui ont pouèté sur l'ANSSI et affichons combien chacun a émis de pouètes. On va grouper les pouètes par compte, fabriquer un objet JSON dont les clés sont le compte, le trier, puis afficher le résultat. Avec ce code jq :

# Count the number of occurrences
[.[].account] | group_by(.acct) |
                # Create an array of objects {account, number}
                [.[] | {"account": .[0].acct, "number": length}] |
                # Now sort
                sort_by(.number) | reverse | 
                # Now, display
                .[] | .account + ": " + (.number | tostring)
    

On obtiendra :

% madonctl --output json timeline :anssi | jq -r -f   anssi.jq
gapz@mstdn.fr: 4
alatitude77@mastodon.social: 4
nschont@mastodon.etalab.gouv.fr: 2
zorglubu@framapiaf.org: 1
sb_51_@mastodon.xyz: 1
    

Voilà, on a bien avancé et je n'ai toujours pas donné d'exemple avec le DNS. Un autre cas d'utilisation de select va y pourvoir. Le DNS Looking Glass peut produire des résultats en JSON, par exemple ici le SOA du TLD .xxx : curl -s -H 'Accept: application/json' https://dns.bortzmeyer.org/xxx/SOA. Si je veux juste le numéro de série de cet enregistrement SOA ?

% curl -s -H 'Accept: application/json' https://dns.bortzmeyer.org/xxx/SOA | \
    jq '.AnswerSection[0].Serial'
2011210193
    

Mais il y a un piège. En prenant le premier élément de la Answer Section, j'ai supposé qu'il s'agissait bien du SOA demandé. Mais l'ordre des éléments dans les sections DNS n'est pas défini. Par exemple, si une signature DNSSEC est renvoyée, elle sera peut-être le premier élément. Il faut donc être plus subtil, et utiliser select pour ne garder que la réponse de type SOA :

% curl -s -H 'Accept: application/json' https://dns.bortzmeyer.org/xxx/SOA | \
    jq '.AnswerSection | map(select(.Type=="SOA")) | .[0].Serial'
2011210193

Notre code jq est désormais plus robuste. Ainsi, sur un nom qui a plusieurs adresses IP, on peut ne tester que celles de type IPv6, ignorant les autres, ainsi que les autres enregistrements que le serveur DNS a pu renvoyer :

% curl -s -H 'Accept: application/json' https://dns.bortzmeyer.org/gmail.com/ADDR | \
    jq '.AnswerSection | map(select(.Type=="AAAA")) | .[] | .Address' 
"2607:f8b0:4006:810::2005"
    

Bon, on approche de la fin, vous devez être fatigué·e·s, mais encore deux exemples, pour illustrer des trucs et concepts utiles. D'abord, le cas où une clé dans un objet JSON n'a pas la syntaxe d'un identificateur jq. C'est le cas du JSON produit par le compilateur Solidity. Si je compile ainsi :

% solc  --combined-json=abi registry.sol > abi-registry.json

Le JSON produit a deux défauts. Le premier est que certains utilisateurs ont une syntaxe inhabituelle (des points dans la clé, et le caractère deux-points) :

{"registry.sol:Registry": {"abi": ...
  

jq n'accepte pas cette clé comme filtre :

%  jq '.contracts.registry.sol:Registry.abi' abi-registry.json  
jq: error: syntax error, unexpected ':', expecting $end (Unix shell quoting issues?) at <top-level>, line 1:
.contracts.registry.sol:Registry.abi                       
jq: 1 compile error
  

Corrigeons cela en mettant la clé bizarre entre guillemets :

% jq '.contracts."registry.sol:Registry".abi' abi-registry.json
  

On récupère ainsi l'ABI du contrat. Mais, et là c'est gravement nul de la part du compilateur, l'ABI est une chaîne de caractères à évaluer pour en faire du vrai JSON ! Heureusement, jq a tout ce qu'il faut pour cette évaluation, avec la fonction fromjson :

% jq '.contracts."registry.sol:Registry".abi | fromjson' abi-registry.json

Ce problème des clés qui ne sont pas des identificateurs à la syntaxe traditionnelle se pose aussi si l'auteur du JSON a choisi, comme la norme le lui permet, d'utiliser des caractères non-ASCII pour les identificateurs. Prenons par exemple le fichier JSON des verbes conjugués du français, en https://github.com/Drulac/Verbes-Francais-Conjugues. Le JSON est :

[{ "Indicatif" : { "Présent" : [ "abade", "abades", "abade", "abadons", "abadez", "abadent" ], "Passé composé" : [ "abadé", "abadé", "abadé", "abadé", "abadé", "abadé" ], "Imparfait" : [ "abadais", "abadais", "abadait", "abadions", "abadiez", "abadaient" ], "Plus-que-parfait" : [ "abadé", "abadé", "abadé", "abadé", "abadé", "abadé" ], "Passé simple" : [ "abadai", "abadas", "abada", "abadâmes", "abadâtes", "abadèrent" ], "Passé antérieur" : [ "abadé", "abadé", "abadé", "abadé", "abadé", "abadé" ], ...
    

Il faut donc mettre les clés non-ASCII entre guillemets. Ici, la conjugaison du verbe « reposer » :

% jq 'map(select(.Infinitif."Présent"[0] == "reposer"))' verbs.json
[
  {
    "Indicatif": {
      "Présent": [
        "repose",
        "reposes",
        "repose",
   ...
    

Notez toutefois que jq met tout en mémoire. Cela peut l'empêcher de lire de gros fichiers :

% ls -lh la-crime.json 
-rw-r--r-- 1 stephane stephane 798M Sep  9 19:09 la-crime.json

% jq .data la-crime.json     
error: cannot allocate memory
zsh: abort      jq .data la-crime.json
   

Et un dernier exemple, pour montrer les manipulations de date en jq, ainsi que la définition de fonctions. On va utiliser l'API d'Icinga pour récupérer l'activité de la supervision. La commande curl -k -s -u operator:XXXXXXX -H 'Accept: application/json' -X POST 'https://ADRESSE:PORT/v1/events?types=StateChange&queue=operator' va nous donner les changements d'état des machines et des services supervisés. Le résultat de cette commande est du JSON : on souhaite maintenant le formater de manière plus compacte et lisible. Un des membres JSON est une date écrite sous forme d'un nombre de secondes depuis une epoch. On va donc la convertir en jolie date avec la fonction todate. Ensuite, les états Icinga (.state) sont affichés par des nombres (0 = OK, 1 = Avertissement, etc.), ce qui n'est pas très agréable. On les convertit en chaînes de caractères, et mettre cette conversion dans une fonction, s2n :

def s2n(s):
  if s==0 then "OK" else if s==1 then "Warning" else "Critical" end  end;
(.timestamp | todate) + " " + .host + " " + .service + " " +  " " + s2n(.state) +
    " " + .check_result.output

Avec ce code, on peut désormais exécuter :

% curl -k -s -u operator:XXXXXXX -H 'Accept: application/json' -X POST 'https://ADRESSE:PORT/v1/events?types=StateChange&queue=operator' | \
   jq --raw-output --unbuffered --from-file logicinga.jq
...
2017-08-08T19:12:47Z server1 example Critical HTTP CRITICAL: HTTP/1.1 200 OK - string 'Welcome' not found on 'http://www.example.com:80/' - 1924 bytes in 0.170 second response time 

Quelques lectures pour aller plus loin :

Merci à Jean-Edouard Babin et Manuel Pégourié-Gonnard pour la correction d'une grosse bogue dans la première version de cet article.


L'article seul

RFC 8198: Aggressive Use of DNSSEC-Validated Cache

Date de publication du RFC : Juillet 2017
Auteur(s) du RFC : K. Fujiwara (JPRS), A. Kato (Keio/WIDE), W. Kumari (Google)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 26 juillet 2017


Lorqu'un client DNS interroge un résolveur, celui-ci peut répondre très vite si l'information demandée est dans sa mémoire (son « cache ») mais cela peut être beaucoup plus lent s'il faut aller demander aux serveurs faisant autorité. Il est donc essentiel, pour les performances du DNS, d'utiliser le cache le plus possible. Traditionnellement, le résolveur n'utilisait le cache que si la question posée par le client avait une réponse exacte dans le cache. Mais l'arrivée de DNSSEC autorise un usage plus énergique et plus efficace du cache : ce nouveau RFC permet aux résolveurs de synthétiser des réponses à partir des informations DNSSEC.

Comment peut-il faire ? Eh bien prenons par exemple les enregistrements NSEC. Ils indiquent une plage où il n'y a pas de noms enregistrés. Ainsi, si une zone example.com contient :

albatross IN AAAA 2001:db8:1028::a:1
elephant  IN AAAA 2001:db8:1028::a:2
zebra     IN AAAA 2001:db8:1028::a:3
    

Sa signature DNSSEC va ajouter des enregistrements NSEC, notamment :

albatross.example.com. IN NSEC elephant.example.com. AAAA    

qui veut dire qu'il n'y a pas de nom entre albatross et elephant. Si le client interroge le résolveur à propos de cat.example.com, le résolveur ira voir les serveurs faisant autorité, il aura une réponse négative (NXDOMAIN, ce nom n'existe pas) et l'enregistrement NSEC. Les deux informations seront renvoyées au client, et pourront être mémorisées dans le cache. Maintenant, si le client demande dog.example.com, les résolveurs traditionnels retourneront demander aux serveurs faisant autorité. Alors que les résolveurs modernes, conformes à ce nouveau RFC 8198, pourront déduire du NSEC que dog.example.com n'existe pas non plus, et immédiatement générer un NXDOMAIN pour le client. Cela fera gagner du temps et des efforts à tout le monde. (Les règles exactes sont dans la section 5.1 de notre RFC.)

La règle (désormais dépassée) comme quoi le résolveur ne peut répondre immédiatement que s'il a l'information correspondant à la question exacte est spécifiée dans le RFC 2308, pour le cache négatif (mémoriser les réponses NXDOMAIN). Désormais, elle n'est plus obligatoire si (et seulement si, voir section 9) le résolveur valide avec DNSSEC, et si la zone est signée (aucun changement si elle ne l'est pas). Outre le cas simple de NSEC avec des réponses négatives, présenté plus haut, il y a deux cas plus complexes, les enregistrements NSEC3, et les jokers. Si la zone est signée avec NSEC3 au lieu de NSEC, les enregistrements NSEC3 indiqueront des condensats et pas des noms et le résolveur devra donc condenser le nom demandé, pour voir s'il tombe dans un NSEC3 connu, et si on peut synthétiser le NXDOMAIN. Les règles exactes sur les NSEC3 sont dans le RFC 5155 (attention, c'est compliqué), sections 8.4 à 8.7, et dans notre RFC, section 5.2. Évidemment, si la zone est signée avec l'option « opt-out » (c'est le cas de la plupart des TLD), le résolveur ne peut pas être sûr qu'il n'y a pas un nom non signé dans une plage indiquée par un enregistrement NSEC, et ne peut donc pas synthétiser. Tout aussi évidemment, toute solution qui empêchera réellement l'énumération des noms dans une zone signée (comme le projet NSEC5) sera incompatible avec cette solution.

Si la zone inclut des jokers (RFC 1034, section 4.3.3), par exemple example.org :

    
avocado   IN A 192.0.2.1
*         IN A 192.0.2.2
zucchini IN A 192.0.2.3

Alors, le résolveur pourra également synthétiser des réponses positives. S'il a déjà récupéré et mémorisé le joker, et que le client lui demande l'adresse IPv4 de leek.example.org, le résolveur pourra tout de suite répondre 192.0.2.2. (J'ai simplifié : le résolveur doit aussi vérifier que le nom leek.example.org n'existe pas, ou en tout cas n'a pas d'enregistrement A. Le joker ne masque en effet pas les noms existants. Détails en section 5.3 du RFC.)

Pour cette utilisation plus énergique de la mémoire d'un résolveur validant, il a fallu amender légèrement le RFC 4035, dont la section 4.5 ne permettait pas cette synthèse de réponses (la nouvelle rédaction est en section 7). Notez un inconvénient potentiel de cette synthèse : un nom ajouté ne sera pas visible tout de suite. Mais c'est de toute façon une propriété générale du DNS et de ses caches souvent appelée, par erreur, « durée de propagation ».

La durée exacte pendant laquelle le résolveur « énergique » pourra garder les informations qui lui servent à la synthèse des réponses est donnée par le TTL des NSEC.

Quel intérêt à cette synthèse énergique de réponses ? Comme indiqué plus haut, cela permet de diminuer le temps de réponse (section 6). Du fait de la diminution de nombre de questions à transmettre, cela se traduira également par une diminution de la charge, et du résolveur, et du serveur faisant autorité. Sur la racine du DNS, comme 65 % des requêtes entrainent un NXDOMAIN (voir par exemple les statistiques de A-root), le gain devrait être important. Un autre avantage sera pour la lutte contre les attaques dites « random QNAMEs » lorsque l'attaquant envoie plein de requêtes pour des noms aléatoires (générant donc des NXDOMAIN). Si l'attaquant passe par un résolveur, celui-ci pourra écluser la grande majorité des requêtes sans déranger le serveur faisant autorité.

Mais la synthèse énergique entraine aussi un gain en matière de vie privée (cf. RFC 7626) : les serveurs faisant autorité verront encore moins de questions.

Cette technique de « cache négatif énergique » avait été proposée pour la première fois dans la section 6 du RFC 5074. Elle s'inscrit dans une série de propositions visant à augmenter l'efficacité des caches DNS, comme le « NXDOMAIN cut » du RFC 8020. La synthèse énergique de réponses prolonge le RFC 8020, en allant plus loin (mais elle nécessite DNSSEC).

Il semble que Google Public DNS mette déjà en œuvre une partie (les réponses négatives) de ce RFC, mais je n'ai pas encore vérifié personnellement.


Téléchargez le RFC 8198


L'article seul

Conférence climagic sur la ligne de commande et ses beautés

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


Le 19 juillet 2017, à Prague, Mark Krenz, le fondateur et animateur du fameux compte Twitter climagic (Command-Line interface magic), profitait de ses vacances en Europe pour faire un exposé sur la ligne de commande, ses beautés, et le projet climagic. Ça m'a fait rater la séance plénière de l'IETF 99 mais ça valait la peine. (Pour ce·ux·lles qui n'étaient pas à Prague, les pauvres, l'exposé est visible chez Google.)

L'exposé se tenait à la ČVUT (excellents locaux, vue magnifique sur la ville depuis le 14e étage). La grande salle était presque pleine. Comme on s'en doute, il n'y avait que trois ou quatre femmes.

La thèse centrale de Mark Krenz est que la ligne de commande n'est pas une survivance du passé, qu'on garde par habitude, ou par nostalgie. C'est toujours un outil puissant et efficace pour les professionnels (comme un de ses enfants lui avait dit une fois, lorsque le réseau était en panne, « open the black window and type text, to fix the network »). Le concept n'est en outre pas spécifique à Unix, Mark Krenz a cité l'ARexx d'Amiga. Une des preuves de cette pertinence de la ligne de commande est que désormais même Windows a une ligne de commandes de qualité raisonnable, Powershell. L'orateur a ensuite illustré ce point de vue de nombreux exemples.

Prévoyant que l'unixien moyen était pinailleur, il a prévenu tout de suite que c'était ses exemples, qu'il y avait plusieurs façons de faire, et que ce n'était pas la peine de lui dire que tel exemple aurait pu être fait avec telle autre méthode. Il a aussi précisé que la référence pour ses tweets était Linux avec bash (même s'il teste parfois sur une VM FreeBSD) et qu'il ne fallait pas l'embêter avec des « mais c'est pas portable ». Effectivement, il utilise des exemples comme la commande interne help (specifique à bash) ou comme l'excellente et indispensable option -i de sed (spécifique à GNU sed).

Mark Krenz (qui travaille au CACR et est l'auteur des excellents num-utils) a d'abord résumé le projet climagic. Démarré en 2009, il consiste en un compte Twitter (il y a aussi des vidéos) où environ la moitié des tweets sont des commandes Unix. Elles sont en général sous le format commande # description (le croisillon indiquant un commentaire). Par exemple :

killall -USR1 dd # Force each dd command in the process table to output its current status (blocks written, etc). The USR1 signal does this.
    

(lisez les documentations de killall et dd si nécessaire.) Les autres tweets sont diverses réflexions, ou des blagues, par exemple sur Chuck Norris (« Chuck Norris once ran cd .. in / and it worked »).

Ah, et pourquoi le magic dans « climagic ». Précisement parce que cela n'a rien de magique, tout peut être appris. « Witchcraft to the ignorant, simple science for the learned one » (Leigh Brackett).

La force du shell Unix est évidemment sa capacité à combiner des commandes pour faire davantage que ce que ferait chaque commande individuelle. Donc, lister des commandes et des options, c'est bien, mais il faut surtout se rappeler qu'on peut les combiner. Un exemple montré par l'auteur est une commande qui énonce à voix haute les réseaux Wi-Fi rencontrés : iwlist wlp2s0 scan | awk -F: '/ESSID/ {print $2}' | sort|uniq | espeak.

Je ne vais pas citer tous les trucs Unix qu'a montré l'orateur, uniquement ceux que je ne connaissais pas. Vous pourrez ainsi juger de la profondeur de mon ignorance d'Unix :

  • Je n'utilise pas les opérateurs rigolos après les noms de variables du shell. Si ALPHABET vaut les lettres de l'alphabet (par exemple en faisant ALPHABET=$(echo {a..z} | tr -d ' '), alors echo ${ALPHABET:0:3} affichera les trois premières, et echo ${ALPHABET^^} affichera leurs versions majuscules (bash seulement).
  • Je ne connaissais pas certaines variables spéciales comme SHLVL (pour voir si on est dans un sous-shell), ou COLUMNS (qui était inutile sur un VT100 mais bien pratique sur un xterm).
  • J'ai découvert qu'après more et less, il y avait désormais une commande most (pas mal pour les fichiers binaires).
  • column est cool pour formater joliment des commandes qui ne le font pas elle-même (comme mount).
  • Il y a d'autres commandes que je ne connaissais pas mais qui ne m'ont pas convaincu comme comm, ou pee (analogue à tee mais envoie les données à un processus, pas à un fichier, il se trouve dans les moreutils).
  • Certaines commandes classiques donnent des résultats inattendus dans certains cas. Par exemple cal 9 1752. Si vous vous demandez ce que sont devenus ces treize jours, ils ont disparu suite à l'application de la réforme grégorienne à l'empire britannique (les pays catholiques l'ont fait longtemps avant, ce dont on peut déduire qu'Unix n'est pas catholique). Comme tous les pays n'ont pas adopté cette réforme au même moment, logiquement, le résultat devrait dépendre de la valeur de la variable d'environnement LC_CTIME mais ce n'est hélas pas le cas.
  • Je ne savais pas non plus que la barre oblique après un joker ne renvoyait que les répertoires. Parfait pour un du -sh */ (au fait, GNU sort peut trier ces tailles « human-readables », avec -h : du -sh */ | sort -rh | head -5 permet de voir les cinq plus gros répertoires, avec un joli affichage des tailles.)
  • Parmi les autres options pratiques, -P dit à xargs d'effectuer les commandes en parallèle (comme le -j de make). Cela a permis à l'orateur de montrer un fping refait avec un ping ordinaire, l'expansion des séquences, et xargs.

Lors de la discussion qui a suivi, j'ai soulevé la question d'Unicode dans le shell Unix. Certaines commandes gèrent bien Unicode, par exemple awk :

%  echo -n café | awk '{print length($1)}'
4
    

Il a bien indiqué la longueur en caractères, pas en octets (« café » fait cinq octets en UTF-8). wc, lui, permet d'avoir caractères ou octets au choix :

% echo -n café | wc -m
4
% echo -n café | wc -c
5      
    

Cela montre que, même si le shell a la réputation d'être un vieux truc, il a su s'adapter à la modernité. Mais ce n'est pas le cas de toutes les commandes Unix. Au moins sur mon Ubuntu (il parait que ça marche sur d'autres Unix), cut semble insensible à l'internationalisation (il affiche les octets bêtement, malgré ce que prétend sa documentation) :

% echo caféthé | cut -c 3
f
% echo caféthé | cut -c 4
�
% echo caféthé | cut -c 5
�
% echo caféthé | cut -c 6     
    

De même, l'expansion par le shell ne semble pas marcher avec les caractères non-ASCII :

% echo {a..c}
a b c
% echo {à..ç}
{à..ç}
    

(Et, oui, la variable d'environnement LC_CTYPE était bien définie, sinon, grep ou wc n'auraient pas marché.)

M. climagic a évidemment parlé de plein d'autres choses, mais je les connaissais. Et vous ?

  • Il a bien sûr cité man et ses options. Notez que man -k delete ne trouve pas la commande rm
  • Il a longuement parlé de nombreux trucs du shell comme les astuces de la commande cd (cd - vous ramène au dernier répertoire), le job control (Control-Z, fg et bg), l'expansion des intervalles (echo {a..z}, echo IMG_{3200..3300}.jpg, qui permet de faire un rsync -a -v IMG_{3200..3300}.jpg server:dir…, les fonctions (le plus bel exemple était une redéfinition de cd, pour accepter l'argument ... pour dire « remonte de deux niveaux » : function cd(){ [[ "$1" == "..." ]] && builtin cd ../.. || builtin cd $@}, la redirection des seules erreurs (command 2> file), etc.
  • Mark Krenz a évidemment consacré du temps à awk et sed (tiens, lui non plus n'a pas l'air de gérer Unicode).
  • Et enfin il a parlé de deux programmes géniaux qui méritent d'être plus connus, ngrep (comme grep mais pour le réseau), et socat (netcat en mieux).

Autre discussion à la fin de la réunion, je lui ai demandé pourquoi ne pas avoir également climagic sur le réseau social décentralisé Mastodon. Il n'est pas contre (surtout si on peut le faire depuis la ligne de commande, bien sûr, mais c'est possible avec madonctl), c'est surtout un problème de manque de temps. (À noter que climagic a existé sur identi.ca mais que cela ne lui a pas laissé de bons souvenirs.)

La vue depuis l'université : prague-from-tu.jpg

Le panneau sur la salle de réunion : climagic-room.jpg


L'article seul

RFC 8197: A SIP Response Code for Unwanted Calls

Date de publication du RFC : Juillet 2017
Auteur(s) du RFC : H. Schulzrinne (FCC)
Chemin des normes
Première rédaction de cet article le 18 juillet 2017


Voici un RFC qui n'est pas trop long à lire ou à comprendre : il ajoute un code de réponse au protocole SIP (utilisé surtout pour la téléphonie sur IP), le code 607 qui dit explicitement « je n'ai pas envie de répondre à cet appel ».

Sur certaines lignes téléphoniques, la majorité des appels entrants sont du spam (cf. RFC 5039), du genre salons de beauté ou remplacements de fenêtres, voire appels automatiques en faveur d'un candidat politicien. Toute méthode de lutte contre ce spam nécessite d'avoir un retour des utilisateurs, qui déclarent « je ne veux pas de cet appel ». Simplement raccrocher ne suffit pas, il faut permettre à l'utilisateur de déclarer explicitement la spamicité d'un appel. (Les codes de rejet existants comme 603 - RFC 3261, section 21.6.2 - ne sont pas assez spécifiques. 603 indique typiquement un rejet temporaire, du genre je suis à table, ne me dérangez pas maintenant.)

C'est désormais possible avec ce nouveau code de retour 607, enregistré à l'IANA. Le logiciel de l'utilisateur enverra ce code (il aura typiquement un bouton « rejet de ce spam » comme c'est le cas pour les logiciels de courrier), et il pourra être utilisé pour diverses techniques anti-spam (par exemple par un fournisseur SIP pour déterminer que tel numéro ne fait que du spam et peut donc être bloqué). La FCC (pour laquelle travaille l'auteur du RFC) avait beaucoup plaidé pour un tel mécanisme.

Notez que l'interprétation du code sera toujours délicate, car il peut y avoir usurpation de l'identité de l'appelant (section 6 de notre RFC, et aussi RFC 4474) ou tout simplement réaffectation d'une identité à un nouvel utilisateur, innocent de ce qu'avait fait le précédent. Au passage, dans SIP, l'identité de l'appelant peut être un URI SIP ou bien un numéro de téléphone, cf. RFC 3966.

Le code aurait dû être 666 (le nombre de la Bête) mais le code 607 a été finalement choisi pour éviter des querelles religieuses. (Le RFC 7999 n'a pas eu les mêmes pudeurs.)

L'action (ou l'inaction) suivant une réponse 607 dépendra de la politique de chaque acteur. (La discussion animée à l'IETF avait surtout porté sur ce point.) Comme toujours dans la lutte contre le spam, il faudra faire des compromis entre trop de faux positifs (on refuse des messages légitimes) et trop de faux négatifs (on accepte du spam). D'autant plus que l'utilisateur peut être malveillant (rejeter comme spam des appels légitimes dans l'espoir de faire mettre l'appelant sur une liste noire) ou simplement incompétent (il clique un peu au hasard). Les signalements sont donc à prendre avec des pincettes (section 6 du RFC).


Téléchargez le RFC 8197


L'article seul

Developing a dnstap to C-DNS converter at the IETF hackathon

First publication of this article on 17 July 2017


The weekend of 15-16 july 2017, I participated to the IETF 99 hackathon in Prague. The project was to develop a dnstap to C-DNS converter. This is a small documentation of the result and of the lessons learned.

First, a bit of background. Most DNS operators these days gather a lot of data from the DNS traffic and then analyze it. Events like DITL can fill many hard disks with pcap files. pcap not being very efficient (both in producing it, in storing it, and in analyzing it), a work is under way at the IETF to create a better format: C-DNS (for "capture DNS"). C-DNS is currently specified in an Internet-Draft, draft-ietf-dnsop-dns-capture-format. It is only a draft, not yet a RFC. One of the goals of IETF hackathons is precisely to test drafts, to see if they are reasonable, implementable, etc, before they are approved.

Note that C-DNS is based on CBOR (RFC 7049). There is already an implementation of C-DNS, available under a free software licence. Here, the idea was to write a second implementation, to test interoperability. The target was a C-DNS producer. Where to find the data to put in the file? I choosed dnstap since it is currently the best way to get data from a DNS server. dnstap relies on protocol buffers so it may be difficult to handle but there is already a good dnstap client, written in Go.

So, the decisions were:

  • Get DNS data via dnstap from a Unbound resolver (the only resolver with dnstap support at this time),
  • Use the dnstap client as a starting point (which means the hackathon project was to use Go),
  • Test the C-DNS produced with the inspector from the first implementation.

First, compiling the first C-DNS implementation, which is in C++ (I used a not-up-to-date Ubuntu machine): it requires several Boost libraries, and I'm too lazy to check exactly which, so I installed everything aptitude install libboost1.58-all-dev liblzma-dev lzma-dev lzma. The C++ capture library libtins does not appear to be in a Ubuntu "xenial" package (but there is a Debian one and it is in some Ubuntu versions). The "trusty" package is too old, we install the "yakkety" one manually:

./configure
make

Then, we can produce C-DNS files from pcaps:

./compactor/compactor -o short.cdns -c /dev/null short.pcap

And read C-DNS files:

./compactor/inspector short.cdns

So, now, we can test the produced files.

Next, compiling Unbound with dnstap support (it is not done by default, probably because it add a lot of dependencies, coming from protocol buffers):

aptitude install libprotobuf-c-dev  protobuf-c-compiler libfstrm-dev fstrm-bin
./configure --enable-dnstap
make

We configure Unbound with:

dnstap:
    dnstap-enable: yes
    dnstap-socket-path: "/var/run/unbound/dnstap.sock" # "the dnstap socket won't appear in the filesystem until the dnstap listener is started."
    dnstap-send-identity: yes
    dnstap-send-version: yes
    dnstap-log-resolver-response-messages: yes
    dnstap-log-client-query-messages: yes
    dnstap-log-resolver-query-messages: yes
    dnstap-log-client-response-messages: yes

And we start it:

./unbound/unbound -d -c dnstap.conf

Then, when processing DNS queries and responses, Unbound sends a report to the dnstap socket. We just need a dnstap client to display it.

We forked the dnstap client, created a c-dns branch, and started our copy. To compile it:

aptitude install golang-dns-dev golang-goprotobuf-dev
go get github.com/farsightsec/golang-framestream
go install github.com/bortzmeyer/golang-dnstap
go build dnstap/main.go

Then we can run it:

./main -y -u /var/run/unbound/dnstap.sock

And we can see the YAML output when Unbound receives or sends DNS messages. To store this output, I was not able to put the data in a file, so I just redirected the standard output.

Most of the hackathon work took place in the new file CdnsFormat.go. First, producing CBOR. I had a look at the various Go implementations of CBOR. Most seem old and unmaintained and CBOR seemed to me simple enough that it was faster to reimplement from scratch. This is the code at the beginning of CdnsFormat.go, the cborInteger() and similar functions. I also developed a small Go program to read CBOR files and display them as a tree, a tool which was very useful to debug my C-DNS producer. The grammar of C-DNS is specified in the CDDL language (currently specified in another Internet-Draft). A validating tool exists, taking CDDL and CBOR and telling if the CBOR file is correct, but this tool's error messages are awful. It is just good to say if the file is OK or not, afterwards, you have to examine the file manually.

Then, actually producing the C-DNS file. The C-DNS format is not simple: it is intended for performance, and harsh compression, not for ease of implementation. The basic idea is to capture in tables most of what is repeated, actual DNS messages being made mostly of references to these tables. For instance, a DNS query for www.sinodun.com won't include the string www.sinodun.com. This FQDN will be stored once, in the name-rdata table, and a numeric index to an entry of this table will be used in the message. For instance:

// Server address 
s.Write(cborInteger(0))
s.Write(cborInteger(2))  

Here, we don't write the server IP address directly, we write an index to the table where the IP address is (2 is the second value of the table, C-DNS indexes start at 1, read later about the value zero). Note that the inspector crashed when meeting indexes going outside of an array, something that Jim Hague fixed during the hackathon.

Speaking of IP addresses, "client" and "server" in the C-DNS specification don't mean "source" and "destination". For a DNS query, "client" is the source and "server" the destination but, for a DNS response, it is the opposite (dnstap works the same way, easing the conversion).

Among the things that are not clear in the current version of the draft (version -03) is the fact that CBOR maps are described with keys that are strings, while in the CDNS format, they are actually integers. So when you read in the draft:

FilePreamble = {
      major-format-version => uint, 
      minor-format-version => uint, 
      ...

Read on: FilePreamble is indeed a CBOR map but the actual keys are further away:

major-format-version = 0
minor-format-version = 1

So for a version number of 0.5 (the current one), you must write in Go the map as:

//    Major version
s.Write(cborInteger(0))
s.Write(cborInteger(0))
//    Minor version
s.Write(cborInteger(1))
s.Write(cborInteger(5)) 

Another choice to make: CBOR allow arrays and maps of indefinite of finite length. C-DNS does not specify which one to use, it is up to you, programmer, to choose. Of course, it is often the case that you don't know the length in advance, so sometimes you have no choice. An example of a finite length array:

s.Write(cborArray(3))
// Then write the three items  

And an example of an indefinite length array:

s.Write(cborArrayIndef())
// Write the items
s.Write(cborBreak())

Note that you need to checkpoint from time to time (for instance by rotating the C-DNS file and creating a new one) otherwise a crash will leave you with a corrupted file (no break at the end). The fact that C-DNS (well, CBOR, actually), requires this break code at the end forced us to modify the API of dnstap modules, to have a new function finish (of type TextFinishFunc, this function is a no-op for the other formats).

Note there is a semantic mismatch between C-DNS and dnstap: C-DNS assumes all the data is mandatory while in dnstap, almost everything is optional. That means that we have to create dummy data in some places for instance:

s.Write(cborArray(2))
if m.QueryTimeSec != nil {
        s.Write(cborInteger(int(*m.QueryTimeSec)))
} else {
        s.Write(cborInteger(0))
}

Here, if we don't have the time of the DNS message, we write the dummy value 0 (because earliest-time is not optional in the C-DNS format). One possible evolution of the C-DNS draft could be to have profiles of C-DNS, for instance a "Full" profile where everything is mandatory and a "Partial" one where missing values are allowed.

The C-DNS specification is currently ambiguous about empty arrays. Most arrays are declared, in the grammar, as allowing to be empty. It raises some issues with the current tools (the inspector crashed when meeting these empty arrays, something that Jim Hague fixed during the hackathon). But it is also unclear in its semantics. For instance, the draft says that a value zero for an index mean "not found" but it does not appear to be supported by the current tools, forcing us to invent dummy data.

dnstap formats a part of the DNS messages but not everything. To get the rest, you need to parse the binary blob sent by dnstap. We use the excellent Go DNS package):

import (
  ...
  "github.com/miekg/dns"
...  
msg = new(dns.Msg)
err := msg.Unpack(m.QueryMessage)

This allows us to get information like the QNAME (name used in the question):

qname := make([]byte, 256)
...
n, err = dns.PackDomainName(msg.Question[0].Name, qname, 0, nil, false)
...
s.Write(cborByteString(qname[0:n]))

(Unpack leaves us with a domain name in the presentation - text - format, but C-DNS requires the label - binary - format, which we do with PackDomainName.) Domain names are defined as CBOR byte string and not strings 1) because you cannot be sure of their encoding (strings have to be UTF-8) 2) and for consistency reasons because the same tables can store other things than domain names.

OK, now, we have everything, compile again and run (there is a Unbound running in another window):

go build dnstap/main.go
./main -c -u /var/run/unbound/dnstap.sock > test.cdns
../compactor/inspector   test.cdns  

And the inspector produced a pcap file, as well as a report. Let's try the pcap:

% tcpdump -n -r test.cdns.pcap
reading from file test.cdns.pcap, link-type EN10MB (Ethernet)
13:02:02.000000 IP6 ::1.51834 > ::.0: UDP, length 25
13:02:02.000000 IP 0.0.0.0.0 > 193.0.14.129.53: 666 SOA? 142.com. (25)
13:02:02.000000 IP 193.0.14.129.53 > 0.0.0.0.0: 666- [0q] 0/0/0 (12)
13:02:02.000000 IP6 ::.0 > 2001:503:83eb::30.53: 666 AAAA? ns2.bodis.com. (31)
13:02:02.000000 IP 0.0.0.0.0 > 192.41.162.30.53: 666 AAAA? ns1.bodis.com. (31)
13:02:02.000000 IP6 2001:503:83eb::30.53 > ::.0: 666- [0q] 0/0/0 (12)

You immediately note the missing data. Most of the times, it was the lack of this data in dnstap (the IP addresses, for instance), sometimes it was my own lazyness (the microseconds in the time).

Sometimes, there are things that are available in dnstap but C-DNS offers no way to store them. This is the case with the bailiwick (the zone from which the DNS responses came) or with the fact thata the resolver cache experienced a hit or a miss.

This was the end of the IETF 99 hackathon. The code is available. An example C-DNS file produced by my converter is attached here. Other things would of course be possible:

  • The biggest limit of the current dnstap-to-C-DNS tool is that it uses one C-DNS block per DNS message. This is simpler for the lazy programmer, but it defeats the most important requirments of C-DNS: compression. Compression is done py putting in tables information which is common to several DNS messages. This is the obvious next step.
  • Test with an authoritative name server like Knot (which has dnstap support).

Thanks to Jim Hague for working with me throughout the hackathon (and fixing many things, and answering many questions) and to Sara Dickinson.


L'article seul

RFC 8200: Internet Protocol, Version 6 (IPv6) Specification

Date de publication du RFC : Juillet 2017
Auteur(s) du RFC : S. Deering (Retired), R. Hinden (Check Point Software)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF 6man
Première rédaction de cet article le 16 juillet 2017


Ce RFC est la nouvelle norme du protocole IPv6. IP est le protocole de base de l'Internet, la version 6 y est minoritaire mais est bien plus répandue qu'elle ne l'était lors de la sortie de la précédente norme, qui était le RFC 2460, norme que notre nouveau RFC remplace (et cela fait passer IPv6 au statut de norme Internet, la précédente étant officiellement une proposition de norme).

Pas de changements cruciaux, la norme est nouvelle, mais le protocole reste largement le même. Ce RFC 8200 continue à présenter IPv6 comme « a new version of the Internet Protocol (IP) ». Comme la première norme IPv6 est sortie en 1995, l'adjectif « new » n'est vraiment pas sérieux. Comme, malheureusement, la plupart des formations réseau ne parlent que d'IPv4 et traitent IPv6 de manière bâclée à la fin, le RFC présente IPv6 en parlant de ses différences par rapport à IPv4 (section 1 du RFC) :

  • La principale est évidemment l'espace d'adressage bien plus grand. Alors qu'IPv4, avec ses adresses sur 32 bits, ne peut même pas donner une adresse IP à chaque habitant de la planète (ce qui serait déjà insuffisant), IPv6, avec ses 128 bits d'adresse, permet de distribuer une quantité d'adresses que le cerveau humain a du mal à se représenter. Cette abondance est la principale raison pour laquelle il est crucial de migrer vers IPv6. Les autres raisons sont plus discutables.
  • IPv6 a sérieusement changé le format des options. En IPv4, les options IP étaient un champ de longueur variable dans l'en-tête, pas exactement ce qui est le plus facile à analyser pour un routeur. Le RFC dit qu'IPv6 a simplifié le format mais c'est contestable : une complexité a succédé à une autre. Désormais, le premier en-tête est de taille fixe, mais il peut y avoir un nombre quelconque d'en-têtes chaînés. Le RFC utilise malheureusement des termes publicitaires assez déconnectés de la réalité, en parlant de format plus efficace et plus souple.
  • IPv6 a un mécanisme standard pour étiqueter les paquets appartenant à un même flot de données. Mais le RFC oublie de dire qu'il semble inutilisé en pratique.
  • Et le RFC termine cette énumération par le plus gros pipeautage, prétendre qu'IPv6 aurait de meilleurs capacités d'authentification et de confidentialité. (C'est faux, elles sont les mêmes qu'en IPv4, et peu déployées, en pratique.)

Notez que ce RFC 8200 ne spécifie que le format des paquets IPv6. D'autres points très importants sont normalisés dans d'autres RFC, les adresses dans le RFC 4291, et ICMP dans le RFC 4443.

La section 3 présente le format des paquets IPv6 :

   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Version| Traffic Class |           Flow Label                  |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         Payload Length        |  Next Header  |   Hop Limit   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   +                                                               +
   |                                                               |
   +                         Source Address                        +
   |                                                               |
   +                                                               +
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   +                                                               +
   |                                                               |
   +                      Destination Address                      +
   |                                                               |
   +                                                               +
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    

(D'ailleurs, si quelqu'un sait pourquoi l'adresse IP source est avant la destination ? Il me semblerait plus logique que ce soit l'inverse, puisque tous les routeurs sur le trajet doivent examiner l'adresse destination, alors qu'on n'a pas toujours besoin de l'adresse source.) Le numéro de version vaut évidemment 6, le « traffic class » est présenté en section 7, le « flow label » en section 6.

Le champ « Next header » remplace le « Protocol » d'IPv4. Il peut indiquer le protocole de transport utilisé (la liste figure dans un registre IANA et est la même pour IPv4 et IPv6) mais aussi les en-têtes d'extension, une nouveauté d'IPv6, présentée en section 4 de notre RFC.

Le champ « Hop limit » remplace le « Time to Live » d'IPv4. En fait, les deux ont exactement la même sémantique, qui est celle d'un nombre maximal de routeurs utilisés sur le trajet (le but est d'éviter les boucles infinies dans le réseau). Autrefois, dans IPv4, il était prévu que ce champ soit réellement une durée, mais aucune mise en œuvre d'IPv4 ne l'avait jamais utilisé comme ceci. Le renommage dans IPv6 fait donc correspondre la terminologie avec une réalité ancienne (cf. aussi la section 8.2). Notez que c'est ce champ qui est utilisé par traceroute.

Voici une simple connexion HTTP en IPv6, vue avec tcpdump et tshark. Le client a demandé /robots.txt et a obtenu une réponse négative (404). Si vous voulez, le pcap complet est ipv6-http-connection.pcap. Voici d'abord avec tcpdump avec ses options par défaut :

15:46:21.768536 IP6 2001:4b98:dc2:43:216:3eff:fea9:41a.37703 > 2605:4500:2:245b::42.80: Flags [S], seq 3053097848, win 28800, options [mss 1440,sackOK,TS val 1963872568 ecr 0,nop,wscale 7], length 0
    

On voit les deux adresses IPv6, tcpdump n'affiche rien d'autre de l'en-tête de couche 3, tout le reste est du TCP, le protocole de transport utilisé par HTTP. Avec l'option -v de tcpdump :

15:46:21.768536 IP6 (hlim 64, next-header TCP (6) payload length: 40) 2001:4b98:dc2:43:216:3eff:fea9:41a.37703 > 2605:4500:2:245b::42.80: Flags [S] [...]
    

Cette fois, on voit le « Hop limit » (64), l'en-tête suivant (TCP, pas d'en-tête d'extension) et la longueur (40 octets). Pour avoir davantage, il faut passer à tshark (décodage complet en ipv6-http-connection.txt) :

Internet Protocol Version 6, Src: 2001:4b98:dc2:43:216:3eff:fea9:41a, Dst: 2605:4500:2:245b::42
    0110 .... = Version: 6
    .... 0000 0000 .... .... .... .... .... = Traffic class: 0x00 (DSCP: CS0, ECN: Not-ECT)
        .... 0000 00.. .... .... .... .... .... = Differentiated Services Codepoint: Default (0)
        .... .... ..00 .... .... .... .... .... = Explicit Congestion Notification: Not ECN-Capable Transport (0)
    .... .... .... 0000 0000 0000 0000 0000 = Flow label: 0x00000
    Payload length: 40
    Next header: TCP (6)
    Hop limit: 64
    Source: 2001:4b98:dc2:43:216:3eff:fea9:41a
    [Source SA MAC: Xensourc_a9:04:1a (00:16:3e:a9:04:1a)]
    Destination: 2605:4500:2:245b::42
    [Source GeoIP: France]
        [Source GeoIP Country: France]
    [Destination GeoIP: United States]
        [Destination GeoIP Country: United States]
    

On a cette fois tout l'en-tête IPv6 : notons le « Flow Label » (j'avais bien dit que peu de gens s'en servaient, il est nul dans ce cas).

La section 4 de notre RFC est dédiée à une nouveauté d'IPv6, les en-têtes d'extension. Au lieu d'un champ « Options » de taille variable (donc difficile à analyser) comme en IPv4, IPv6 met les options IP dans des en-têtes supplémentaires, chaînés avec l'en-tête principal. Par exemple, si un paquet UDP a un en-tête « Destination Options », le champ « Next header » de l'en-tête principal vaudra 60 (pour « Destination Options »), il sera suivi de l'en-tête d'extension « Destination Options » qui aura, lui, un « Next header » de 17 pour indiquer que ce qui suit est de l'UDP. (Je rappelle que les valeurs possibles pour « Next Header » sont dans un registre IANA.) Il peut y avoir zéro, un ou davantage d'en-têtes d'extension chaînés entre eux.

Notez qu'analyser cette chaîne d'en-têtes est compliqué car les en-têtes n'ont pas tous le même format (le RFC 6564 a créé un format unique, mais cela ne concerne que les futurs en-têtes.) Il est donc exagéré de dire que la suppression du champ « Options » de taille variable a simplifié les choses.

Les différents en-têtes ne sont pas tous traités pareillement par les routeurs. Il existe notamment un en-tête, « Hop-by-hop Options » qui doit être examiné par tous les routeurs du trajet (cette obligation, jamais respectée, a de toute façon été relâchée par rapport au RFC 2460). C'est pour cela qu'il doit être placé au début des en-têtes, juste après l'en-tête principal. Les autres en-têtes d'extension doivent être ignorés par les routeurs.

Comme il est compliqué de rajouter un nouveau modèle d'en-tête (il faudrait modifier toutes les machines IPv6), une solution légère existe pour les options simples : utiliser les en-têtes d'options, « Hop-by-hop Options » et « Destination Options ». Tous les deux sont composés d'une série d'options encodées en TLV. En outre, le type de l'option indique, dans ses deux premiers bits, le traitement à appliquer au paquet si le système ne connait pas cette option. Si les deux premiers bits sont à zéro, on ignore l'option et on continue. Autrement, on jette le paquet (les trois valeurs restantes, 01, 10 et 11, indiquent si on envoie un message d'erreur ICMP et lequel). Ainsi, l'option pour la destination de numéro 0x07 (utilisée par le protocole de sécurité du RFC 5570) est facultative : elle a les deux premiers bits à zéro et sera donc ignorée silencieusement par les destinataires qui ne la connaissent pas (cf. registre IANA.)

« Destination Options », comme son nom l'indique, n'est examinée que par la machine de destination. Si vous voulez envoyer des paquets avec cet en-tête, regardez mon article.

Outre les en-têtes « Hop-by-hop Options » et « Destination Options », il existe des en-têtes :

  • « Routing Header », qui permet de spécifier le routage depuis la source (un peu comme les options de « source routing » en IPv4). Il y a plusieurs types, et les valeurs possibles sont dans un registre IANA, géré suivant les principes du RFC 5871. (Le type 0 a été retiré depuis le RFC 5095.)
  • « Fragment Header », qui permet de fragmenter les paquets de taille supérieure à la MTU.
  • Les autres en-têtes sont ceux utilisés par IPsec, décrits dans les RFC 4302 et RFC 4303.

La fragmentation est différente de celle d'IPv4. En IPv6, seule la machine émettrice peut fragmenter, pas les routeurs intermédiaires. Si le paquet est plus grand que la MTU, on le découpe en fragments, chaque fragment portant un « Fragment Header ». Cet en-tête porte une identification (un nombre sur 32 bits qui doit être unique parmi les paquets qui sont encore dans le réseau), un décalage (offset) qui indique à combien d'octets depuis le début du paquet original se situe ce fragment (il vaut donc zéro pour le premier fragment) et un bit qui indique si ce fragment est le dernier. À la destination, le paquet est réassemblé à partir des fragments. (Il est désormais interdit que ces fragments se recouvrent, cf. RFC 5722.) Voici un exemple de fragmentation. La sonde Atlas n° 6271 a interrogé un serveur DNS de la racine Yeti avec le type de question ANY qui signifie « envoie-moi tout ce que tu peux / veux ». La réponse, plus grande que la MTU (plus de quatre kilo-octets !), a été fragmentée en trois paquets (le pcap complet est en ipv6-dns-frag.pcap) :

16:14:27.112945 IP6 2001:67c:217c:4::2.60115 > 2001:4b98:dc2:45:216:3eff:fe4b:8c5b.53: 19997+ [1au] ANY? . (28)
16:14:27.113171 IP6 2001:4b98:dc2:45:216:3eff:fe4b:8c5b > 2001:67c:217c:4::2: frag (0|1232) 53 > 60115: 19997*- 35/0/1 NS bii.dns-lab.net., NS yeti.bofh.priv.at., NS yeti.ipv6.ernet.in., NS yeti.aquaray.com., NS yeti.mind-dns.nl., NS dahu1.yeti.eu.org., NS dahu2.yeti.eu.org., NS yeti1.ipv6.ernet.in., NS ns-yeti.bondis.org., NS yeti-ns.ix.ru., NS yeti-ns.lab.nic.cl., NS yeti-ns.tisf.net., NS yeti-ns.wide.ad.jp., NS yeti-ns.conit.co., NS yeti-ns.datev.net., NS yeti-ns.switch.ch., NS yeti-ns.as59715.net., NS yeti-ns1.dns-lab.net., NS yeti-ns2.dns-lab.net., NS yeti-ns3.dns-lab.net., NS xn--r2bi1c.xn--h2bv6c0a.xn--h2brj9c., NS yeti-dns01.dnsworkshop.org., NS yeti-dns02.dnsworkshop.org., NS 3f79bb7b435b05321651daefd374cd.yeti-dns.net., NS ca978112ca1bbdcafac231b39a23dc.yeti-dns.net., RRSIG, NSEC, RRSIG[|domain]
16:14:27.113187 IP6 2001:4b98:dc2:45:216:3eff:fe4b:8c5b > 2001:67c:217c:4::2: frag (1232|1232)
16:14:27.113189 IP6 2001:4b98:dc2:45:216:3eff:fe4b:8c5b > 2001:67c:217c:4::2: frag (2464|637)
    

On note que tcpdump n'a interprété qu'un seul fragment comme étant du DNS, la premier, puisque c'était le seul qui portait l'en-tête UDP, avec le numéro de port 53 identifiant du DNS. Dans le résultat de tcpdump, après le mot-clé frag, on voit le décalage du fragment par rapport au début du paquet original (respectivement 0, 1232 et 2464 pour les trois fragments), et la taille du fragment (respectivement 1232, 1232 et 637 octets). Vu par tshark (l'analyse complète est en ipv6-dns-frag.txt), le premier fragment contient :

    
    Type: IPv6 (0x86dd)
Internet Protocol Version 6, Src: 2001:4b98:dc2:45:216:3eff:fe4b:8c5b, Dst: 2001:67c:217c:4::2
    Payload length: 1240
    Next header: Fragment Header for IPv6 (44)
    Fragment Header for IPv6
        Next header: UDP (17)
        Reserved octet: 0x00
        0000 0000 0000 0... = Offset: 0 (0 bytes)
        .... .... .... .00. = Reserved bits: 0
        .... .... .... ...1 = More Fragments: Yes
        Identification: 0xcbf66a8a
Data (1232 bytes)

On note le « Next Header » qui indique qu'un en-tête d'extension, l'en-tête « Fragmentation », suit l'en-tête principal. Le bit M (« More Fragments ») est à 1 (ce n'est que le premier fragment, d'autres suivent), le décalage (« offset ») est bien sûr de zéro. L'identificateur du paquet est de 0xcbf66a8a. Le dernier fragment, lui, contient :

Internet Protocol Version 6, Src: 2001:4b98:dc2:45:216:3eff:fe4b:8c5b, Dst: 2001:67c:217c:4::2
    Payload length: 645
    Next header: Fragment Header for IPv6 (44)
    Fragment Header for IPv6
        Next header: UDP (17)
        Reserved octet: 0x00
        0000 1001 1010 0... = Offset: 308 (2464 bytes)
        .... .... .... .00. = Reserved bits: 0
        .... .... .... ...0 = More Fragments: No
        Identification: 0xcbf66a8a

Cette fois, le « Next Header » indique que c'est de l'UDP qui suit, le décalage est de 2464, et le bit M est à zéro (plus d'autres fragments). L'identificateur est le même, c'est celui du paquet original, et c'est grâce à lui que la machine de destination saura réassembler les fragments. Notez qu'à la fin de l'analyse par tshark figure un réassemblage complet du paquet, ce qui permet une analyse DNS complète.

Et si je ne suis pas satisfait des en-têtes d'extension existants et que je veux créer le mien ? C'est en général une mauvaise idée. La plupart des cas concrets devraient être résolus avec un des en-têtes déjà normalisés. Notamment, l'en-tête « Destination Options » est là pour la majorité des situations. C'est lui qu'il faut regarder en premier (ce qu'ont fait les RFC 6744, RFC 6788 ou RFC 7837, la liste complète figurant dans un registre IANA). Notre RFC 8200 exige donc que, si vous tenez à créer un nouvel en-tête, vous expliquiez bien pourquoi c'est indispensable, et pourquoi aucun des en-têtes existants ne convient.

Il est également déconseillé de créer de nouvelles options « hop-by-hop » (la liste actuelle est à l'IANA), car ces options doivent être traitées par tous les routeurs du trajet. Il y a un risque sérieux qu'ils laissent tomber les paquets ayant cet en-tête « Hop-by-hop Options », ou bien qu'ils le traitent plus lentement (logiciel du routeur et pas circuits matériels spécialisés). Là aussi, il faudra donc une justification sérieuse.

La section 5 de notre RFC se penche sur un problème délicat, la taille des paquets. IPv6 exige une MTU d'au moins 1 280 octets, mais les paquets sont souvent plus grands (par exemple 1 500 octets si on part d'un Ethernet). Beaucoup de liens ont, en pratique, une MTU de 1 500 octets mais IPv6 étant souvent porté par des tunnels (en raison de l'immobilisme de beaucoup de FAI, qui ne déploient toujours pas IPv6), un certain nombre de liens offrent moins de 1 500 octets. Normalement, l'émetteur d'un paquet IPv6 doit faire de la découverte de la MTU du chemin (RFC 8201) afin de pouvoir fragmenter, si nécessaire. Une mise en œuvre « paresseuse » d'IPv6 pourrait se dispenser de découverte de la MTU du chemin, et se limiter à 1 280 octets par paquet.

Le problème est que la découverte de la MTU du chemin dépend du bon fonctionnement d'ICMP. L'émetteur d'un paquet doit pouvoir recevoir les paquets ICMP « Packet too big » (RFC 4443, section 3.2). Or, un certain nombre de pare-feux, stupidement configurés par des amateurs, bloquent tout ICMP « pour des raisons de sécurité » (c'est d'ailleurs une bonne question pour les entretiens d'embauche d'un administrateur de réseaux : « filtrer ICMP en entrée ou pas ? » S'il répond Oui, on est sûr qu'il est incompétent.) Ne recevant pas les « Packet too big », l'émetteur risque de croire à tort que ses paquets sont passés. En outre, si l'émetteur décide de fragmenter (en général, quand la MTU du chemin est inférieure à la sienne, avec TCP, on réduit la MSS, avec UDP, on fragmente), il faut que les fragments passent à travers le réseau. Et, là encore, un certain nombre de pare-feux bêtement configurés bloquent les fragments. Donc, en pratique, découverte du chemin + fragmentation est un processus fragile, en raison de ce véritable sabotage par des middleboxes.

C'est certainement le plus gros problème pratique lors du déploiement d'IPv6. On peut même penser à prendre des mesures radicales et coûteuses, comme d'abaisser la MTU à 1 280 octets pour être tranquille. Moins violent, il est fréquent de voir des MTU à 1 480 octets. Voici par exemple la configuration de mon routeur Turris Omnia pour passer par l'IPv6 de Free (un tunnel) :

config interface 'lan'
	option ip6assign '64'
	option ip6addr '2001:db8:42::1:fe/64'
	option ip6prefix '2001:db8:42::/64'
	option ip6gw '2001:db8:42::1'
	option mtu '1480'
    

Et le « flow label », dont j'avais parlé plus haut ? Il est décrit dans la (très courte) section 6, qui renvoie surtout au RFC 6437. En pratique, ce champ semble peu utilisé comme on l'a vu dans l'exemple décodé par tshark.

Même chose pour le « traffic class », en section 7 : pour son utilisation pour la différenciation de trafic, voir les RFC 2474 et RFC 3168.

Maintenant qu'IPv6, protocole de couche 3, a été bien défini, le RFC monte vers la couche 4, en consacrant sa section 8 aux problèmes des couches supérieures. Cela concerne notamment la somme de contrôle. Si vous avez fait attention au schéma de l'en-tête IPv6 de la section 3, vous avez noté qu'il n'y avait pas de champ « Header Checksum », contrairement à ce qui existait en IPv4. En IPv6, pas de somme de contrôle en couche 3, c'était une tâche supplémentaire pour les routeurs (pour citer le RFC 791, « Since some header fields change (e.g., time to live), this is recomputed and verified at each point that the internet header is processed. »), tâche dont ils sont désormais dispensés.

Par contre, la somme de contrôle existe pour les en-têtes de couche 4 et elle devient même obligatoire pour UDP (elle était facultative en IPv4, quoique très fortement recommandée). (Voir le RFC 1071, au sujet de cette somme de contrôle.)

Un gros changement de ce RFC par rapport à son prédécesseur, le RFC 2460, concerne la sécurité. La section sur la sécurité est passée d'une annexe de deux lignes (qui se contentait de passer le bébé à IPsec) à une analyse plus détaillée (section 10 du RFC). La question est délicate car la sécurité d'IPv6 a souvent fait l'objet de FUD, visant à justifier l'immobilisme de pas mal d'acteurs. Il est évidemment plus valorisant de dire « nous ne migrons pas vers IPv6 pour des raisons de sécurité » que de reconnaitre « nous sommes trop flemmards et paralysés par la peur du changement ». (Cf. mon exposé sur la sécurité d'IPv6 à l'ESGI.) S'il fallait synthétiser (la deuxième partie de cette synthèse ne figure pas dans le RFC), je dirais :

  • La sécurité d'IPv6 est quasiment la même qu'en IPv4 (ce qui est logique, il ne s'agit après tout que de deux versions du même protocole). Les grandes questions de sécurité d'IPv4 (usurpation d'adresse source, relative facilité à faire des attaques par déni de service, pas d'authentification ou de confidentialité par défaut) sont exactement les mêmes en IPv6. (C'est une blague courante à l'IETF de dire que si IPv4, l'un des plus grands succès de l'ingéniérie du vingtième siècle, était présenté à l'IESG aujourd'hui, il serait rejeté pour sa trop faible sécurité.)
  • Par contre, en pratique, les solutions techniques d'attaque et de défense, ainsi que les compétences des attaquants et des défenseurs, sont bien plus faibles en IPv6. Pas mal de logiciels « de sécurité » ne gèrent pas IPv6, bien des logiciels de piratage ne fonctionnent qu'en IPv4, les administrateurs système sont déroutés face à une attaque IPv6, et les pirates ne pensent pas à l'utiliser. (Faites l'expérience : configurez un pot de miel SSH en IPv4 et IPv6. Vous aurez plusieurs connexions par jour en IPv4 et jamais une seule en IPv6.) L'un dans l'autre, je pense que ces deux aspects s'équilibrent.

Bon, assez de stratégie, passons maintenant aux problèmes concrets que décrit cette section 10. Elle rappelle des risques de sécurité qui sont exactement les mêmes qu'en IPv4 mais qu'il est bon de garder en tête, ce sont des problèmes fondamentaux d'IP :

  • Communication en clair par défaut, donc espionnage trop facile pour les États, les entreprises privées, les pirates individuels. (Cf. Snowden.)
  • Possibilité de rejouer des paquets déjà passés, ce qui permet certaines attaques. Dans certains cas, on peut modifier le paquet avant de le rejouer, et ça passera.
  • Possibilité de générer des faux paquets et de les injecter dans le réseau.
  • Attaque de l'homme du milieu : une entité se fait passer pour l'émetteur auprès du vrai destinataire, et pour le destinataire auprès du vrai émetteur.
  • Attaque par déni de service, une des plaies les plus pénibles de l'Internet.

En cohérence avec le RFC 2460 (mais pas avec la réalité du terrain), notre RFC recommande IPsec (RFC 4301) comme solution à la plupart de ces problèmes. Hélas, depuis le temps qu'il existe, ce protocole n'a jamais connu de déploiement significatif sur l'Internet public (il est par contre utilisé dans des réseaux privés, par exemple le VPN qui vous permet de vous connecter avec votre entreprise de l'extérieur utilise probablement une variante plus ou moins standard d'IPsec). Une des raisons de ce faible déploiement est la grande complexité d'IPsec, et la complexité pire encore de ses mises en œuvre. En pratique, même si le RFC ne le reconnait que du bout des lèvres, ce sont les protocoles applicatifs comme SSH ou TLS, qui sécurisent l'Internet.

Pour les attaques par déni de service, par contre, aucune solution n'est proposée : le problème ne peut pas forcément se traiter au niveau du protocole réseau.

La différence la plus spectaculaire entre IPv4 et IPv6 est évidemment la taille des adresses. Elle rend le balayage bien plus complexe (mais pas impossible), ce qui améliore la sécurité (l'Internet IPv4 peut être exploré incroyablement vite, par exemple avec masscan, et, si on est trop flemmard pour balayer soi-même, on peut utiliser des balayages déjà faits, par exemple par Shodan ou https://scans.io/). Le RFC 7707 fait une très bonne synthèse de l'état de l'art en matière de balayage IPv6. Par exemple, Shodan, cité plus haut, doit utiliser des techniques assez douteuses pour récolter des adresses IPv6 à examiner.

Et qu'en est-il de la vie privée ? L'argument, largement FUDé, a été beaucoup utilisé contre IPv6. Le RFC note qu'IPv6 a entre autre pour but de rendre inutile l'usage du NAT, dont certaines personnes prétendent qu'il protège un peu les utilisateurs. L'argument est largement faux : le NAT (qui est une réponse à la pénurie d'adresses IPv4, pas une technique de sécurité) ne protège pas contre tout fingerprinting, loin de là. Et, si on veut empêcher les adresses IP des machines du réseau local d'être visibles à l'extérieur, on peut toujours faire du NAT en IPv6, si on veut, ou bien utiliser des méthodes des couches supérieures (comme un relais).

Autre question de vie privée avec IPv6, les adresses IP fondées sur l'adresse MAC. Cette ancienne technique, trop indiscrète, a été abandonnée avec les RFC 4941 et RFC 7721, le premier étant très déployé. Il est curieux de constater que cet argument soit encore utilisé, alors qu'il a perdu l'essentiel de sa (faible) pertinence.

Mais il y avait bien des problèmes de sécurité concrets avec le précédent RFC 2460, et qui sont réparés par ce nouveau RFC :

  • Les « fragments atomiques » ne doivent désormais plus être générés (RFC 8021) et, si on en reçoit, doivent être « réassemblés » par une procédure spéciale (RFC 6946).
  • Les fragments qui se recouvrent partiellement sont désormais interdits (cf. RFC 5722, le réassemblage des fragments en un paquet est déjà assez compliqué et sujet à erreur comme cela).
  • Si le paquet est fragmenté, les en-têtes d'extension doivent désormais tous être dans le premier fragment (RFC 7112).
  • L'en-tête de routage (Routing Header) de type 0, dit « RH0 », est abandonné, il posait trop de problèmes de sécurité (cf. RFC 5095 et RFC 5871).

L'annexe B de notre RFC résume les changements depuis le RFC 2460. Pas de choses révolutionnaires, les changements les plus importantes portaient sur la sécurité, comme listé un peu plus haut (section 10 du RFC). On notera comme changements :

  • [Section 1] Clarification que IPv6 est gros-boutien, comme IPv4 (cf. l'annexe B du RFC 791).
  • [Section 3] Clarification de l'utilisation du « hop limit », décrémenté de 1 à chaque routeur.
  • [Section 4] Clarification du fait qu'un équipement intermédiaire ne doit pas tripoter les en-têtes d'extension (à part évidemment « Hop-by-hop Options »), ni les modifier, ni en ajouter ou retirer, ni même les lire.
  • [Section 4] Le traitement de l'en-tête « Hop-by-hop Options par les routeurs sur le trajet n'est plus obligatoire. Si un routeur est pressé, il peut s'en passer (le RFC suit ainsi la pratique).
  • [Section 4.4] Suppression de l'en-tête de routage de type 0.
  • [Section 4.5] Pas mal de changements sur la fragmentation (un processus toujours fragile !), notamment l'abandon des fragments atomiques.
  • [Section 4.8] Intégration du format unique des éventuels futurs en-têtes d'extension, format initialement présenté dans le RFC 6564.
  • [Section 6] Reconnaissance du fait que « Flow Label » n'était pas réellement bien défini, et délégation de cette définition au RFC 6437.
  • [Section 8.1] Autorisation, dans certains cas bien délimités, d'omission de la somme de contrôle UDP (RFC 6935).
  • Il y a eu également correction de diverses erreurs comme les 2541 (omission sur la définition du « Flow Label ») et 4279 (paquet entrant avec un « Hop Limit » déjà à zéro).

Le projet de mise à jour de la norme IPv6 avait été lancé en 2015 (voici les supports du premier exposé).

D'habitude, je termine mes articles sur les RFC par des informations sur l'état de mise en œuvre du RFC. Mais, ici, il y en a tellement que je vous renvoie plutôt à cette liste. Notez que des tests d'interopérabilité ont été faits sur les modifications introduites par ce nouveau RFC et que les résultats publiés n'indiquent pas de problème.

Parmi les publications récentes sur le déploiement d'IPv6, signalons :


Téléchargez le RFC 8200


L'article seul

RFC 8201: Path MTU Discovery for IP version 6

Date de publication du RFC : Juillet 2017
Auteur(s) du RFC : J. McCann (Digital Equipment Corporation), S. Deering (Retired), J. Mogul (Digital Equipment Corporation), R. Hinden (Check Point Software)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF 6man
Première rédaction de cet article le 16 juillet 2017


Ce RFC est l'adaptation à IPv6 du protocole décrit dans le RFC 1191, protocole qui permet de découvrir la MTU du chemin entre deux machines reliées par Internet. Il remplace le RFC 1981.

Avec IPv4, déterminer la MTU maximale du chemin est très utile pour optimiser les performances. Mais elle devient presque indispensable en IPv6, où les routeurs n'ont pas le droit de fragmenter les paquets trop gros (toute fragmentation doit être faite dans la machine de départ). PMTU fait donc quasi-obligatoirement partie d'IPv6. Une alternative est de n'envoyer que des paquets suffisamment petits pour passer partout. C'est moins efficace (on ne tirera pas profit des MTU plus importantes), mais ça peut être intéressant pour des mises en œuvre légères d'IPv6, par exemple pour des objets connectés. (La section 4 du RFC précise en effet que mettre en œuvre cette découverte de la MTU du chemin n'est pas obligatoire.)

L'algorithme (section 3 de notre RFC) est le même qu'en v4, envoyer des paquets, et voir si on reçoit des paquets ICMP Packet too big (RFC 4443, section 3.2) qui contiennent en général la MTU maximale du lien suivant. (Pas de bit DF - Don't fragment - en IPv6 puisque la fragmentation n'est possible qu'à la source. L'annexe A détaille les différences avec l'algorithme IPv4 décrit dans le RFC 1191.)

Voici par exemple un de ces paquets ICMP, vu par tcpdump (déclenché par une commande ping -s 1500 …) :

18:18:44.813927 IP6 2a02:8400:0:3::a > 2605:4500:2:245b::bad:dcaf: ICMP6, packet too big, mtu 1450, length 1240

Il ne faut pas s'arrêter au premier paquet Packet too big reçu, car il peut y avoir des liens réseau avec une MTU encore plus basse plus loin. En outre, la MTU du chemin change avec le temps et il faut donc rester vigilant, on peut recevoir des Packet too big si le trafic passe désormais par un lien de MTU plus faible que la précédente MTU du chemin. Plus délicat, il faut aussi de temps en temps réessayer avec des paquets plus gros, pour voir si la MTU du chemin n'aurait pas augmenté. Comme cela va faire perdre des paquets, s'il n'y a pas eu d'augmentation, il ne faut pas le faire trop souvent. (Dix minutes, dit la section 5.)

La section 4 du RFC précise les exigences qu'il normalise. (Je donne quelques exemples de leur mise en œuvre dans le noyau Linux, dans net/ipv6/route.c.) Par exemple, le nœud IPv6 doit vérifier le contenu des paquets ICMP Packet Too Big qu'il reçoit (le Message Body, cf. RFC 4443, où le routeur émetteur est censé placer des données du paquet original), pour éviter de croire des faux messages ICMP envoyés par un attaquant (rien n'authentifie un paquet ICMP, à part son contenu).

Nouveauté de ce RFC, par rapport à son prédecesseur, un nœud IPv6 doit ignorer les messages Packet Too Big indiquant une MTU inférieur aux 1 280 octets minimum d'IPv6 (le RFC 8021 explique pourquoi). C'est le but de l'instruction mtu = max_t(u32, mtu, IPV6_MIN_MTU); dans le code du noyau Linux, qui ne garde pas la MTU indiquée si elle est inférieur au minimum.

Et la machine IPv6 ne doit jamais augmenter la MTU du chemin suite à la réception d'un Packet Too Big indiquant une MTU supérieure à l'actuelle. Un tel message ne peut être qu'une erreur ou une attaque. Voir les commentaires au début de la fonction rt6_mtu_change_route dans le code Linux.

Comme avec IPv4, l'algorithme PMTU marche mal en pratique car beaucoup de sites filtrent stupidement tous les paquets ICMP et la machine qui tente de faire de la Path MTU discovery n'aura jamais de réponse. IPv6, comme IPv4, devra donc plutôt utiliser la technique du RFC 4821.

La section 5 du RFC rassemble diverses questions de mise en œuvre de cette découverte de la MTU du chemin. D'abord, il y a l'interaction entre IP (couche 3) et les protocoles au dessus, comme TCP mais aussi des protocoles utilisant UDP, comme le DNS. Ces protocoles de transport ont une MMS_S (maximum send transport-message size, voir le RFC 1122) qui est à l'origine la MTU moins la taille des en-têtes. Les protocoles de transport peuvent diminuer cette MMS_S pour ne pas avoir à faire de fragmentation.

Pour le DNS, par exemple, on peut diminuer l'usage de la découverte de la MTU du chemin en diminuant la taille EDNS. Par exemple, avec le serveur nsd, on mettrait :

ipv6-edns-size: 1460

Et, avec 1 460 octets possibles, on serait alors raisonnablement sûr de ne pas avoir de fragmentation donc, même si la découverte de la MTU du chemin marche mal, tout ira bien.

Une fois la découverte de la MTU du chemin faite, où faut-il garder l'information, afin d'éviter de recommencer cette découverte à chaque fois ? Idéalement, cette information est valable pour un chemin donné : si le chemin change (ou, a fortiori, si la destination change), il faudra découvrir à nouveau. Mais la machine finale ne connait pas en général tout le chemin suivi (on ne fait pas un traceroute à chaque connexion). Le RFC suggère donc (section 5.2) de stocker une MTU du chemin par couple {destination, interface}. Si on n'a qu'une seule interface réseau, ce qui est le cas de la plupart des machines terminales, on stocke la MTU du chemin par destination et on maintient donc un état pour chaque destination (même quand on ne lui parle qu'en UDP, qui n'a normalement pas d'état). Les deux exemples suivants concernent une destination où la MTU du chemin est de 1 450 octets. Pour afficher cette information sur Linux pour une adresse de destination :

% ip -6  route get 2a02:8428:46c:5801::4
2a02:8428:46c:5801::4 from :: via 2001:67c:1348:7::1 dev eth0 src 2001:67c:1348:7::86:133 metric 0 
    cache  expires 366sec mtu 1450 pref medium

S'il n'y a pas de PMTU indiquée, c'est que le cache a expiré, réessayez (en UDP, car TCP se fie à la MSS). Notez que, tant qu'on n'a pas reçu un seul Packet Too Big, il n'y a pas de MTU affichée. Donc, pour la plupart des destinations (celles joignables avec comme PMTU la MTU d'Ethernet), vous ne verrez rien. Sur FreeBSD, cet affichage peut se faire avec :

% sysctl -o net.inet.tcp.hostcache.list | fgrep 2a02:8428:46c:5801::4
2a02:8428:46c:5801::4  1450        0     23ms     34ms         0  4388        0 0   23    4 3600

Pour d'autres systèmes, je vous recommande cet article.

Notez qu'il existe des cas compliqués (ECMP) où cette approche de stockage de la PMTU par destination peut être insuffisante. Ce n'est donc qu'une suggestion, chaque implémentation d'IPv6 peut choisir sa façon de mémoriser les MTU des chemins.

Si on veut simplifier la mise en œuvre, et la consommation mémoire, on peut ne mémoriser qu'une seule MTU du chemin, la MTU minimale de tous les chemins testés. Ce n'est pas optimal, mais ça marchera.

Si on stocke la MTU du chemin, comme l'Internet peut changer, il faut se préparer à ce que l'information stockée devienne obsolète. Si elle est trop grande, on recevra des Packet too big (il ne faut donc pas s'attendre à n'en recevoir que pendant la découverte initiale). Si elle est trop petite, on va envoyer des paquets plus petits que ce qu'on pourrait se permettre. Le RFC suggère donc (section 5.3) de ne garder l'information sur la MTU du chemin que pendant une dizaine de minutes.

Le RFC demande enfin (section 5.5) que les mises en œuvre d'IPv6 permettent à l'ingénieur système de configurer la découverte de la MTU du chemin : la débrayer sur certaines interfaces, fixer à la main la MTU d'un chemin, etc.

Il ne faut pas oublier les problèmes de sécurité (section 6 du RFC) : les Packet Too Big ne sont pas authentifiés et un attaquant peut donc en générer pour transmettre une fausse information. Si la MTU indiquée est plus grande que la réalité, les paquets seront jetés. Si elle est plus petite, on aura des sous-performances.

Autre problème de sécurité, le risque que certains administrateurs réseau incompétents ne filtrent tous les messages ICMP, donc également les Packet Too Big (c'est stupide, mais cela arrive). Dans ce cas, la découverte de la MTU du chemin ne fonctionnera pas (cf. RFC 4890 sur les recommandations concernant le filtrage ICMP). L'émetteur devra alors se rabattre sur des techniques comme celle du RFC 4821.

L'annexe B de notre RFC résume les changements faits depuis le RFC 1981. Rien de radical, les principaux portent sur la sécurité (ce sont aussi ceux qui ont engendré le plus de discussions à l'IETF) :

  • Bien expliquer les problèmes créés par le blocage des messages ICMP,
  • Formulations plus strictes sur la validation des messages ICMP,
  • Et surtout, suppression des « fragments atomiques » (RFC 8021).

Le texte a également subi une actualisation générale, les références (pré-)historiques à BSD 4.2 ou bien à TP4 ont été supprimées.

Enfin, pour faire joli, un exemple de traceroute avec recherche de la MTU du chemin activée (option --mtu). Notez la réception d'un Packet Too Big à l'étape 12, annonçant une MTU de 1 450 octets :

% sudo traceroute -6 --mtu --udp --port=53 eu.org
traceroute to eu.org (2a02:8428:46c:5801::4), 30 hops max, 65000 byte packets
 ...
 3  vl387-te2-6-paris1-rtr-021.noc.renater.fr (2001:660:300c:1002:0:131:0:2200)  2.605 ms  2.654 ms  2.507 ms
 4  2001:660:7903:6000:1::4 (2001:660:7903:6000:1::4)  5.002 ms  4.484 ms  5.494 ms
 5  neuf-telecom.sfinx.tm.fr (2001:7f8:4e:2::178)  4.193 ms  3.682 ms  3.763 ms
 6  neuf-telecom.sfinx.tm.fr (2001:7f8:4e:2::178)  14.264 ms  11.927 ms  11.509 ms
 7  2a02-8400-0000-0003-0000-0000-0000-1006.rev.sfr.net (2a02:8400:0:3::1006)  12.760 ms  10.446 ms  11.902 ms
 8  2a02-8400-0000-0003-0000-0000-0000-1c2e.rev.sfr.net (2a02:8400:0:3::1c2e)  10.524 ms  11.477 ms  11.896 ms
 9  2a02-8400-0000-0003-0000-0000-0000-1c2e.rev.sfr.net (2a02:8400:0:3::1c2e)  13.061 ms  11.589 ms  11.915 ms
10  2a02-8400-0000-0003-0000-0000-0000-117e.rev.sfr.net (2a02:8400:0:3::117e)  10.446 ms  10.420 ms  10.361 ms
11  2a02-8400-0000-0003-0000-0000-0000-000a.rev.sfr.net (2a02:8400:0:3::a)  10.460 ms  10.453 ms  10.517 ms
12  2a02-8428-046c-5801-0000-0000-0000-00ff.rev.sfr.net (2a02:8428:46c:5801::ff)  12.495 ms F=1450  12.102 ms  11.203 ms
13  2a02-8428-046c-5801-0000-0000-0000-0004.rev.sfr.net (2a02:8428:46c:5801::4)  12.397 ms  12.217 ms  12.507 ms

La question de la fragmentation en IPv6 a suscité d'innombrables articles. Vous pouvez commencer par celui de Geoff Huston.

Merci à Pierre Beyssac pour avoir fourni le serveur de test configuré comme il fallait, et à Laurent Frigault pour la solution sur FreeBSD.


Téléchargez le RFC 8201


L'article seul

RFC 8212: Default External BGP (EBGP) Route Propagation Behavior without Policies

Date de publication du RFC : Juillet 2017
Auteur(s) du RFC : J. Mauch (Akamai), J. Snijders (NTT), G. Hankins (Nokia)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF grow
Première rédaction de cet article le 10 juillet 2017


Ce RFC est très court, mais concerne un problème fréquent sur l'Internet : les fuites de routes BGP. Traditionnellement, un routeur BGP acceptait par défaut toutes les routes, et annonçait par défaut à ses voisins toutes les routes qu'il connaissait. Il fallait une configuration explicite pour ne pas le faire. En cas d'erreur, ce comportement menait à des fuites (RFC 7908). Notre RFC normalise désormais le comportement inverse : un routeur BGP ne doit, par défaut, rien accepter et rien annoncer. Il faut qu'il soit configuré explicitement si on veut le faire.

Avec l'ancien comportement, la configuration de certains routeurs BGP, les plus imprudents, indiquait les pairs avec qui on échangeait de l'information, plus un certain nombre de routes qu'on n'envoyait pas. Si une erreur faisait qu'on recevait tout à coup des routes imprévues, on les acceptait, et on les renvoyait telles quelles, propageant la fuite (RFC 7908). Des cas fameux de fuites ne manquent pas (voir par exemple celle d'un opérateur malaisien). L'idée derrière ce comportement était d'assurer la connectivité du graphe de l'Internet. Aujourd'hui, on est plutôt plus sensibles aux risques de sécurité qu'à ceux de partitionnement du graphe, et les bonnes pratiques demandent depuis longtemps qu'on indique explicitement ce qu'on accepte et ce qu'on envoie. Voyez par exemple les recommandations de l'ANSSI.

En pratique, ce très court RFC ajoute juste deux paragraphes à la norme BGP, le RFC 4271. Dans sa section 9.1, les paragraphe en question disent désormais qu'il ne doit pas y avoir du tout d'exportation ou d'importation de routes, par défaut. Notez donc que cela ne change pas le protocole BGP, juste le comportement local.

Du moment qu'on change le comportement par défaut, il va y avoir un problème de transition (et ce point a soulevé des discussions à l'IETF). Si le logiciel du routeur s'adaptait au nouveau RFC, certains opérateurs seraient bien surpris que leurs routes ne soient tout coup plus annoncées. L'annexe A du RFC recommande une stratégie en deux temps :

  • D'abord introduire une nouvelle option « BGP non sécurisé » qui serait à Vrai par défaut (mais que les opérateurs pourraient changer), ce qui garderait le comportement actuel mais avec un avertissement émis quelque part « attention, vous exportez des routes sans décision explicite »,
  • Ensuite, dans la version suivante du logiciel, faire que cette option soit à Faux par défaut.

Les routeurs Cisco utilisant IOS-XR ont déjà ce comportement.

Et pour finir sur une note d'humour, à une réunion IETF (IETF 97), le projet qui a finalement mené à ce RFC était illustré… de photos de préservatifs. Pratiquez donc le BGP safe, l'Internet vous remerciera.


Téléchargez le RFC 8212


L'article seul

Les #MercrediFiction de Mastodon, techniques pour les traduire dans d'autres formats

Première rédaction de cet article le 9 juillet 2017


Une des pratiques les plus intéressantes de Mastodon est celle du #MercrediFiction. Le mercredi, des gens écrivent une fiction, a priori en moins de 500 caractères. Plusieurs personnes avaient demandé si on pouvait publier ces pouètes sous d'autres formats (la demande initiale était pour créer un « ebook »). J'ai donc fait un petit programme qui publie les pouètes #MercrediFiction en EPUB et HTML. Cet article, destiné à des programmeurs, explique les choix techniques et quelques problèmes rencontrés. Si vous n'êtes pas programmeur, n'hésitez pas à lire #MercrediFiction quand même !

Même moi, je m'y suis mis à en écrire. Et pour les récupérer ? Premier choix, j'ai décidé d'écrire le code en Python. J'ai utilisé la version 3 de Python (qui n'est pas compatible avec la précédente), la version 2 n'évoluant plus.

Comment récupérer les pouètes ayant le bon mot-croisillon ? La solution évidente est d'utiliser l'API de Mastodon, qui est bien documentée. Mais je la trouve trop compliquée pour mon goût, surtout question authentification (même si ce projet particulier n'a pas besoin d'authentification). J'ai donc décidé de déléguer cette tâche à l'excellent outil en ligne de commande madonctl. (On aurait pu utiliser toot.) Récupérer les pouètes ayant le mot-croisillon #MercrediFiction et les exporter au format JSON se fait simplement avec :

    
% madonctl --output json timeline :MercrediFiction

Je ne sais pas exactement jusqu'où madonctl, ou plutôt l'API de Mastodon qu'il utilise, remonte dans le temps. Je fais donc plusieurs récupérations par jour, et je supprime ensuite les doublons.

Quelle instance utiliser pour récupérer les pouètes ? Dans une fédération, rien ne garantit que deux instances (deux nœuds Mastodon) ont le même contenu. L'algorithme exact qui détermine dans quel cas une instance a reçu un pouète n'est pas, à ma connaissance, documenté (sauf dans le code source, feront remarquer les libristes). Il semble que ce soit à peu près « on récupère les pouètes des gens qui sont sur mon instance, ainsi que ceux repouètés par quelqu'un de mon instance, ainsi que ceux émis par quelqu'un suivi par quelqu'un de mon instance ». Dans ce cas, il est clairement évident qu'il vaut mieux récupérer les pouètes sur une grosse instance, avec plein d'utilisateurs et plein de connexions avec d'autres instances. J'ai choisi Mamot, qui a ces caractéristiques, et est en plus géré par des gens bien.

Une fois le compte mercredifiction@mamot.fr créé, l'appel à madonctl me donne donc les pouètes en JSON. Qu'en faire à la fin de la journée ? (Au passage, le code est évidemment distribué sous une licence libre, et il est visible en https://github.com/bortzmeyer/MercrediFiction.)

Les deux formats que je crée actuellement, EPUB et HTML, utilisent tous les deux XHTML. Il faut donc traduire le JSON en XHTML, ce qui est trivial. J'utilise la bibliothèque officielle de Python pour analyser le JSON et, pour produire le XHTML, le module Yattag, dont le gros intérêt est que le gabarit est lui-même en Python : on n'utilise donc qu'un seul langage. Voici un exemple du code Python+Yattag utilisé pour produire le XHTML :

with tag('head'):
    with tag('link', rel = "stylesheet", type = "text/css", href = "mercredifiction.css"):
        pass
    with tag('title'):
        text("Mercredi Fiction %s" % datestr)
with tag('body'):
    with tag('h1'):
        text("Mercredi Fiction du %s" % formatdate(datestr, short=True))
    

Et le contenu du pouète lui-même ? L'API le distribue en HTML. Mais il y a deux pièges : c'est du HTML et pas du XHTML, et surtout rien ne garantit que l'instance n'enverra que du HTML sûr. Il y a un risque d'injection si on copie ce HTML aveuglément dans le document EPUB ou dans une page Web. Si demain l'instance envoie du JavaScript, on sera bien embêté.

Donc, première chose, traduire le HTML en XHTML. C'est simple, avec le module ElementTree (etree) de lxml :

    
from lxml import etree, html
...
tree = html.fromstring(content)
result = etree.tostring(tree, pretty_print=False, method="xml")
doc.asis(result.decode('UTF-8'))

(La fonction asis vient de Yattag et indique de mettre le contenu tel quel dans le document HTML, sans, par exemple, convertir les > en &gt;.)

Et pour la sécurité ? On utilise un autre module de lxml, Cleaner :

    
from lxml.html.clean import Cleaner
...
cleaner = Cleaner(scripts=True, javascript=True, embedded=True, meta=True, page_structure=True,
                                      links=False, remove_unknown_tags=True,
                                      style=False)

Le code ci-dessus supprime de l'HTML le JavaScript, et plusieurs autres éléments HTML dangereux.

Voilà, on a désormais du XHTML propre et, espérons-le, sûr. Pour produire les pages Web, ça suffit. Mais, pour l'EPUB, c'est un peu plus compliqué. À ma connaissance, le format EPUB ne bénéficie pas d'une norme publique. J'ai donc dû me rabattre sur d'excellentes documentations comme le Epub Format Construction Guide qui explique bien la structure d'un fichier EPUB. Un fichier EPUB est un zip contenant entre autre des fichiers HTML. Le lecteur EPUB typique est bien plus strict que le navigateur Web typique, et il faut donc faire attention à l'HTML qu'on génère. Il faut aussi placer diverses métadonnées, en Dublin Core. En l'absence d'une norme disponible, certaines choses doivent être devinées (par exemple, mettre le fichier CSS dans l'EPUB et le manifeste ne suffit pas, il faut aussi le déclarer dans l'HTML, ou bien le fait que les compteurs du nombre de pages dans la table des matières semblent optionnels). Pour les programmeurs Python, il y a aussi cet exemple en Python.

Si vous écrivez un programme qui produit de l'EPUB, n'oubliez pas de valider l'EPUB produit. Cela peut se faire en ligne de commande avec le programme epubcheck :

% epubcheck mercredifiction-dernier.epub   
Validating using EPUB version 2.0.1 rules.
No errors or warnings detected.
epubcheck completed

Ou cela peut être vérifié en ligne, ce service utilisant le même programme.

Enfin, question EPUB, n'oubliez pas de servir les fichiers EPUB avec le bon type MIME, application/epub+zip, pour que le lecteur d'ebooks soit lancé automatiquement. Sur Apache, ça peut se faire avec la directive AddType application/epub+zip .epub.

Quelques petits détails amusants ou énervants, pour terminer. D'abord, l'API de Mastodon, pour des raisons qui m'échappent complètement (j'ai entendu des explications, mais toutes mauvaises), lorsque le pouète contient des emojis (comme 🌸 ou 🌤), renvoie non pas le caractère Unicode correspondant, mais du texte entre deux-points, par exemple :cherry_blossom: (ces textes viennent du système commercial Emoji One). Il n'y a absolument aucune raison d'utiliser ces séquences de texte. La bogue a été signalée mais pour l'instant sans résultat. J'avais envisagé de faire la conversion moi-même pour #MercrediFiction mais c'est trop complexe, vu la taille de la base des emojis, à moins d'installer le code très compliqué fourni par Emoji One.

Autre limite de la version actuelle de mon service, le fait que les images ne sont pas incluses. Rare sont les pouètes #MercrediFiction qui comportent des images, mais ce serait tout de même sympathique de les gérer proprement. L'EPUB et le HTML devraient sans doute être gérés différemment : dans le premier cas, le lecteur s'attend à un ebook autonome, fonctionnant sans connexion Internet, et il faut donc récupérer les images en local, et les mettre dans le fichier EPUB. Dans le second, il vaut sans doute mieux mettre un lien vers l'image originale (avec toutefois le risque que, si l'hébergement original disparait, on perde l'image).

Notez que récupérer les images présente quelques risques juridiques. En théorie, c'est pareil avec les textes (le droit d'auteur s'applique aux deux, et je dois avouer que je récolte les pouètes sans penser à ce problème) mais, en pratique, le risque de réclamation est plus élevé pour les images (demandez à n'importe quel hébergeur). Pour l'instant, je n'ai pas encore reçu de message d'un juriste me demandant de supprimer tel ou tel pouète.

Le résultat est visible ici. Sinon, il existe un autre service de distribution, dont les sources (également en Python) sont disponibles.


L'article seul

RFC 8195: Use of BGP Large Communities

Date de publication du RFC : Juin 2017
Auteur(s) du RFC : J. Snijders, J. Heasley (NTT), M. Schmidt (i3D.net)
Pour information
Réalisé dans le cadre du groupe de travail IETF grow
Première rédaction de cet article le 1 juillet 2017


Le RFC 8092 a normalisé la notion de « grande communauté » BGP, des données attachées aux annonces de routes, plus grandes que les précédentes « communautés » et permettant donc de stocker davantage d'informations. Ce nouveau RFC explique à quoi peuvent servir ces grandes communautés et donne des exemples d'utilisation. Un document concret, qui ravira les opérateurs réseaux.

Le RFC original sur les communautés BGP était le RFC 1997, et il était accompagné d'un RFC 1998 présentant des cas concrets d'utilisation. C'est la même démarche qui est faite ici, de faire suivre le document de normalisation, le RFC 8092, d'un document décrivant des utilisations dans le monde réel (avec des expériences pratiques décrites à NANOG ou NLnog).

Un petit rappel des grandes communautés, d'abord (section 2 du RFC). Chaque communauté se compose de trois champs de quatre octets chacun. Le premier champ se nomme GA, pour Global Administrator, et sa valeur est le numéro de l'AS qui a ajouté cette communauté (rappelez-vous que les numéros d'AS font désormais quatre octets), ce qui assure l'unicité mondiale des communautés. Les deux autres champs portent les noms peu imaginatifs de Local Data Part 1 et Local Data Part 2. La plupart du temps, on se sert du premier pour identifier une fonction, et du second pour identifier les paramètres d'une fonction. Notez qu'il n'existe pas de standard pour ces deux champs locaux : comme leur nom l'indique, chaque AS les affecte comme il veut. Une même valeur peut donc avoir des significations différentes selon l'AS.

Le RFC contient plusieurs exemples, qui utilisent à chaque fois la même topologie : un transitaire, l'AS 65551, avec un client, l'AS 64497, qui est lui-même transitaire pour les AS 64498 et 64499, qui par ailleurs ont une relation de peering (en épais sur l'image) example-bgp-large-comm.png :

Notre RFC classe les communautés en deux catégories : celles d'information et celles d'action. Les premières servent à distribuer des informations qui ne modifieront pas le comportement des routeurs, mais pourront être utiles pour le déboguage, ou les statistiques. Un exemple est l'ajout d'une communauté pour indiquer dans quel pays on a appris telle route. Elles sont typiquement ajoutées par l'AS qui a défini ces informations, et il mettra donc son numéro d'AS dans le champ GA. Les secondes, les communautés d'action, servent à indiquer une action souhaitée. Elles sont définies par l'AS qui aura à agir, et mises dans le message BGP par un de ses pairs. Un exemple est une communauté pour indiquer à son pair quelle préférence on souhaite qu'il attribue à telle route.

La section 3 donne des exemples de communautés d'information. Le premier est celui où l'AS 66497 marque les routes qu'il a reçues avec une communauté qui indique le pays où la route a été apprise (le code pays utilisé est celui de ISO 3166-1). La fonction a reçu le numéro 1. Donc, la présence dans l'annonce BGP de la communauté 64497:1:528 indique une route apprise aux Pays-Bas, la communauté 64497:1:392 indique le Japon, etc. Le même opérateur peut aussi prévoir une fonction 2 pour indiquer la région (concept plus vaste que le pays), en utilisant les codes M.49 : 64497:2:2 dit que la route a été apprise en Afrique, 64497:2:150 en Europe et ainsi de suite. Rappelez-vous bien que la signification d'une fonction (et donc des paramètres qui suivent) dépend de l'AS (ou, plus rigoureusement, du champ GA). Il n'y a pas de standardisation des fonctions. Si vous voyez une communauté 65551:2:2, cela ne signifie pas forcément que la route vient d'Afrique : l'AS 65551 peut utiliser la fonction 2 pour tout à fait autre chose.

À part l'origine géographique d'une route, il est souvent utile de savoir si la route a été apprise d'un pair ou d'un transitaire. Le RFC donne l'exemple d'une fonction 3 où le paramètre 1 indique une route interne, 2 une route apprise d'un client, 3 d'un pair et 4 d'un transitaire. Ainsi, la communauté 64497:3:2 nous dit que la route vient d'un client de l'AS 64497.

Une même annonce de route peut avoir plusieurs communautés. Une route étiquetée 64497:1:528, 64497:2:150 et 64497:3:3 vient donc d'un pair aux Pays-Bas.

Et les communautés d'action (section 4 du RFC) ? Un premier exemple est celui où on indique à son camarade BGP de ne pas exporter inconditionnellement une route qu'on lui annonce (ce qui est le comportement par défaut de BGP). Mettons que c'est la fonction 4. (Vous noterez qu'il n'existe pas d'espace de numérotation de fonctions distinct pour les communautés d'information et d'action. On sait que la fonction 4 est d'action uniquement si on connait la politique de l'AS en question.) Mettons encore que l'AS 64497 a défini le paramètre comme étant un numéro d'AS à qui il ne faut pas exporter la route. Ainsi, envoyer à l'AS 64497 une route étiquetée 64497:4:64498 signifierait « n'exporte pas cette route à l'AS 64498 ». De la même façon, on peut imaginer une fonction 5 qui utilise pour cette non-exportation sélective le code pays. 64997:5:392 voudrait dire « n'exporte pas cette route au Japon ». (Rappelez-vous que BGP est du routage politique : son but principal est de permettre de faire respecter les règles du business des opérateurs.)

Je me permets d'enfoncer le clou : dans les communautés d'action, l'AS qui ajoute une communauté ne met pas son numéro d'AS dans le champ GA, mais celui de l'AS qui doit agir.

Un autre exemple de communauté d'action serait un allongement sélectif du chemin d'AS. On allonge le chemin d'AS en répétant le même numéro d'AS lorsqu'on veut décourager l'utilisation d'une certaine route. (En effet, un des facteurs essentiels d'une décision BGP est la longueur du chemin d'AS : BGP préfère le plus court.) Ainsi, l'exemple donné utilise la fonction 6 avec comme paramètre l'AS voisin où appliquer cet allongement (prepending). 64497:6:64498 signifie « ajoute ton AS quand tu exportes vers 64498 ».

Quand on gère un routeur BGP multihomé, influencer le trafic sortant est assez simple. On peut par exemple mettre une préférence plus élevée à la sortie vers tel transitaire. Influencer le trafic entrant (par exemple si on veut que le trafic vienne surtout par tel transitaire) est plus délicat : on ne peut pas configurer directement la politique des autres AS. Certains partenaires BGP peuvent permettre de définir la préférence locale (RFC 4271, section 5.1.5), via une communauté. Ici, le RFC donne l'exemple d'une fonction 8 qui indique « mets la préférence d'une route client [a priori une préférence élevée] », 10 une route de peering, 11 de transit et 12 une route de secours, à n'utiliser qu'en dernier recours (peut-être parce qu'elle passe par un fournisseur cher, ou bien de mauvaise qualité). Le paramètre (le troisième champ de la communauté) n'est pas utilisé. Alors, la communauté 64997:12:0 signifiera « AS 64997, mets à cette route la préférence des routes de secours [a priori très basse] ».

Le RFC suggère aussi, si le paramètre est utilisé, de le prendre comme indiquant la région où cette préférence spécifique est appliquée. Par exemple 64997:10:5 demandera à l'AS 64997 de mettre la préférence des routes de peering (10) à tous les routeurs en Amérique du Sud (code 5 de la norme M.49).

Attention, changer la préférence locale est une arme puissante, et sa mauvaise utilisation peut mener à des coincements (RFC 4264).

Dernier exemple donné par notre RFC, les souhaits exprimés à un serveur de routes (RFC 7947). Ces serveurs parlent en BGP mais ne sont pas des routeurs, leur seul rôle est de redistribuer l'information aux routeurs. Par défaut, tout est redistribué à tout le monde (RFC 7948), ce qui n'est pas toujours ce qu'on souhaite. Si on veut une politique plus sélective, certains serveurs de route documentent des communautés que les clients du serveur peuvent utiliser pour influencer la redistribution des routes. Notre RFC donne comme exemple une fonction 13 pour dire « n'annonce pas » (alors que cela aurait été le comportement normal) et une fonction 14 pour « annonce quand même » (si le comportement normal aurait été de ne pas relayer la route). Ainsi, 64997:13:0 est « n'annonce pas cette route par défaut » et 64997:14:64999 est « annonce cette route à l'AS 64999 ».

Vous voudriez des exemples réels ? Netnod avait annoncé que ces grandes communautés étaient utilisables sur leur AS 52005 :

  • 52005:0:0 Do not announce to any peer
  • 52005:0:ASx Do not announce to ASx
  • 52005:1:ASx Announce to ASx if 52005:0:0 is set
  • 52005:101:ASx Prepend peer AS 1 time to ASx
  • 52005:102:ASx Prepend peer AS 2 times to ASx
  • 52005:103:ASx Prepend peer AS 2 times to ASx

On trouve également ces communautés au IX-Denver, documentées ici (sous « Route Server B and C Communities ») ou au SIX (dans leur documentation). Il y a enfin cet exemple à ECIX. (Merci à Job Snijders et Pier Carlo Chiodi‏ pour les exemples.)

Voilà, à vous maintenant de développer votre propre politique de communautés d'information, et de lire la documentation de vos voisins BGP pour voir quelles communautés ils utilisent (communautés d'action).


Téléchargez le RFC 8195


L'article seul

RFC 8190: Updates to Special-Purpose IP Address Registries

Date de publication du RFC : Juin 2017
Auteur(s) du RFC : R. Bonica (Juniper Networks), M. Cotton (ICANN), B. Haberman (Johns Hopkins University), L. Vegoda (ICANN)
Première rédaction de cet article le 28 juin 2017


Le RFC 6890 avait créé un registre unique des préfixes d'adresses IP « spéciaux ». Ce nouveau RFC met à jour le RFC 6890, précisant et corrigeant quelques points.

Un préfixe « spécial » est juste un préfixe IP dédié à un usage inhabituel, et qui peut nécessiter un traitement particulier. Ainsi, les préfixes 203.0.113.0/24 et 2001:db8::/32 sont dédiés à la documentation, aux cours et exemples et, bien que n'étant pas traités différemment des autres préfixes par les routeurs, ne doivent normalement pas apparaitre dans un vrai réseau. Le RFC 6890 spécifie les deux registres de préfixes spéciaux, un pour IPv4 et un pour IPv6. Mais il y avait une ambiguité dans ce RFC, dans l'usage du terme « global ». Il n'est pas évident à traduire en français. Dans le langage courant, global/globalement, a surtout le sens de « le plus souvent/généralement », alors que, dans le contexte de l'Internet, ce serait plutôt dans le sens « valable pour tout point du globe ». Même en anglais, le terme est polysémique : il peut signifier que l'adresse IP est unique au niveau mondial ou bien il peut signifier que l'adresse est potentiellement joignable mondialement, et qu'un routeur peut donc faire suivre le paquet en dehors de son propre domaine. (La joignabilité effective, elle, dépend évidemment de l'état de BGP.) Le RFC 4291, dans sa section 2.5.4, n'est pas plus clair.

Reprenons l'exemple des préfixes de documentation. Dans les trois préfixes IPv4 de documentation (comme 203.0.113.0/24), les adresses ne sont pas uniques (chacun les alloue comme il veut et, vu le faible nombre d'adresses total, il y aura certainement des collisions) et, de toute façon, n'ont pas de signification en dehors d'un domaine : cela n'a pas de sens de transmettre des paquets ayant de telles adresses à ses partenaires. Pour le préfixe de documentation IPv6, 2001:db8::/32, c'est un peu plus compliqué car les adresses peuvent être quasi-uniques mondialement (si on prend la précaution de les tirer au sort dans le préfixe) mais, de toute façon, n'ont pas vocation à être joignables de partout et on ne transmet donc pas ces paquets à ses voisins.

Bref, notre nouveau RFC choisit d'être plus précis, renommant l'ancien booléen « global » du RFC 6890 en « globally reachable ».

La nouvelle définition (section 2) est donc bien « le paquet ayant cette adresse comme destination peut être transmis par un routeur à un routeur d'un autre domaine (d'un autre opérateur, en pratique) ». La colonne Global du registre est donc remplacée par Globally reachable. Notez qu'il n'y a pas de colonne « Unicité » (une autre définition possible de global).

Cela a nécessité quelques changements supplémentaires dans le registre (toujours section 2), colonne Globally reachable :

  • Le préfixe Teredo 2001::/32 passe de False (les adresses Teredo ne sont pas uniques) à N/A (Not Applicable) car la question de la joignabilité mondiale n'a pas vraiment de sens pour Teredo,
  • Les ULA du RFC 4193 restent à False mais avec une précision en note.

Téléchargez le RFC 8190


L'article seul

Fiche de lecture : La face cachée d'Internet

Auteur(s) du livre : Rayna Stamboliyska
Éditeur : Larousse
978-2-03-593641-7
Publié en 2017
Première rédaction de cet article le 28 juin 2017


Vous allez peut-être vous dire, en prenant connaissance du sous-titre (« hackers, darkweb, Tor, Anonymous, WikiLeaks, bitcoins… ») et surtout en voyant la couverture un peu tape-à-l'œil, que c'est juste le Nième livre sur les gros méchants cyberaffreux des bas-fonds du réseau, ceux qui font sauter une centrale nucléaire en tapotant sur un smartphone, ceux que le gentil ministre va heureusement bientôt civiliser. Des livres de ce genre, sensationnalistes, bourrés d'erreurs qui amusent les geeks et égarent les lecteurs innocents, il y en a plein. Mais celui-ci est différent :

  • Il est écrit par une auteure qui connait son (ou plutôt ses) sujets,
  • Il ne contient pas d'erreurs (ou alors je ne les ai pas trouvées),
  • Il essaie de tout expliquer sans simplifier et sans prendre le lecteur pour un ahuri.

Le cahier des charges était donc difficile. Les éditions Larousse font des livres à destination du grand public (le célèbre M. Michu). Souvent, « mais c'est pour le grand public » sert d'excuse pour bâcler le travail. Or, cela devrait être le contraire : comme M. Michu n'a pas les connaissances nécessaires pour corriger de lui-même les erreurs, il faut être plus rigoureux et plus précis quand on écrit pour le grand public. L'auteure n'a pas écrit pour toi, lecteur typique de mon blog ou, plutôt, pas que pour toi.

On trouve très peu de livres ayant un cahier des charges analogue. Soit on fait du purement technique, pour informaticiens, soit on fait du Paris Match. C'est ce que j'apprécie le plus dans le travail de Rayna Stamboliyska, l'effort pour être claire et pédagogue, sans être approximative et baratineuse.

Est-ce réussi ? Je vais laisser l·a·e lect·rice·eur en juger. Personnellement, je trouve que le livre est riche, plein d'informations, et que M. Michu va devoir faire un effort pour suivre ces nombreux personnages et ces rebondissements multiples (notamment dans le chapitre sur WikiLeaks). D'autant plus qu'on trouve dans ce livre des choses qu'on s'attend plus à voir dans un ouvrage universitaire (ma préférée est le double espace de numérotation des références, un en romain pour des notes de bas de page, un autre en italique pour la bibliographie à la fin) Mais, justement, je ne pense pas que M. Michu soit un imbécile et demander un travail au lecteur ne me choque pas.

Ah, et puisque j'ai parlé de la bibliographie : recopier à la main des URL est évidemment pénible et provoque souvent des erreurs. Donc, pour accéder aux nombreux textes cités, et pour d'autres raisons, je recommande que vous visitiez le site Web officiel attaché au livre. Quelques autres références :

Avertissement : je suis l'auteur de la préface, et j'ai reçu un exemplaire gratuit. Je ne suis donc pas neutre.

Un dernier mot, pour les programmeurs : il y a deux fautes dans le code en C sur la couverture. Cherchez.


L'article seul

Les drôles de pratiques BGP d'un opérateur bulgare et d'une université colombienne

Première rédaction de cet article le 26 juin 2017


Aujourd'hui, nous allons un peu regarder un drôle de problème BGP, et en n'utilisant que des sources ouvertes. L'infrastructure de l'Internet est en effet largement ouverte aux investigations.

Alors, d'abord, le premier fait : l'opérateur bulgare Wireless Network Solutions annonçait en BGP un grand nombre de routes assez éloignées de ce qu'on attendrait de lui. L'annonce a cessé le 7 juin mais on peut toujours voir les anciennes annonces dans ce fichier issu de RIPE stat ou directement sur RIPE Stat : announces-as34991.png

Parmi les préfixes annoncés, on trouvait en effet des valeurs comme 168.176.219.0/24. Or, l'AS 34991 est situé en Bulgarie (ici, les données complètes, obtenues via whois). Alors que le préfixe 168.176.219.0/24 fait partie du 168.176.0.0/16 alloué à l'Université nationale de Colombie (ici, les données complètes via whois). Pourquoi donc un opérateur situé dans une petite ville de Bulgarie aurait-il comme client une université à Bogota ? Les liens économiques, historiques, linguistiques ou religieux, entre la Bulgarie et la Colombie semblent faibles. Creusons.

L'Université nationale de Colombie dispose d'un préfixe IPv4 de longueur 16 (cf. les données citées plus haut) mais ne l'annonce pas en BGP. On ne le voit pas sur RIPE stat, ou sur le looking glass de Hurricane Electric. Des sous-préfixes sont annoncés mais pas le /16 complet. Voyons sur RouteViews en telnet :

% telnet route-views.routeviews.org
Trying 2001:468:d01:33::80df:3367...
Connected to route-views.routeviews.org.
...
User Access Verification

Username: rviews

route-views>show ip route 168.176.219.0   
% Subnet not in table

route-views>show ip route 168.176.0.0  
Routing entry for 168.176.0.0/16, 46 known subnets
  Variably subnetted with 7 masks
B        168.176.0.0/18 [20/0] via 66.110.0.86, 2d02h
B        168.176.64.0/19 [20/0] via 66.110.0.86, 2d02h
B        168.176.72.0/22 [20/0] via 66.110.0.86, 2d02h
B        168.176.76.0/24 [20/0] via 66.110.0.86, 2d02h
...
    

Donc, l'université colombienne n'annonce qu'une petite partie de son /16. À une époque où tout le monde souffre de la pénurie d'adresses IPv4, c'est un peu curieux. L'annonce du sous-préfixe 168.176.219.0/24 par l'AS 34991 n'est donc pas un détournement BGP classique comme l'étaient ceux par Telekom Malaysia en 2015 ou bien lors du shunt de 2013. « Aucun préfixe BGP n'a été détourné pour le tournage de ce film. » Néanmoins, il ne semble pas non plus que cette annonce, qui n'a duré que dix jours, ait été légitime, à moins que le campus de Medellín n'ait eu envie pendant dix jours d'être connecté à l'Internet via la Bulgarie ? Une autre hypothèse, qui ne peut être formulée que par des gens pratiquant la méchanceté gratuite, serait que l'université a loué des /24 à des spammeurs bulgares…

Certains ont émis l'hypothèse que l'AS bulgare avait été pris par des méchants, à l'occasion d'une attaque flamant. Une attaque flamant est l'enregistrement d'un domaine qui n'existait pas, pour récupérer le courrier, y compris les demandes d'autorisation, d'un objet dont les contacts utilisaient des domaines disparus, ou mal enregistrés. Un exemple d'attaque flamant pour des TLD est par exemple documentée ici. La (je crois) première description publique date de 2007.

Les contacts techniques et administratifs de l'AS sont en @wirelessnetbg.info. Ils ont été modifiés juste avant les annonces bizarres, et le domaine créé peu de temps avant ces annonces (cf. les données récupérées par whois, le champ Creation Date). Mais rien ne prouve (en tout cas avec des données ouvertes) qu'il y a eu une attaque flamant contre l'AS, bien qu'elle soit possible. Notez enfin que l'entreprise derrière l'AS ne semble pas exister dans le registre des entreprises en Bulgarie.

Merci à Ronald F. Guilmette pour avoir détecté le cas et l'avoir publié, et à Rayna Stamboliyska pour des investigations.


L'article seul

RFC 8126: Guidelines for Writing an IANA Considerations Section in RFCs

Date de publication du RFC : Juin 2017
Auteur(s) du RFC : M. Cotton (ICANN), B. Leiba (Huawei Technologies), T. Narten (IBM Corporation)
Première rédaction de cet article le 21 juin 2017


Un aspect peu connu du travail de normalisation est la nécessité de tenir des registres de certains paramètres, lorsque la liste de ces derniers n'est pas fixée dans un RFC. Par exemple, les algorithmes publiés dans le DNS pour IPsec ne sont pas définis de manière limitative dans le RFC 4025 mais sont enregistrés dans un registre public. Même chose pour les types de média du RFC 6838, qui ont leur propre registre. Pour les RFC, ces registres sont tenus par l'IANA. Celle-ci ne décide pas quels registres elle doit tenir ni à quelle condition un nouveau paramètre peut y rentrer, elle applique les décisions contenues dans la section IANA Considerations d'un RFC. C'est cette section qui est décrite ici. Ce RFC remplace l'ancien RFC 5226.

Prenons l'exemple du RFC 3315 (DHCP). Sa section 24 contient le texte « This document defines several new name spaces associated with DHCPv6 and DHCPv6 options: Message types, Status codes, DUID and Option codes. IANA has established a registry of values for each of these name spaces, which are described in the remainder of this section. These name spaces will be managed by the IANA and all will be managed separately from the name spaces defined for DHCPv4. ». En application de ce texte, l'IANA a créé le registre DHCPv6 and DHCPv6 options qu'on peut consulter en ligne à https://www.iana.org/assignments/dhcpv6-parameters. Et comment ajoute-t-on des entrées dans ce registre ? En suivant les règles données dans ce même RFC : «  New DHCP option codes are tentatively assigned after the specification for the associated option, published as an Internet Draft, has received expert review by a designated expert [11]. The final assignment of DHCP option codes is through Standards Action, as defined in RFC 2434.  ».

L'intérêt d'avoir une section obligatoire IANA Considerations est de concentrer en un seul endroit les informations nécessaires à l'IANA pour faire son travail. Pour aider les auteurs de RFC à écrire correctement cette section IANA Considerations, notre RFC 8126, qui succède au RFC 5226, pose quelques règles.

La section 1 du RFC décrit le problème général de la gestion d'un espace de nommage (namespace). Tous ces espaces n'ont pas les mêmes caractéristiques. Certains sont très petits (le champ protocole, qui n'a que huit bits soit 256 valeurs possibles, cf. RFC 5237) et doivent donc être gérés avec prudence, certains sont hiérarchiques comme le DNS ou comme les OID et peuvent donc être délégués, certains sont immenses, et peuvent être gérés avec moins de précautions (mais nécessitent quand même des règles, comme expliqué dans la section 1).

Cette même section 1 résume les points essentiels que doit connaitre l'auteur d'un RFC, notamment d'avoir une section dédiée IANA Considerations, et de n'y mettre que ce qui est strictement nécessaire à l'IANA (pas de digressions, pas de détails techniques).

La section 2 est consacrée à la création d'un nouveau registre. Il y a bien des décisions à prendre à ce stade. Par exemple, notre RFC recommande de voir si on ne peut pas faire un registre arborescent, où l'action de l'IANA se limiterait à la racine de ce registre, diminuant ainsi sa charge de travail. (C'est le cas des URN du RFC 8141.)

Si un RFC doit demander une telle création, il doit préciser quelle politique d'enregistrement devra suivre l'IANA. C'est une des parties les plus intéressantes du RFC, notamment sa section 4 qui explique les politiques possibles :

  • Premier Arrivé, Premier Servi (« First Come First Served »), où toute requête est acceptable et est faite dans l'ordre d'arrivée. Les entrées dans le préfixe OID iso.org.dod.internet.private.enterprise sont un bon exemple (https://www.iana.org/assignments/enterprise-numbers mais attention, le registre est lourd à charger). C'est aussi le cas des plans d'URI provisoires (RFC 7595) ou des états de traitement du courrier (RFC 6729). C'est sans doute la plus « libérale » des politiques d'enregistrement. (Il n'y a pas de mécanisme explicite contre les vilains qui enregistreraient plein de valeurs inutiles, avait noté l'examen de sécurité.)
  • Examen par un expert (« Expert review »), comme détaillé plus bas (section 5 du RFC). C'est ainsi que sont gérés les plans des URI permanents du RFC 7595, ou bien les types de méthode d'EAP (RFC 3748, sections 6 et 7.2, notez que Expert review était appelé Designated expert à cette époque).
  • Spécification nécessaire (« Specification required ») où un texte écrit et stable décrivant le paramètre souhaité est nécessaire (ce texte n'est pas forcément un RFC, il y a d'ailleurs une politique « RFC required »). Il faut en outre un examen par un expert, comme dans la politique ci-dessus, l'expert vérifiant que la spécification est claire. Les profils de ROHC (RFC 5795) sont enregistrées sous cette condition. Les utilisations de certificat de DANE (RFC 6698, section 7.2) sont « RFC nécessaire ».
  • Examen par l'IETF (« IETF Review »), l'une des plus « lourdes », puisqu'il faut un RFC « officiel », qui soit passé par l'IESG ou par un groupe de travail IETF (tous les RFC ne sont pas dans ce cas, certains sont des contributions individuelles). C'est la politique des extensions TLS du RFC 6066 (cf. RFC 5246, section 12, qui utilisait encore l'ancien terme de « IETF Consensus »).
  • Action de normalisation (« Standards Action »), encore plus difficile, le RFC doit être sur le chemin des normes et donc avoir été approuvé par l'IESG. C'est le cas par exemple des types de message BGP (RFC 4271, section 4.1), en raison de la faible taille de l'espace en question (un seul octet, donc un nombre de valeurs limité) et sans doute aussi en raison de l'extrême criticité de BGP. C'est aussi la politique pour les options DHCP données en exemple plus haut.
  • Utilisation privée (« Private use ») est une politique possible, qui est en fait l'absence de registre, et donc l'absence de politique de registre : chacun utilise les valeurs qu'il veut. Par exemple, dans le protocole TLS (RFC 5246, section 12), les valeurs 224 à 255 des identifiants numériques du type de certificat sont à usage privé ; chacun s'en sert comme il veut, sans coordination.
  • Utilisation à des fins expérimentales (« Experimental use »), est en pratique la même chose que l'utilisation privée. La seule différence est le but (tester temporairement une idée, cf. RFC 3692). C'est le cas des valeurs des en-têtes IP du RFC 4727.
  • Et il existe encore l'approbation par l'IESG (« IESG approval ») qui est la politique de dernier recours, à utiliser en cas de cafouillage du processus.
  • Et le cas un peu particulier de l'allocation hiérarchique (« Hierarchical allocation ») où l'IANA ne gère que le registre de plus haut niveau, selon une des politiques ci-dessus, déléguant les niveaux inférieurs à d'autres registres. C'est le cas par exemple des adresses IP ou bien sûr des noms de domaine.

Le choix d'une politique n'est pas évident : elle ne doit pas être trop stricte (ce qui ferait souffrir les auteurs de protocoles, confrontés à une bureaucratie pénible) mais pas non plus trop laxiste (ce qui risquerait de remplir les registres de valeurs inutiles, les rendant difficilement utilisables). En tout cas, c'est une des décisions importantes qu'il faut prendre lors de l'écriture d'une spécification.

Notre RFC conseille (sans l'imposer) d'utiliser une de ces politiques (« well-known policies »). Elles ont été testés en pratique et fonctionnent, concevoir une nouvelle politique fait courir le risque qu'elle soit incohérente ou insuffisamment spécifiée, et, de toute façon, déroutera les lecteurs et l'IANA, qui devront apprendre une nouvelle règle.

Parmi les autres points que doit spécifier le RFC qui crée un nouveau registre, le format de celui-ci (section 2.2 ; la plupart des registres sont maintenus en XML, mais même dans ce cas, des détails de syntaxe, comme les valeurs acceptables, peuvent devoir être précisés). Notez que le format n'est pas forcément automatiquement vérifié par l'IANA. Notre RFC recommande également de bien préciser si le registre accepte des caractères non-ASCII (cf. RFC 8264, section 10).

Autre choix à faire dans un registre, le pouvoir de changer les règles (change control). Pour des normes IETF (RFC sur le chemin des normes), c'est en général l'IETF qui a ce pouvoir, mais des registres IANA peuvent être créés pour des protocoles qui ne sont pas gérés par l'IETF et, dans ce cas, le pouvoir peut appartenir à une autre organisation. C'est ainsi que les types de données XML (RFC 7303), comme le application/calendar+xml (RFC 6321) sont contrôlés par le W3C.

La section 3 couvre l'enregistrement de nouveaux paramètres dans un registre existant. C'est elle qui précise, entre autres, que l'IANA ne laisse normalement pas le choix de la valeur du paramètre au demandeur (mais, en pratique, l'IANA est sympa et a accepté beaucoup de demandes humoristiques comme le port TCP n° 1984 pour le logiciel Big Brother...)

La section 6 donne des noms aux différents états d'enregistrement d'une valeur. Le registre note l'état de chaque valeur, parmi ces choix :

  • Réservé à une utilisation privée,
  • Réservé à une utilisation expérimentale,
  • Non affecté (et donc libre),
  • Réservé (non alloué mais non libre, par exemple parce que la norme a préféré le garder pour de futures extensions),
  • Affecté,
  • Utilisation connue, mais non officiellement affecté, ce qui se produit parfois quand un malotru s'approprie des valeurs sans passer par les procédures normales, comme dans le cas du RFC 8093.

Par exemple, si on prend les types de messages BGP, on voit dans le registre que 0 est réservé, les valeurs à partir de 6 sont libres, les autres sont affectées (1 = OPEN, etc).

La section 5 décrit le rôle des experts sur lesquels doit parfois s'appuyer l'IANA. Certains registres nécessitent en effet un choix technique avec l'enregistrement d'un nouveau paramètre et l'IANA n'a pas forcément les compétences nécessaires pour cette évaluation. Elle délègue alors cette tâche à un expert (designated expert, leur nom est noté à côté de celui du registre). Par exemple, pour le registre des langues, défini par le RFC 5646, l'expert actuel est Michael Everson. Ce registre utilise également une autre possibilité décrite dans cette section, une liste de discussion qui sert à un examen collectif des requêtes (pour le registre des langues, cette liste est ietf-languages@iana.org). La section 5.1 discute des autres choix qui auraient pu être faits (par exemple un examen par le groupe de travail qui a créé le RFC, ce qui n'est pas possible, les groupes de travail IETF ayant une durée de vie limitée). Elle explique ensuite les devoirs de l'expert (comme la nécessité de répondre relativement rapidement, section 5.3, chose qui est loin d'être toujours faite).

Enfin, diverses questions sont traitées dans la section 9, comme la récupération de valeurs qui avaient été affectées mais qui ne le sont plus (le RFC 3942 l'avait fait mais c'est évidemment impossible dès que les paramètres en question ont une... valeur, ce qui est le cas entre autres des adresses IP).

Bien que la plupart des allocations effectuées par l'IANA ne sont guère polémiques (à l'exception des noms de domaine et des adresses IP, qui sont des sujets très chauds), notre RFC 8126 prévoit une procédure d'appel, décrite en section 10. Cela n'a pas suffit à régler quelques cas pénibles comme l'enregistrement de CARP.

Ce RFC 8126 remplace le RFC 5226. Les principaux changements sont détaillés dans la section 14.1 :

  • Moins de texte normatif style RFC 2119, puisqu'il ne s'agit pas de la description d'un protocole,
  • Meilleure description des registres hiérarchiques,
  • Ajout d'une partie sur le pouvoir de changer les règles (change control),
  • Ajout de la possibilité de fermer un registre,
  • Ajout de la section 8 sur le cas de RFC qui remplacent un RFC existant,
  • Etc.

Notez que notre RFC est également complété en ligne par des informations plus récentes.

Les relations entre l'IETF et l'IANA sont fixées par le MOU contenu dans le RFC 2860. À noter que tout n'est pas couvert dans ce RFC, notamment des limites aux demandes de l'IETF. Que se passerait-il par exemple si l'IETF demandait à l'IANA, qui ne facture pas ses prestations, de créer un registre de milliards d'entrées, très dynamique et donc très coûteux à maintenir ? Pour l'instant, l'IANA ne peut pas, en théorie, le refuser et la question s'est parfois posée à l'IETF de savoir si tel ou tel registre n'était pas trop demander.

Puisque l'IANA est un acteur important de l'Internet, rappelons aussi que, bien que la fonction de l'IANA soit actuellement assurée par l'ICANN, la tâche de gestion des protocoles et des registres n'a rien à voir avec les activités, essentiellement politiciennes, de l'ICANN. La « fonction IANA » (création et gestion de ces registres) est formellement nommée IFO (IANA Functions Operator) ou IPPSO (IANA Protocol Parameter Services Operator) mais tout le monde dit simplement « IANA ».


Téléchargez le RFC 8126


L'article seul

Sur une panne DNS, et sur les leçons à en tirer (BNP Paribas)

Première rédaction de cet article le 20 juin 2017
Dernière mise à jour le 22 juin 2017


Ce matin du 20 juin, plein de messages sur les réseaux sociaux pour se plaindre d'une impossibilité d'accéder aux services en ligne de BNP Paribas. Le CM a bien du mal à répondre à tout le monde. À l'origine de cette panne, un problème DNS. Que s'est-il passé et pourquoi ?

Les noms de domaine utilisés par BNP Paribas sont tous dans le TLD .bnpparibas. On peut facilement vérifier que ce TLD va bien (un seul serveur ne répond pas) :

% check-soa -i bnpparibas
a0.nic.bnpparibas.
	2a01:8840:3e::9: OK: 1000002072 (241 ms)
	65.22.64.9: OK: 1000002072 (91 ms)
a2.nic.bnpparibas.
	65.22.67.9: OK: 1000002072 (3 ms)
	2a01:8840:41::9: OK: 1000002072 (3 ms)
b0.nic.bnpparibas.
	2a01:8840:3f::9: OK: 1000002072 (19 ms)
	65.22.65.9: OK: 1000002072 (27 ms)
c0.nic.bnpparibas.
	2a01:8840:40::9: OK: 1000002072 (21 ms)
	65.22.66.9: ERROR: read udp 10.10.86.133:33849->65.22.66.9:53: i/o timeout
    

Mais c'est en dessous qu'il y a des problèmes. En raison de règles ICANN absurdes, les noms publiés sont tous des sous-domaines de .bnpparibas, délégués à deux serveurs de noms :


% dig @a2.nic.bnpparibas. NS mabanqueprivee.bnpparibas
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51136
...
;; AUTHORITY SECTION:
mabanqueprivee.bnpparibas. 86400 IN NS sns5.bnpparibas.net.
mabanqueprivee.bnpparibas. 86400 IN NS sns6.bnpparibas.net.
...
;; SERVER: 2a01:8840:41::9#53(2a01:8840:41::9)
;; WHEN: Tue Jun 20 09:55:22 CEST 2017

    

Et, de ces deux serveurs de noms, l'un n'existe pas, l'autre est en panne (ou bien victime d'une attaque par déni de service) :

% check-soa -i -ns sns5.bnpparibas.net\ sns6.bnpparibas.net mabanqueprivee.bnpparibas
sns5.bnpparibas.net.
	159.50.249.65: ERROR: read udp 10.10.86.133:57342->159.50.249.65:53: i/o timeout
sns6.bnpparibas.net.
	Cannot get the IPv4 address: NXDOMAIN
    

On peut aussi tester avec dig, si vous préférez :


% dig @sns5.bnpparibas.net. NS mabanquepro.bnpparibas

; <<>> DiG 9.10.3-P4-Debian <<>> @sns5.bnpparibas.net. NS mabanquepro.bnpparibas
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached

% dig @sns6.bnpparibas.net. NS mabanquepro.bnpparibas
dig: couldn't get address for 'sns6.bnpparibas.net.': not found

    

C'est assez étonnant. Un des deux serveurs n'existe même pas dans le DNS. D'après DNSDB, il n'a même jamais existé ! Tous les noms de BNP Paribas (mabanqueprivee.bnpparibas, mabanquepro.bnpparibas, etc) dépendaient donc d'un seul serveur, qui a défailli ce matin.

Résultat, les utilisateurs ne pouvaient pas résoudre le nom en adresse et donc pas accéder au site Web ou à l'API. On le voit avec les sondes RIPE Atlas :

% atlas-resolve -r 100 mabanqueprivee.bnpparibas.
[ERROR: SERVFAIL] : 48 occurrences 
[TIMEOUT(S)] : 50 occurrences 
Test #8925333 done at 2017-06-20T07:55:45Z
    

La reprise a été partielle (plusieurs serveurs anycastés ? Attaque par déni de service qui se calme ?), un peu plus tard, on voit que certaines sondes réussissaient :

    
% atlas-resolve -r 100 mabanquepro.bnpparibas.
[159.50.188.21] : 3 occurrences 
[ERROR: SERVFAIL] : 51 occurrences 
[TIMEOUT(S)] : 40 occurrences 
Test #8925337 done at 2017-06-20T07:58:24Z

Pour le serveur inexistant, l'explication m'a été donnée par un anonyme (que je remercie beaucoup) : le vrai nom est sns6.bnpparibas.FR (et pas .NET). Ce serveur existe bien, répond, et fait en effet autorité pour les zones de la banque. Lors de la délégation de la plupart des sous-domaines, c'est simplement un mauvais nom qui a été indiqué !

Tout ceci n'est pas très conforme aux bonnes pratiques (telles qu'elles sont régulièrement rappelées, par exemple, dans le rapport annuel sur la résilience de l'Internet français) :

  • Deux serveurs DNS faisant autorité, ce n'est en général pas assez (surtout si un des deux n'existe en fait pas),
  • Les serveurs doivent être supervisés. Icinga ou Zabbix auraient prévenu depuis longtemps qu'un des deux serveurs ne marchait pas.
  • Les configurations DNS doivent être testées, par exemple avec Zonemaster.
  • Le TTL renvoyé par le serveur, lorsqu'il marche, est de seulement 30 secondes. C'est très insuffisant. En cas de panne imprévue, il faut facilement plusieurs heures pour réparer (l'essentiel du temps étant consacré à paniquer en criant).

Notez que, deux jours après la panne, rien n'a été réparé :

  • Les zones n'ont toujours qu'un seul serveur (celui inexistant ne comptant évidemment pas),
  • En prime, l'unique serveur répond incorrectement (il accepte les requêtes DNS de type A mais pas celles de types NS ou SOA).

Ces erreurs sont clairement visible sur les programmes de test DNS comme Zonemaster ou bien DNSviz.


L'article seul

Sur la communication quantique (et les exagérations)

Première rédaction de cet article le 19 juin 2017


Dans un article publié dans Science, un groupe de chercheurs annonce avoir réussi à transporter des photons intriqués sur plus de mille kilomètres. Mais ce n'est pas cette avancée scientifique intéressante que je veux commenter, mais les grossières exagérations avec lesquelles elle a été annoncée, notamment la promesse ridicule que la communication quantique allait donner un Internet plus sûr, voire un « Internet inviolable » (comme dit dans l'article sensationaliste de l'Express).

L'article en question promet en effet, outre l'Internet inviolable, de la téléportation, des « ordinateurs dotés d'une puissance de calcul sans commune mesure avec les plus puissantes machines actuelles » et même « un Internet basé à l'avenir sur les principes de la physique quantique ». Revenons au monde réel. L'article originel est bien plus modeste. (Au passage, il n'est pas disponible en ligne, car où irait-on si tout le monde pouvait accéder facilement aux résultats scientifiques ? Heureusement, on le trouve sur Sci-Hub, qu'on ne remerciera jamais assez.) Les auteurs ont réussi à transmettre des photons intriqués sur une distance plus grande que ce qui avait été fait avant. Beau résultat (l'opération est très complexe, et est bien décrite dans leur article) mais en quoi est-ce que ça nous donne un Internet inviolable ? L'idée est que la communication quantique est à l'abri de l'écoute par un tiers, car toute lecture, même purement passive, casserait l'intrication et serait facilement détectée. Sa sécurité est donc physique et non plus algorithmique. La NSA serait donc réduite au chômage ? C'est en tout cas ce que promet l'Express…

Il y a plusieurs raisons pour lesquelles cette promesse est absurde. La plupart de ces raisons découlent d'un fait simple que le journaliste oublie complètement dans son article : la communication de point à point n'est qu'une partie du système technique complexe qui permet à Alice et Bob de s'envoyer des messages de manière sécurisée. Si on ne considère pas la totalité de ce système, on va se planter. D'abord, citons « l'argument de Schneier », écrit il y a plus de huit ans et toujours ignoré par les vendeurs de solutions quantiques : la communication quantique ne sert à rien, pas parce qu'elle ne marche pas (au contraire, les progrès sont importants) mais parce qu'elle sécurise ce qui était déjà le maillon le plus fort du système. Quant on veut surveiller les gens, la plupart du temps, on ne va pas casser la cryptographie qui les protège. On va faire plus simple : exploiter une faille du protocole de communication, ou une bogue du logiciel qui le met en œuvre, ou tout simplement récupérer l'information à une des deux extrêmités (il ne sert à rien de faire de la communication quantique avec Google si Google fournit toutes vos données à la NSA). Avant de remplacer la cryptographie classique, il faudrait déjà sécuriser tous ces points. Autrement, le surveillant et l'espion, qui sont des gens rationnels, continueront à attaquer là où la défense est faible. Déployer de la communication quantique, c'est comme utiliser un camion blindé pour transmettre de l'argent entre deux entrepôts dont les portes ne ferment pas à clé. (C'est d'autant plus important que la communication quantique n'est pas de bout en bout : elle n'arrive pas jusqu'aux PC d'Alice et Bob.)

Et ce n'est pas la seule faiblesse de la communication quantique. La plus importante, à mon avis, est qu'elle n'authentifie pas. Vos messages ne peuvent pas être interceptés mais vous ne pouvez pas être sûr de savoir à qui vous parlez. Vous avez une communication sécurisée avec… quelqu'un, c'est tout. Cela rend la communication quantique très vulnérable à l'attaque de l'Homme du Milieu. Il existe bien sûr des remèdes contre cette attaque, mais tous dépendent de la cryptographie classique, avec ses faiblesses.

En parlant de cryptographie classique, notons aussi que la communication quantique est limitée à la distribution de clés. Vous avez toujours besoin d'un algorithme classique (comme AES) pour chiffrer.

Autre limite de la communication quantique : elle protège le canal, pas les données. Une fois les données « au repos » (sur les disques durs des ordinateurs d'Alice et Bob), elles sont aussi vulnérables qu'avant. Un piratage avec fuite de données (comme Ashley Madison, Yahoo, Sony…) se ferait aussi bien avec communication quantique que sans.

Qu'y avait-il encore comme promesses dénuées de sens dans l'article de l'Express ? Ah, oui, « des ordinateurs dotés d'une puissance de calcul sans commune mesure avec les plus puissantes machines actuelles ». Ici, il semble que l'auteur ait confondu la communication quantique avec le calcul quantique, qui n'a rien à voir. Et quant à la téléportation, elle relève plus de la science-fiction : l'expérience décrite dans Science n'a rien à voir avec de la téléportation, même limitée.

Voilà, comme d'habitude, on a une avancée scientifique réelle, décrite dans un article sérieux mais que personne ne lira, sur-vendue dans un dossier de presse aguicheur, lui-même repris sans critique et sans vérification dans de nombreux médias. Vu l'information reçue par les citoyens ordinaires, il n'est pas étonnant que la sécurité informatique soit actuellement si mauvaise.


L'article seul

dnstap, un journal de l'activité d'un serveur DNS

Première rédaction de cet article le 8 juin 2017


Comment voir en temps réel l'activité d'un serveur DNS, les requêtes qu'il reçoit et les réponses qu'il envoie ? Il existe plusieurs méthodes, cet article présent rapidement la plus récente, dnstap.

Est-ce que je présente d'abord dnstap, ou d'abord les autres solutions, afin d'expliquer leurs faiblesses et de dire pourquoi dnstap a été développé ? Je choisis d'être positif et de commencer par les bonnes choses : dnstap, ce qu'il fait, et comment.

dnstap est un moyen d'obtenir un flux d'informations de la part d'un serveur DNS (qu'il s'agisse d'un résolveur ou bien d'un serveur faisant autorité). Le serveur doit être modifié pour journaliser son activité, et configuré ensuite pour émettre un flux dnstap. Typiquement, ce flux est envoyé vers une prise Unix, où un lecteur dnstap l'attendra pour stocker ce flux, ou pour le formater et l'afficher joliment. Ce flux est encodé en Protocol Buffers, eux-mêmes transportés dans des Frame Streams.

C'est pas assez concret ? OK, alors jouons un peu avec un résolveur DNS, Unbound. On le configure ainsi :

 dnstap:
    dnstap-enable: yes
    dnstap-socket-path: "/var/run/unbound/dnstap.sock" 
    dnstap-send-identity: yes
    dnstap-send-version: yes
    dnstap-log-resolver-response-messages: yes
    dnstap-log-client-query-messages: yes
    

(Les quatre dernières lignes sont optionnelles. Ici, avec les deux dernières lignes, on ne journalise qu'une partie des requêtes et des réponses.) Et on relance le serveur. Il va dire quelque chose comme :

[1496927690] unbound[32195:0] notice: attempting to connect to dnstap socket /var/run/unbound/dnstap.sock
[1496927690] unbound[32195:0] notice: dnstap identity field set to "gpd-01"
[1496927690] unbound[32195:0] notice: dnstap version field set to "unbound 1.5.10"
[1496927690] unbound[32195:0] notice: dnstap Message/RESOLVER_RESPONSE enabled
[1496927690] unbound[32195:0] notice: dnstap Message/CLIENT_QUERY enabled
    

Si on l'interroge (par exemple avec dig), le serveur DNS va envoyer les messages dnstap à la prise, ici /var/run/unbound/dnstap.sock. Pour les lire, on va utiliser le client en ligne de commande dnstap :

% dnstap -q -u /var/run/unbound/dnstap.sock     
...
15:17:09.433521 CQ ::1 UDP 41b "witches.town." IN A
...
15:18:23.050690 CQ ::1 UDP 52b "fete.lutte-ouvriere.org." IN A
15:18:23.055833 RR 2001:4b98:abcb::1 UDP 147b "fete.lutte-ouvriere.org." IN A
    

Pour la première requête, l'information était dans le cache (la mémoire) du résolveur DNS. Il n'y a donc eu qu'une seule requête, depuis dig vers le résolveur (CQ = Client Query). Dans le second cas, la réponse ne se trouvait pas dans le cache, notre résolveur a dû aller demander à un serveur faisant autorité pour le domaine (en l'occurrence le serveur 2001:4b98:abcb::1, dont on voit la réponse au résolveur).

Si le cache est froid (le résolveur vient de démarrer), on verra que le résolveur devra faire plein d'autres requêtes (« requêtes tertiaires », dit le RFC 7626), ici, on a demandé www.sinodun.com, et le résolveur devra trouver les adresses IP des serveurs de .com (les serveurs de la racine étaient déjà connus, ici, c'est le serveur I.root-servers.net qui a répondu) :

11:47:39.556098 CQ ::1 UDP 44b "www.sinodun.com." IN A
11:47:39.560778 RR 192.36.148.17 UDP 1097b "." IN NS
11:47:39.598590 RR 192.112.36.4 UDP 867b "www.sinodun.com." IN A
11:47:39.605212 RR 2001:500:9f::42 UDP 852b "m.gtld-servers.net." IN AAAA
11:47:39.611999 RR 2001:503:a83e::2:30 UDP 789b "m.gtld-servers.net." IN AAAA
11:47:39.611999 RR 199.7.91.13 UDP 852b "l.gtld-servers.net." IN AAAA
11:47:39.611999 RR 192.35.51.30 UDP 789b "j.gtld-servers.net." IN AAAA
11:47:39.616442 RR 192.35.51.30 UDP 771b "av4.nstld.com." IN A
11:47:39.618318 RR 192.12.94.30 UDP 771b "av1.nstld.com." IN A
11:47:39.619118 RR 192.26.92.30 UDP 517b "www.sinodun.com." IN A
11:47:39.620192 RR 192.82.134.30 UDP 286b "av4.nstld.com." IN A
11:47:39.625671 RR 192.82.134.30 UDP 115b "m.gtld-servers.net." IN AAAA
11:47:39.628389 RR 192.54.112.30 UDP 789b "f.gtld-servers.net." IN AAAA
11:47:39.628974 RR 192.54.112.30 UDP 789b "d.gtld-servers.net." IN AAAA
    

Et si je veux voir les réponses, pas seulement les questions ? Demandons à dnstap d'être plus bavard :

% dnstap -y -u /var/run/unbound/dnstap.sock           
type: MESSAGE
identity: "gpd-01"
version: "unbound 1.5.10"
message:
  type: CLIENT_QUERY
  query_time: !!timestamp 2017-06-08 13:24:56.664846
  socket_family: INET6
  socket_protocol: UDP
  query_address: ::1
  query_port: 52255
  query_message: |
    ;; opcode: QUERY, status: NOERROR, id: 32133
    ;; flags: rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
    
    ;; QUESTION SECTION:
    ;www.kancha.de.	IN	 A
    
    ;; ADDITIONAL SECTION:
    
    ;; OPT PSEUDOSECTION:
    ; EDNS: version 0; flags: do; udp: 4096
---
type: MESSAGE
identity: "gpd-01"
version: "unbound 1.5.10"
message:
  type: RESOLVER_RESPONSE
  query_time: !!timestamp 2017-06-08 13:24:56.664838
  response_time: !!timestamp 2017-06-08 13:24:56.697349
  socket_family: INET
  socket_protocol: UDP
  response_address: 85.13.128.3
  response_port: 53
  query_zone: "kancha.de."
  response_message: |
    ;; opcode: QUERY, status: NOERROR, id: 6700
    ;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
    
    ;; QUESTION SECTION:
    ;www.kancha.de.	IN	 A
    
    ;; ANSWER SECTION:
    www.kancha.de.	7200	IN	A	85.13.153.248
    
    ;; ADDITIONAL SECTION:
    
    ;; OPT PSEUDOSECTION:
    ; EDNS: version 0; flags: do; udp: 1680
---
    

(Eh oui, c'est du YAML.) Notons quelques points importants :

  • C'est le client dnstap pas le résolveur DNS, qui formate ce résultat comme il veut. On peut donc écrire d'autres clients dnstap, qui feraient autre chose avec les données (calculer des résultats agrégés, présenter comme tcpdump, etc). C'est une propriété importante de dnstap : le serveur journalise, le client dnstap en fait ce qu'il veut.
  • dnstap permet d'ajouter des informations qui n'apparaissent à aucun moment dans la requête ou dans la réponse. C'est le cas de query_zone, qui indique le bailliage (la zone d'où vient l'information sur ce nom) ou des temps de réponse.

Bon, maintenant, quelques petits détails techniques sur ce qu'il a fallu faire pour obtenir cela. D'abord, Unbound, sur presque tous les systèmes, a été compilé sans dnstap, probablement pour ne pas ajouter une dépendance supplémentaire (vers les Protocol Buffers). Si vous essayez de configurer Unbound pour dnstap, vous aurez un « fatal error: dnstap enabled in config but not built with dnstap support ». Il faut donc le compiler soi-même, avec l'option --enable-dnstap. Cela nécessite d'installer Protocol Buffers et Frame Streams (paquetages Debian libprotobuf-c-dev et libfstrm-dev).

Quant au client dnstap, celui utilisé est écrit en Go et nécessite d'installer le paquetage golang-dnstap :

% go get github.com/dnstap/golang-dnstap
% go install github.com/dnstap/golang-dnstap/dnstap

Quelles sont les autres mises en œuvre dont on dispose ? Le serveur DNS faisant autorité Knot a dnstap (mais ce n'est pas le cas du résolveur du même nom). Le serveur BIND peut également être compilé avec dnstap (testé avec la 9.11.1). Comme client, il existe aussi un client fondé sur ldns (paquetage Debian dnstap-ldns).

Enfin, pour terminer, voyons les autres solutions qui existent pour afficher ce que fait le serveur DNS. La plus évidente est de demander au serveur de journaliser lui-même. Avec le formatage des données, cela peut imposer une charge sévère au serveur, alors qu'un serveur DNS doit rester rapide, notamment en cas d'attaque par déni de service (avec dnstap, le formatage à faire est minime, vu l'efficacité des Protocol Buffers). Comme le note la documentation d'Unbound, « note that it takes time to print these lines which makes the server (significantly) slower ». Mais il y a une autre limite : on ne peut voir que les requêtes, pas les réponses, ni les requêtes tertiaires.

Avec Unbound, cela se configure ainsi :

    
     log-queries: yes

Et, si on demande le MX de sonic.gov.so, on obtient :

Jun 08 15:53:01 unbound[2017:0] info: ::1 sonic.gov.so. MX IN
  

Avec BIND, c'est :

  
08-Jun-2017 16:22:44.548 queries: info: client @0x7f71d000c8b0 ::1#39151 (sonic.gov.so): query: sonic.gov.so IN MX +E(0)D (::1)

Une autre solution pour voir le trafic DNS, et celle que je recommandais avant, est de sniffer le trafic réseau et de l'analyser. Il vaut mieux ne pas faire cela sur le serveur, que cela peut ralentir, mais sur une autre machine, avec du port mirroring pour envoyer une copie du trafic à la machine d'observation. Cette technique a le gros avantage de n'imposer absolument aucune charge au serveur. Mais elle a quatre défauts :

  • Il faut réassembler les réponses fragmentées, phénomène relativement fréquent avec le DNS, où les données peuvent dépasser la MTU et où l'utilisation d'UDP fait que la couche transport ne découpe pas les données.
  • Il faut reconstituer le trafic TCP, qui se retrouve réparti en plusieurs paquets. Avec le RFC 7766, l'utilisation de TCP deviendra sans doute de plus en plus fréquente pour le DNS.
  • Enfin, si le trafic est chiffré (RFC 7858), le sniffer est aveugle.
  • Certaines choses (comme la bailliage) ne se voient pas du tout dans le trafic, seul le serveur les connait.

Les trois premières raisons sont relativement récentes (autrefois, on ne chiffrait pas, on n'utilisait pas TCP, et les données étaient plus petites) et justifient le retour à un système où le serveur va devoir faire une partie du travail.


L'article seul

RFC 8179: Intellectual Property Rights in IETF Technology

Date de publication du RFC : Mai 2017
Auteur(s) du RFC : Scott Bradner (Harvard University), Jorge Contreras (University of Utah)
Première rédaction de cet article le 1 juin 2017


L'appropriation intellectuelle est partout et donc logiquement aussi dans les organismes de normalisation comme l'IETF. C'est l'objet de ce RFC, qui remplace les RFC 3979 et RFC 4879 sur les questions de brevets (nommées, à tort, questions de « propriété intellectuelle », alors que les brevets ne sont pas la même chose que les copyrights, traités dans le RFC 5378).

Donc, sur quels principes repose la politique de l'IETF au sujet des brevets ? L'idée de base est de s'assurer que l'IETF disposera d'information sur les brevets pouvant s'appliquer à une norme donnée, de façon à pouvoir prendre une décision en toute connaissance de cause. Il n'y a par contre pas de mécanisme automatique de décision, par exemple « Ne jamais normaliser des technologies brevetées ». En effet, compte-tenu du fait que l'écrasante majorité des brevets logiciels est futile, enregistrée uniquement parce que les organismes de brevetage ont un intérêt financier à accepter tout et n'importe quoi, une telle politique mènerait à ne rien pouvoir normaliser.

En pratique, tout ce RFC 8179 pourrait donc se résumer à « Tout participant à l'IETF qui connait ou devrait connaitre un brevet pouvant s'appliquer à une technique en cours de discussion doit en informer l'IETF ». C'est tout. Mais il y a quelques détails pratiques.

D'abord, il faut rappeler que ce sont officiellement des individus qui participent à l'IETF, pas des sociétés. Donc l'obligation s'applique à ces individus et ils ne peuvent pas y échapper en prétendant que leur compagnie leur interdit de réveler un brevet sous-marin (brevet sur lequel on fait peu de publicité, pour le ressortir une fois que la technique brevetée a été largement adoptée). Ensuite, le RFC définit ce que signifie contribuer à l'IETF (section 1). Par exemple, écrire sur une liste de diffusion d'un groupe de travail est une contribution. Cette règle est régulièrement rappelée par le fameux Note Well.

La section 1 définit formellement bien d'autres choses. Un concept essentiel mais souvent oublié est le Reasonably and personally known. Il désigne une information que le participant connait ou devrait connaitre, vu sa position dans l'entreprise qui l'emploie. L'idée est que le participant IETF n'est pas obligé de chercher activement dans le portefeuille de brevets de son entreprise, que l'obligation ne s'applique qu'à ce qu'il connait forcément, depuis son poste. Le but de l'ajout reasonably est d'éviter qu'une entreprise ne dissimule un brevet à ses propres employés.

Les principes sont donc :

  • L'IETF ne va pas chercher à déterminer si un brevet est futile ou pas (cela peut être un très gros travail, la plupart des brevets étant rédigés en termes délibérement incompréhensibles),
  • L'IETF peut normaliser ou pas une technique brevetée, il n'y a pas de refus systématique,
  • Pour pouvoir néanmoins savoir où on va, l'IETF a besoin d'information et c'est de là que découle l'exigence de divulgation des brevets, la principale obligation concrète de ce RFC 8179.

La section 3 rentre dans le concret, même si elle commence par un bel exercice de langue de bois (« The intent is to benefit the Internet community and the public at large, while respecting the legitimate rights of others. »). C'est elle qui impose que le contributeur à l'IETF ait bien divulgué tous les brevets qu'il connaissait « raisonnablement ». Outre le brevet lui-même, il peut y avoir une licence associée (un droit d'utiliser la technologie brevetée, sous certaines conditions). Si le détenteur du brevet n'indique pas de licence, l'IETF peut poliment lui demander. La licence (RAND, FRAND, RANDZ - c'est-à-dire gratuite …) sera évidemment un des éléments sur lesquels les participants à l'IETF fonderont leur position (cf. RFC 6410).

La section 4 indique ce que l'IETF va en faire, de ces divulgations (nommées « IPR [Intellectual Property Rights] disclosures ») : indication du fait qu'il existe des brevets pouvant s'y appliquer et publication de ces divulgations en http://www.ietf.org/ipr/. Par exemple, Verisign a un brevet (brevet états-unien 8,880,686, et la promesse de licence de Verisign) qu'ils prétendent valable, et dont ils affirment qu'il couvre la technique décrite dans le RFC 7816. (Sur la page officielle du RCF, c'est le lien « Find IPR Disclosures from the IETF ».) L'IETF (ou l'IAB, ou l'ISOC ou autre) n'ajoutera aucune appréciation sur la validité du brevet, ou sur les conditions de licence. Une telle appréciation nécessiterait en effet un long et coûteux travail juridique.

La note d'information n'est plus à inclure dans chaque RFC comme c'était autrefois le cas. Elle est désormais dans le IETF Trust Legal Provisions (version de 2015 : « The IETF Trust takes no position regarding the validity or scope of any Intellectual Property Rights or other rights that might be claimed to pertain to the implementation or use of the technology described in any IETF Document or the extent to which any license under such rights might or might not be available; nor does it represent that it has made any independent effort to identify any such rights. »). Comme exemple d'un brevet abusif, on peut citer la divulgation #1154, qui se réclame d'un brevet sur les courbes elliptiques qui s'appliquerait à tous les RFC parlant d'un protocole qui peut utiliser ces courbes, comme le RFC 5246.

Les divulgations ne sont pas incluses dans les RFC eux-mêmes (section 10) car elles peuvent évoluer dans le temps alors que le RFC est stable. Il faut donc aller voir ces « IPR disclosures » en ligne sur http://www.ietf.org/ipr/.

Les divulgations sont-elles spécifiées plus en détail ? Oui, en section 5. La 5.1 précise qui doit faire la divulgation (le participant, en tant que personne physique), la section 5.2 donne les délais (« aussi vite que possible »), la section 5.4.3 rappelle que la divulgation doit être précise et qu'un contributeur ne peut pas se contenter de vagues généralités (« blanket disclosure »). Le tout est aussi mis en ligne, en http://www.ietf.org/ipr-instructions.

Et si un tricheur, comme la société RIM, ne respecte pas cette obligation de divulgation ? La section 6 ne prévoit aucune dérogation : si, par exemple, une société empêche ses employés de divulguer les brevets, ces employés ne doivent pas participer à l'IETF (« tu suis les règles, ou bien tu ne joues pas »). Tout participant à l'IETF est censé connaitre cette règle (section 3.3). Le RFC 6701 liste les sanctions possibles contre les tricheurs et le RFC 6702 expose comment encourager le respect des règles.

Bien, donc, arrivé là, l'IETF a ses informations et peut prendre ses décisions. Sur la base de quelles règles ? La section 7 rappelle le vieux principe qu'une technique sans brevets est meilleure ou, sinon, à la rigueur, une technique où le titulaire des brevets a promis des licences gratuites. Mais ce n'est pas une obligation, l'IETF peut choisir une technologie brevetée, même sans promesses sur la licence, si cette technologie en vaut la peine.

La seule exception concerne les techniques de sécurité obligatoires : comme tout en dépend, elles ne doivent être normalisées que s'il n'existe pas de brevet ou bien si la licence est gratuite.

Les règles de bon sens s'appliquent également : s'il s'agit de faire une nouvelle version normalisée d'un protocole très répandu, on évitera de choisir une technologie trop encombrée de brevets, s'il s'agit d'un tout nouveau protocole expérimental, on pourra être moins regardant.

Les changements depuis les RFC précédents, les RFC 3979 et RFC 4879, sont décrits dans la section 13. Pas de révolution, les principes restent les mêmes. Parmi les changements :

  • Texte modifié pour permettre l'utilisation de ces règles en dehors de la voie IETF classique (par exemple par l'IRTF).
  • La définition d'une « contribution IETF » a été élargie pour inclure, par exemple, les interventions dans les salles XMPP de l'IETF.
  • Meilleure séparation des questions de brevets (traitées dans notre RFC) avec celles de droit d'auteur (traitées dans le RFC 5378). Le terme de « propriété intellectuelle » a plusieurs défauts, dont celui de mêler des choses très différentes (brevets, marques, droit d'auteur…)
  • Il n'y a plus de boilerplate (qui était en section 5 du RFC 3979) à inclure dans les documents, il est désormais en ligne.

Téléchargez le RFC 8179


L'article seul

RFC 8111: Locator/ID Separation Protocol Delegated Database Tree (LISP-DDT)

Date de publication du RFC : Mai 2017
Auteur(s) du RFC : V. Fuller, D. Lewis, V. Ermagan (Cisco), A. Jain (Juniper Networks), A. Smirnov (Cisco)
Expérimental
Réalisé dans le cadre du groupe de travail IETF lisp
Première rédaction de cet article le 27 mai 2017


Le protocole LISP (dont on ne rappelera jamais assez qu'il ne faut pas le confondre avec le langage de programmation du même nom) sépare les deux rôles de l'adresse IP, identificateur et localisateur. C'est très joli de séparer, cela permet plein de choses intéressantes, comme de lutter contre la croissance illimitée de la DFZ mais cela présente un défi : comment obtenir un localisateur quand on a un identificateur ? Dit autrement, « où est cette fichue machine que j'essaie de joindre ? » Ajouter une indirection, en informatique, oblige toujours à créer un système de correspondance permettant de passer par dessus le fossé qu'on vient juste de créer. LISP a plusieurs systèmes de correspondance possibles, tous expérimentaux, et ce nouveau DDT (Delegated Database Tree) vient les rejoindre. C'est le système qui est le plus proche du DNS dans ses concepts. Comme je connais un peu le DNS, j'utiliserai souvent dans cet article des comparaisons avec le DNS.

Pour résumer DDT en un paragraphe : dans LISP (RFC 6830), l'identificateur se nomme EID (Endpoint Identifier) et le localisateur RLOC (Routing Locator). Les EID ont une structure arborescente (leur forme syntaxique est celle d'adresses IP). Cet arbre est réparti sur plusieurs serveurs, les nœuds DDT. Un nœud DDT fait autorité pour un certain nombre de préfixes d'EID. Il délègue ensuite les sous-préfixes à d'autres nœuds DDT, ou bien à des Map Servers LISP (RFC 6833) quand on arrive en bas de l'arbre. (Une des différences avec le DNS est donc que les serveurs qui délèguent sont d'une nature distincte de ceux qui stockent les feuilles de l'arbre.)

LISP a une interface standard avec les serveurs qui font la résolution d'EID en RLOC, décrite dans le RFC 6833. En gros, le client envoie un message Map-Request et obtient une réponse Map-Reply, ou bien une délégation (Map-Referral) qu'il va devoir suivre en envoyant le Map-Request suivant au RLOC indiqué dans la délégation. Derrière cette interface, LISP ne spécifie pas comment les serveurs obtiennent l'information. Plusieurs propositions ont déjà été faites (comme ALT, dans le RFC 6836, ou NERD, dans le RFC 6837…), auxquelles s'ajoute celle de notre RFC. Un bon résumé est dans cette image (mais qui ne montre qu'un seul niveau de délégation, il peut y en avoir davantage.)

DDT vise avant tout le passage à l'échelle, d'où la structuration hiérarchique de l'information. La notion de délégation (d'un préfixe général à un sous-préfixe plus spécifique) est centrale dans DDT. Un client (le routeur LISP qui a un paquet destiné à un EID donné et qui cherche à quel RLOC le transmettre, ou bien un résolveur, un serveur spécialisé agissant pour le compte de ce routeur) va donc devoir suivre cette délégation, descendant l'arbre jusqu'à l'information souhaitée.

La délégation est composée, pour un préfixe délégué, d'un ensemble de RLOC (pas d'EID pour éviter des problèmes d'œuf et de poule) désignant les nœuds qui ont une information sur le préfixe délégué. (Ce sont donc l'équivalent des enregistrements NS du DNS, mais avec une indirection en moins, comme si la partie droite d'un enregistrement NS stockait directement une adresse IP.)

J'ai écrit jusque là que la clé d'accès à l'information (rôle tenu par le nom de domaine dans le DNS) était l'EID mais c'est en fait un peu plus compliqué : la clé est le XEID (eXtended EID), qui est composé de plusieurs valeurs, dont l'EID (section 4.1 de notre RFC).

Pour indiquer au résolveur qu'il doit transmettre sa requête à une autre machine, ce RFC crée un nouveau type de message LISP, Map-Referral, type 6 (cf. le registre IANA) détaillé en section 6, envoyé en réponse à un Map-Request, quand le nœud DDT ne connait pas la réponse. (Comme indiqué plus haut, c'est l'équivalent d'une réponse DNS avec uniquement une section Autorité contenant des enregistrements NS.)

Continuons un peu la terminologie (section 3 du RFC) :

  • Un client DDT est une machine qui interroge les nœuds DDT (avec un Map-Request, cf. RFC 6833) et suit les Map-Referral jusqu'au résultat. C'est en général un résolveur (Map Resolver, RFC 6833) mais cela peut être aussi un routeur LISP (ITR, Ingress Tunnel Router).
  • Un résolveur est serveur d'un côté, pour les routeurs qui envoient des Map-Request, et client DDT de l'autre, il envoie des requêtes DDT. Il gère un cache (une mémoire des réponses récentes). Le résolveur maintient également une liste des requêtes en cours, pas encore satisfaites.

La base des données des serveurs DDT est décrite en section 4. Elle est indexée par XEID. Un XEID est un EID (identificateur LISP) plus un AFI (Address Family Identifier, 1 pour IPv4, 2 pour IPv6, etc), un identificateur d'instance (voir RFC 6830, section 5.5, et RFC 8060, section 4.1) qui sert à avoir plusieurs espaces d'adressage, et quelques autres paramètres, pas encore utilisés. Configurer un serveur DDT, c'est lui indiquer la liste de XEID qu'il doit connaitre, avec les RLOC des serveurs qui pourront répondre. Désolé, je n'ai pas de serveur DDT sous la main mais on peut trouver un exemple, dans la documentation de Cisco, où on délègue au Map Server de RLOC 10.1.1.1 :

router lisp
    ddt authoritative 2001:db8:eeee::/48
          delegate 10.1.1.1 eid-prefix 172.16.0.0/16
          delegate 10.1.1.1 eid-prefix 2001:db8:eeee::/48
    

Un autre exemple de délégation est l'actuelle liste des données dans la racine DDT.

Le DNS n'a qu'un type de serveurs faisant autorité, qu'ils soient essentiellement serveurs de délégation (ceux des TLD, par exemple) ou qu'ils soient serveurs « finaux » contenant les enregistrements autres que NS. Au contraire, LISP+DDT a deux types de serveurs, les nœuds DDT présentés dans ce RFC, qui ne font que de la délégation, et les traditionnels Map Servers, qui stockent les correspondances entre EID et RLOC (entre identificateurs et localisateurs). Dit autrement, DDT ne sert pas à trouver la réponse à la question « quel est le RLOC pour cet EID », il sert uniquement à trouver le serveur qui pourra répondre à cette question.

Comme pour le DNS, il existe une racine, le nœud qui peut répondre (enfin, trouver une délégation) pour tout XEID. (Sur le Cisco cité plus haut, la directive ddt root permettra d'indiquer le RLOC des serveurs de la racine, voir aussi la section 7.3.1 de notre RFC.) Une racine expérimentale existe, vous trouverez ses RLOC en http://ddt-root.org/.

La section 5 de notre RFC décrit en détail la modification au message Map-Request que nécessite DDT. Ce message était normalisé par le RFC 6830 (section 6.1.2) et un seul ajout est fait : un bit qui était laissé vide sert désormais à indiquer que la requête ne vient pas directement d'un routeur LISP mais est passée par des nœuds DDT.

La section 6, elle, décrit un type de message nouveau, Map-Referral, qui contient les RLOC du nœud DDT qui pourra mieux répondre à la question. Cette réponse contient un code qui indique le résultat d'un Map-Request. Ce résultat peut être « positif » :

  • NODE-REFERRAL, renvoi à un autre nœud DDT,
  • MS-REFERRAL, renvoi à un Map Server (rappelez-vous que, contrairement au DNS, il y a une nette distinction entre nœud intermédiaire et Map Server final),
  • MS-ACK, réponse positive d'un Map Server.

Mais aussi des résultats « négatifs » :

  • MS-NOT-REGISTERED, le Map Server ne connait pas cet EID,
  • DELEGATION-HOLE, l'EID demandé tombe dans un trou (préfixe non-LISP dans un préfixe LISP),
  • NOT-AUTHORITATIVE, le nœud DDT n'a pas été configuré pour ce préfixe.

Le fonctionnement global est décrit en détail dans la section 7 du RFC. À lire si on veut savoir exactement ce que doivent faire le Map Resolver, le Map Server, et le nouveau venu, le nœud DDT. La même description figure sous forme de pseudo-code dans la section 8. Par exemple, voici ce que doit faire un nœud DDT lorsqu'il reçoit un Map-Request (demande de résolution d'un EID en RLOC) :

    if ( I am not authoritative ) {
        send map-referral NOT_AUTHORITATIVE with
         incomplete bit set and ttl 0
    } else if ( delegation exists ) {
        if ( delegated map-servers ) {
            send map-referral MS_REFERRAL with
              ttl 'Default_DdtNode_Ttl'
        } else {
            send map-referral NODE_REFERRAL with
              ttl 'Default_DdtNode_Ttl'
        }
    } else {
        if ( eid in site) {
            if ( site registered ) {
                forward map-request to etr
                if ( map-server peers configured ) {
                    send map-referral MS_ACK with
                     ttl 'Default_Registered_Ttl'
                } else {
                    send map-referral MS_ACK with
                     ttl 'Default_Registered_Ttl' and incomplete bit set
                }
            } else {
                if ( map-server peers configured ) {
                    send map-referral MS_NOT_REGISTERED with
                     ttl 'Default_Configured_Not_Registered_Ttl'
                } else {
                    send map-referral MS_NOT_REGISTERED with
                     ttl 'Default_Configured_Not_Registered_Ttl'
                     and incomplete bit set
                }
            }
        } else {
            send map-referral DELEGATION_HOLE with
             ttl 'Default_Negative_Referral_Ttl'
        }
    }      
    

Un exemple complet et détaillé figure dans la section 9, avec description de tous les messages envoyés.

Question sécurité, je vous renvoie à la section 10 du RFC. DDT dispose d'un mécanisme de signature des messages (l'équivalent de ce qu'est DNSSEC pour le DNS). La délégation inclut les clés publiques des nœuds à qui on délègue.

Il existe au moins deux mises en œuvre de DDT, une chez Cisco et l'autre chez OpenLisp. (Le RFC ne sort que maintenant mais le protocole est déployé depuis des années.)


Téléchargez le RFC 8111


L'article seul

RFC 8112: Locator/ID Separation Protocol Delegated Database Tree (LISP-DDT) Referral Internet Groper (RIG)

Date de publication du RFC : Mai 2017
Auteur(s) du RFC : D. Farinacci (lispers.net), A. Jain (Juniper Networks), I. Kouvelas, D. Lewis (cisco Systems)
Expérimental
Première rédaction de cet article le 27 mai 2017


Ce RFC concerne les utilisateurs de LISP (le protocole réseau, pas le langage de programmation). Il décrit un nouvel outil, rig, le Referral Internet Groper, qui permet d'interroger les tables de correspondance identificateur->localisateur.

Un point important de LISP (RFC 6830) est en effet cette séparation de l'EID (l'identificateur d'une machine) et du RLOC (le localisateur de cette machine, qui indique où envoyer les paquets). Tout système ayant cette séparation doit maintenir une correspondance (mapping) entre les deux : lorsque je veux écrire à telle machine dont je connais l'EID, il faut que je trouve le localisateur. LISP permet plusieurs mécanismes pour cette correspondance. L'outil rig, présenté dans ce RFC, est conçu pour le mécanisme DDT (RFC 8111), une base de données arborescente et répartie. rig est donc un client DDT de déboguage, lig (RFC 6835) étant un autre outil, plus général (il peut interroger d'autres bases que DDT).

Un client DDT comme rig (ou comme un routeur LISP lors de son fonctionnement normal) va donc envoyer des Map-Request (RFC 6830, section 6.1, et aussi RFC 6833) aux serveurs DDT.

La section 4 de notre RFC résume le fonctionnement de rig. Il envoie le Map-Request et affiche le Map-Referral de réponse. Il peut ensuite suivre cette référence jusqu'à arriver au Map Server qui gère ce préfixe. (Notez que c'est le RLOC du Map Server qu'on obtient, sinon, on aurait un intéressant problème d'œuf et de poule si on avait besoin de DDT pour utiliser DDT...)

rig a donc besoin d'au moins deux paramètres, l'EID (l'identificateur) qu'on cherche à résoudre, et le serveur DDT par lequel on va commencer la recherche. (Pour l'EID, rig accepte également un nom de domaine, qu'il va traduire en EID dans le DNS.) La syntaxe typique est donc :

rig <eid> to <ddt-node>
    

La section 5 décrit les mises en œuvres existantes, sur les routeurs Cisco. La syntaxe est un peu différente de ce que je trouve dans la doc' de Cisco mais, bon, tout ceci est expérimental et en pleine évolution. Voici un exemple tiré de la documentation officielle de Cisco (LISP DDT Configuration Commands) :

Device# lisp-rig 172.16.17.17 to 10.1.1.1

rig LISP-DDT hierarchy for EID [0] 172.16.17.17 
Send Map-Request to DDT-node 10.1.1.1 ... replied, rtt: 0.007072 secs
  EID-prefix [0] 172.16.17.16/28, ttl: 1, action: ms-not-registered, referrals:
    10.1.1.1, priority/weight: 0/0
    10.2.1.1, priority/weight: 0/0
    10.3.1.1, priority/weight: 0/0
    

Et voilà, on sait que l'EID 172.16.17.17, il faut aller demander aux serveurs 10.1.1.1, 10.2.1.1 et 10.3.1.1. Dans le RFC, on trouve un exemple où rig suit ces références :

   Router# rig 12.0.1.1 to 1.1.1.1 

   Send Map-Request to DDT-node 1.1.1.1 ... node referral, rtt: 4 ms
   EID-prefix: [0] 12.0.0.0/16, ttl: 1440
   referrals: 2.2.2.2

   Send Map-Request to DDT-node 2.2.2.2 ... node referral, rtt: 0 ms
   EID-prefix: [0] 12.0.1.0/24, ttl: 1440
   referrals: 4.4.4.4, 5.5.5.5

   Send Map-Request to DDT-node 4.4.4.4 ... map-server acknowledgement,
                                            rtt: 0 ms
   EID-prefix: [0] 12.0.1.0/28, ttl: 1440
   referrals: 4.4.4.4, 5.5.5.5
    

Si vous voulez en savoir plus sur DDT et rig, vous pouvez aussi regarder l'exposé de Cisco ou celui de Paul Vinciguerra à NANOG, ou bien la page officielle de la racine DDT (qui semble peu maintenue).


Téléchargez le RFC 8112


L'article seul

L'axe des Y doit partir de zéro !

Première rédaction de cet article le 22 mai 2017
Dernière mise à jour le 23 mai 2017


On voit souvent dans les infographies des graphiques où l'axe des Y (axe des ordonnées) ne part pas de zéro. Pourquoi faut-il appeler la malédiction de tous les démons connus et inconnus sur les auteurs de ces graphiques ?

Parce que cela sert à tromper. Mettre comme point de départ une valeur différente de zéro tend à amplifier artificiellement un phénomène. Imaginons une grandeur qui varie assez peu, disons entre (unités arbitraires) 650 et 660. Si on la représente sur un graphique qui part de 0, la variation semblera faible. Si l'axe des Y part de la valeur 650, on aura l'impression de grandes variations.

Un bon exemple est la dispositive n° 11 de cet exposé : elle donne l'impression d'une envolée de la dette, en laissant entendre qu'on partait de zéro, alors que l'augmentation n'a été que de 30 % : iif-debt.jpg

Un autre exemple est ce graphique de la croissance de l'ether, où le fait de ne pas partir de zéro donne l'impression d'une croissance encore plus spectaculaire : ether-growth.png

Même s'il y a une échelle sur l'axe des Y (certains graphiques n'en ont même pas), l'œil pressé n'y fait pas attention (on en voit, des graphiques, dans une journée…) et retient une fausse impression.

Cette tromperie est bien illustrée dans ce dessin de William Easterly : y-axis.jpg

Cette règle de partir de zéro est-elle absolue ? Non, évidemment. D'abord, évidemment, si l'échelle est logarithmique, elle ne va évidemment pas partir de zéro. Ensuite, il y a des cas où cela peut être logique, par exemple s'il existe une justification liée à la nature du phénomène mesuré. Si on fait un graphique de la température du corps humain, il est plus logique de partir de 35 ou 36° que de 0, puisque la température du corps ne va jamais se promener aussi bas. Et, bien sûr, on peut vouloir mettre en évidence des petites variations (qui seraient lissées si l'axe des Y partait de zéro) sans intention de tromper. Mais je soupçonne que de tels cas sont très minoritaires.


L'article seul

RFC 8174: RFC 2119 Key Words: Clarifying the Use of Capitalization

Date de publication du RFC : Mai 2017
Auteur(s) du RFC : B. Leiba (Huawei)
Première rédaction de cet article le 19 mai 2017
Dernière mise à jour le 20 mai 2017


Un très court RFC discutant un problème de procédure : dans le RFC 2119, qui décrit les termes précis à utiliser dans les normes, un doute subsistait sur la casse de ces termes.

Ce RFC 2119 est celui qui formalise les fameux MUST, SHOULD et MAY, les termes qu'il faut utiliser dans les normes pour être sûr d'indiquer le niveau exact d'exigence. Suivant l'exemple du RFC 2119, ils sont toujours écrits en CAPITALES pour les distinguer du sens courant en anglais, mais cet usage n'était pas explicite dans le RFC 2119 (qui a juste un vague « These words are often capitalized »). Un oubli que corrige notre RFC 8174. Désormais, MUST n'a le sens du RFC 2119 que s'il est en capitales.

Par exemple, dans le RFC 8120, dans le texte « The client SHOULD try again to construct a req-KEX-C1 message in this case », SHOULD est en capitales et a donc bien le sens précis du RFC 2119 (le client est censé ré-essayer de faire son message, sauf s'il a une très bonne raison), alors que dans le texte « This case should not happen between a correctly implemented server and client without any active attacks », should est en minuscules et a donc bien son sens plus informel qui est usuel en anglais.

Le texte qu'il est recommandé d'inclure dans les RFC qui font référence au RFC 2119 apporte désormais cette précision : « The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119],[RFC 8174] when, and only when, they appear in all capitals, as shown here. » Plusieurs auteurs de RFC, conscients de l'ambiguité, avaient d'ailleurs déjà fait une telle modification dans leur référence au RFC 2119. Ainsi, le RFC 5724 dit « The _capitalized_ [souligné par moi] key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119]. »

Notez que le fait de faire une différence sémantique entre le mot en minuscules et le mot en capitales est assez typique des utilisateurs de l'alphabet latin, et déroute toujours beaucoup les utilisateurs d'écritures qui n'ont pas cette distinction, comme les Coréens.


Téléchargez le RFC 8174


L'article seul

RFC 8170: Planning for Protocol Adoption and Subsequent Transitions

Date de publication du RFC : Mai 2017
Auteur(s) du RFC : D. Thaler (Microsoft)
Pour information
Première rédaction de cet article le 18 mai 2017


L'Internet existe depuis de nombreuses années (le nombre exact dépend de la façon dont on compte…) et, pendant tout ce temps, les protocoles utilisés ne sont pas restés identiques à eux-mêmes. Ils ont évolué, voire ont été remplacés. Cela soulève un problème : la transition entre l'ancien et le nouveau (le cas le plus fameux étant évidemment le passage d'IPv4 à IPv6…) Beaucoup de ces transitions se sont mal passées, parfois en partie car l'ancien protocole ou l'ancienne version n'avait pas prévu son futur remplacement. Contrairement à ce qu'espèrent souvent les techniciens, il ne suffit pas d'incrémenter le numéro de version pour que tous les acteurs adoptent la nouvelle version. Ce nouveau RFC de l'IAB raconte les leçons tirées, et regarde comment on pourrait améliorer les futures transitions.

Ce RFC se focalise sur les transitions techniques. Ce ne sont évidemment pas les seules (il y a par exemple des transitions organisationnelles) mais ce sont celles qui comptent pour l'IAB et l'IETF. Une transition peut être aussi bien le déploiement d'un tout nouveau protocole, que le passage d'un protocole d'une version à une autre. Le thème de la transition d'un protocole à l'autre est fréquent, et de nombreux RFC ont déjà été consacrés à une transition. C'est le cas de :

  • Le RFC 3424, qui parlait des techniques de contournement des NAT, en insistant sur le fait qu'elles devaient avoir un caractère provisoire, et ne pas ossifier encore plus l'Internet,
  • Le RFC 4690 qui parlait de la transition d'une version d'Unicode à l'autre, dans le contexte des IDN,
  • La déclaration de l'IAB sur NAT-PT, qui critiquait une méthode de transition vers IPv6.

Outre les transitions à proprement parler, l'IAB s'est déjà penché sur les principes qui faisaient qu'un protocole pouvait marcher ou pas. C'est notamment le cas de l'excellent RFC 5218 qui étudie les facteurs qui font d'un protocole un échec, un succès, ou un succès fou. Parmi les leçons tirées par ce RFC 5218, les concepteurs d'un protocole devraient s'assurer que :

  • Les bénéfices sont pour celui qui assume les coûts. Dans un réseau, coûts et bénéfices ne sont pas forcément alignés. Par exemple, le déploiement de BCP 38 bénéficie aux concurrents de celui qui paie, ce qui explique le manque d'enthousiasme des opérateurs. Notez que coûts et bénéfices ne sont pas déterminés par les lois physiques, ils peuvent être changés par la loi (amendes pour ceux qui ne déploient pas BCP 38, ou à l'inverse code source gratuitement disponible et payé par l'argent public, comme cela avait été le cas pour encourager le déploiement de TCP/IP).
  • Le protocole est déployable de manière incrémentale (dans un réseau comme l'Internet, qui représente des investissements énormes, toute solution qui nécessite de jeter tout l'existant d'un coup est condamnée d'avance).
  • Le coût total est raisonnable. Il ne faut pas seulement regarder le prix des machines et d'éventuelles licences logicielles. Il faut aussi tenir compte de la formation, des changements de pratiques, des conséquences juridiques…

Le RFC 7305 discutait également des aspects économiques de la transition et notait l'importance de donner une carotte aux premiers à adopter le nouveau protocole, ceux qui font un pari risqué. C'est pour cela qu'il est parfaitement légitime que les premiers à avoir cru dans Bitcoin aient reçu une quantité importante de bitcoins à un prix très faible. Cette décision était une des meilleures prises par Satoshi Nakamoto. Ce RFC note aussi l'importance d'un partenariat avec des organisations qui peuvent aider ou contrarier la transition (comme les RIR ou l'ICANN).

La section 2 de notre RFC rappelle que, de toute façon, le terme « transition » risque d'être mal interprété. Il n'est plus possible depuis longtemps de faire un flag day dans l'Internet, un jour J où on change toutes les machines d'un coup de manière coordonnée. Les transitions sont donc forcément longues, avec une période de co-existence entre l'ancien et le nouveau.

Si l'ancien et le nouveau protocole ne peuvent pas interopérer directement (cas d'IPv4 et d'IPv6), il faudra parfois envisager un mécanisme de traduction (qui ne se situera pas forcément dans la même couche). Un tel traducteur, s'il est situé sur le chemin entre les deux machines, pose souvent d'ennuyeux problèmes de sécurité car il risque fort de casser le modèle de bout en bout.

La section 5 de notre RFC est consacrée aux plans de transition. Ah, les plans… Ils sont évidemment indispensables (on ne va pas se lancer dans une grande transition sans avoir planifié un minimum) mais ils sont aussi très fragiles (comme disent les militaires, « aucun plan ne survit à la première rencontre avec l'ennemi »), et ils terminent souvent au musée des mauvaises idées. Disons qu'il faut avoir un plan, mais ne pas en être esclave.

Quelles sont les qualités d'un bon plan de transition, en s'appuyant sur les expériences ratées et réussies ? D'abord, il faut bien connaitre l'existant. Par exemple, si l'ancien protocole a une fonction optionnelle qui n'a pas d'équivalent, ou un équivalent très différent dans le nouveau protocole, il est bon de savoir si cette fonction est utilisée en pratique (elle peut même ne pas être implémentée du tout, ce qui facilite les choses). De même, il est important de savoir si les logiciels existants mettent réellement en œuvre l'ancien protocole tel qu'il est spécifié, ou bien si, en pratique, ils en dévient, et ont des comportements qui vont poser des problèmes pendant la transition. (Un cas typique est celui de SSL où la plupart des programmes n'avaient pas mis en œuvre correctement le mécanisme de négociation, et plantaient donc lorsqu'une nouvelle version arrivait.)

Un autre élément important d'un plan de transition est d'avoir les idées claires sur les incitations à migrer. Les acteurs de l'Internet utilisent l'ancien protocole. Ça marche pour eux. Pourquoi feraient-ils l'effort de migrer vers un nouveau protocole, ce qui leur coûtera du temps et de l'argent ? Il faut donc des incitations (ou du marketing, qui arrive souvent à faire acheter un gadget inutile). Il n'y a pas que les coûts financiers directs, il faut aussi regarder d'autres problèmes à surmonter (par exemple l'hostilité de certains acteurs, ainsi le chiffrement a du mal à se répandre car les acteurs de l'Internet qui font de la surveillance ont intérêt à continuer à violer la vie privée).

Il y a ensuite le plan proprement dit : une liste des étapes, avec un vague calendrier. Le calendrier est certainement la partie la plus fragile du plan ; l'Internet n'ayant pas de chef, une transition va dépendre des efforts d'un grand nombre d'acteurs non coordonnés, et prédire leurs délais de réaction est à peu près impossible. (Voir le RFC 5211 pour un exemple.)

Un bon plan doit aussi comprendre un moyen de déterminer le succès (ou l'échec). Là aussi, ce n'est pas évident du tout. Certains protocoles sont surtout utilisés dans des réseaux locaux, donc difficiles à mesurer de l'extérieur (comment savoir combien de FAI proposent un résolveur DNS sécurisé par le RFC 7858 ?) Parfois, les critères quantitatifs ne sont pas évidents à établir. Prenons l'exemple d'IPv6 (lisez à ce sujet le rapport de l'ARCEP sur la transition IPv6, qui traite la question en détail). Comment mesure-t-on le succès d'IPv6 ? Le pourcentage de sites Web du Top N d'Alexa qui a une adresse IPv6 ? Le pourcentage d'utilisateurs finaux qui a IPv6 ? Le pourcentage d'octets IPv6 vs. IPv4 ? (Et où ? Chez Google ? Sur un point d'échange comme le France-IX ? Sur le réseau d'un transitaire ? Les valeurs seront très différentes.)

On l'a dit, les plans, même les meilleurs, survivent rarement à la rencontre avec le monde réel. Il faut donc un (ou plusieurs) « plan B », une solution de secours. Souvent, de facto, la solution de secours est la coexistence permanente de l'ancien et du nouveau protocole…

Et puis bien des acteurs de l'Internet ne suivent pas attentivement ce que fait l'IETF, voire ignorent complètement son existence, ce qui ajoute un problème supplémentaire : il faut communiquer le plan, et s'assurer qu'il atteint bien tous les acteurs pertinents (tâche souvent impossible). C'est le but d'opérations de communication comme le World IPv6 Launch Day.

Notre RFC rassemble ensuite (annexe A) quatre études de cas, illustrant des problèmes de transition différents. D'abord, le cas d'ECN. Ce mécanisme, normalisé dans le RFC 3168, permettait aux routeurs de signaler aux machines situées en aval de lui que la congestion menaçait. L'idée est que la machine aval, recevant ces notifications ECN, allait dire à la machine émettrice, située en amont du routeur, de ralentir, avant qu'une vraie congestion n'oblige à jeter des paquets. Les débuts d'ECN, vers 2000-2005, ont été catastrophiques. Les routeurs, voyant apparaitre des options qu'ils ne connaissaient pas, ont souvent planté. C'est un cas typique où une possibilité existait (les options d'IPv4 étaient normalisées depuis le début) mais n'était pas correctement implémentée en pratique. Toute transition qui se mettait à utiliser cette possibilité allait donc se passer mal. Pour protéger les routeurs, des pare-feux se sont mis à retirer les options ECN, ou bien à jeter les paquets ayant ces options, rendant ainsi très difficile tout déploiement ultérieur, même après correction de ces sérieuses failles dans les routeurs.

À la fin des années 2000, Linux et Windows ont commencé à accepter l'ECN par défaut (sans toutefois le réclamer), et la présence d'ECN, mesurée sur le Top Million d'Alexa, a commencé à grimper. De quasiment zéro en 2008, à 30 % en 2012 puis 65 % en 2014. Bref, ECN semble, après un très long purgatoire, sur la bonne voie (article « Enabling Internet-Wide Deployment of Explicit Congestion Notification »).

(Un autre cas, non cité dans le RFC, où le déploiement d'une possibilité ancienne mais jamais testé, a entrainé des conséquences fâcheuses, a été celui de BGP, avec la crise de l'attribut 99.)

L'exemple suivant du RFC est celui d'IDN. L'internationalisation est forcément un sujet chaud, vu les sensibilités existantes. Les IDN résolvent enfin un problème très ancien, l'impossibilité d'avoir des noms de domaine dans toutes les écritures du monde. (Voir la section 3 du RFC 6055, pour la longue et compliquée histoire des IDN.) Une fois que la norme IDN était disponible, il restait à effectuer la transition. Elle n'est pas encore terminée aujourd'hui. En effet, de nombreuses applications manipulent les noms de domaine et doivent potentiellement être mises à jour. Bien sûr, elles peuvent toujours utiliser la forme Punycode, celle-ci est justement conçue pour ne pas perturber les applications traditionnelles, mais ce n'est qu'un pis-aller (ஒலிம்பிக்விளையாட்டுகள்.சிங்கப்பூர் est quand même supérieur à xn--8kcga3ba7d1akxnes3jhcc3bziwddhe.xn--clchc0ea0b2g2a9gcd).

Pire, IDN a connu une transition dans la transition, lors du passage de la norme IDN 2003 (RFC 3490) vers IDN 2008 (RFC 5890). IDN 2008 était conçu pour découpler IDN d'une version particulière d'Unicode mais l'un des prix à payer était le cassage de la compatibilité : certains caractères comme le ß étaient traités différemment entre IDN 2003 et IDN 2008.

Le cas d'IDN est aussi l'occasion, pour le RFC, de rappeler que tout le monde n'a pas forcément les mêmes intérêts dans la transition. IDN implique, outre l'IETF, les auteurs de logiciels (par exemple ceux des navigateurs), les registres de noms de domaine, les domaineurs, et bien sûr les utilisateurs. Tous ne sont pas forcément d'accord et le blocage d'une seule catégorie peut sérieusement retarder une transition (diplomatiquement, le RFC ne rappele pas que l'ICANN a longtemps retardé l'introduction d'IDN dans la racine du DNS, pour des pseudo-raisons de sécurité, et que leur introduction n'a pu se faire qu'en la contournant.)

Lorsqu'on parle transition douloureuse, on pense évidemment tout de suite à IPv6. Ce successeur d'IPv4 a été normalisé en 1995 (par le RFC 1833), il y a vingt-deux ans ! Et pourtant, il n'est toujours pas massivement déployé. (Il existe de nombreuses métriques mais toutes donnent le même résultat : IPv6 reste minoritaire, bien que ces dernières années aient vu des progrès certains. Notez que les réseaux visibles publiquement ne sont qu'une partie de l'Internet : plusieurs réseaux internes, par exemple de gestion d'un opérateur, sont déjà purement IPv6.) Il y avait pourtant un plan de transition détaillé (RFC 1933), fondé sur une coexistence temporaire où toutes les machines auraient IPv4 et IPv6, avant qu'on ne démantèle progressivement IPv4. Mais il a clairement échoué, et ce problème est maintenant un sujet de plaisanterie (« l'année prochaine sera celle du déploiement massif d'IPv6 », répété chaque année).

Là encore, un des problèmes était que tout le monde n'a pas les mêmes intérêts. Si les fabricants de routeurs et les développeurs d'applications bénéficient d'IPv6, c'est beaucoup moins évident pour les gérants de sites Web, ce qui explique que plusieurs sites à forte visibilité, comme Twitter, ou bien gérés par des gens pourtant assez branchés sur la technique, comme GitHub, n'aient toujours pas IPv6 (c'est également le cas de la totalité des sites Web du gouvernement français, qui pourtant promeut officiellement l'usage d'IPv6).

L'effet réseau a également joué à fond contre IPv6 : les pionniers n'ont aucune récompense, puisqu'ils seront tout seuls alors que, par définition, le réseau se fait à plusieurs. Bien sûr, IPv6 marche mieux que l'incroyable et branlante pile de techniques nécessaire pour continuer à utiliser IPv4 malgré la pénurie (STUN, TURN, port forwarding, ICE, etc). Mais tout le monde ne ressent pas ce problème de la même façon : le FAI, par exemple, ne supporte pas les coûts liés à la non-transition, alors qu'il paierait ceux de la transition. Ce problème de (non-)correspondance entre les coûts et les bénéfices est celui qui ralentit le plus les nécessaires transitions. Et puis, pour les usages les plus simples, les plus Minitel 2.0, IPv4 et ses prothèses marchent « suffisamment ».

La lenteur de la transition vers IPv6 illustre aussi la difficulté de nombreux acteurs à planifier à l'avance. C'est seulement lorsque l'IANA, puis les RIR sont l'un après l'autre tombés à court d'adresses IPv4 que certains acteurs ont commencé à agir, alors que le problème était prévu depuis longtemps.

Il n'y a évidemment pas une cause unique à la lenteur anormale de la transition vers IPv6. Le RFC cite également le problème de la formation : aujourd'hui encore, dans un pays comme la France, une formation de technicien ou d'ingénieur réseaux peut encore faire l'impasse sur IPv6.

Le bilan du déploiement d'IPv6 est donc peu satisfaisant. Si certains réseaux (réseaux internes d'entreprises, réseaux de gestion) sont aujourd'hui entièrement IPv6, le déploiement reste loin derrière les espérances. Ce mauvais résultat nécessite de penser, pour les futurs déploiements, à aligner les coûts et les bénéfices, et à essayer de fournir des bénéfices incrémentaux (récompenses pour les premiers adoptants, comme l'a fait avec succès Bitcoin).

Dernier cas de transition étudié par notre RFC, HTTP/2 (RFC 7540). Nouvelle version du super-populaire protocole HTTP, elle vise à améliorer les performances, en multiplexant davantage, et en comprimant les en-têtes (RFC 7541). HTTP/2 a vécu la discussion classique lors de la conception d'une nouvelle version, est-ce qu'on résout uniquement les problèmes les plus sérieux de l'ancienne version ou bien est-ce qu'on en profite pour régler tous les problèmes qu'on avait laissés ? HTTP/2 est très différent de HTTP/1. Ses règles plus strictes sur l'utilisation de TLS (algorithmes abandonnés, refus de la renégociation, par exemple) ont d'ailleurs entrainé quelques problèmes de déploiement.

Il y a même eu la tentation de supprimer certaines fonctions de HTTP/1 considérées comme inutiles ou néfastes (les réponses de la série 1xx, et les communications en clair, entre autres). Après un débat très chaud et très houleux, HTTP/2 n'impose finalement pas HTTPS : les communications peuvent se faire en clair même si, en pratique, on voit très peu de HTTP/2 sans TLS.

Et comment négocier l'ancien protocole HTTP/1 ou le nouveau HTTP/2 ? Ce problème du client (le même qu'avec les versions d'IP : est-ce que je dois tenter IPv6 ou bien est-ce que j'essaie IPv4 d'abord ?) peut être résolu par le mécanisme Upgrade de HTTP (celui utilisé par le RFC 6455), mais il nécessite un aller-retour supplémentaire avec le serveur. Pour éviter cela, comme presque toutes les connexions HTTP/2 utilisent TLS, le mécanisme privilégié est l'ALPN du RFC 7301.

Ce mécanisme marche tellement bien que, malgré le conseil du RFC 5218, HTTP/2 prévoit peu de capacités d'extensions du protocole, considérant qu'il vaudra mieux, si on veut l'étendre un jour, passer à un nouvelle version, négociée grâce à ALPN (cf. RFC 6709.)

En conclusion, on peut dire que la conception d'un nouveau protocole (ou d'une nouvelle version d'un protocole existant) pour que la transition se passe vite et bien, reste un art plutôt qu'une science. Mais on a désormais davantage d'expérience, espérons qu'elle sera utilisée dans le futur.


Téléchargez le RFC 8170


L'article seul

RFC 8164: Opportunistic Security for HTTP/2

Date de publication du RFC : Mai 2017
Auteur(s) du RFC : M. Nottingham, M. Thomson (Mozilla)
Expérimental
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 16 mai 2017


Pendant la mise au point de la version 2 du protocole HTTP (finalement normalisée dans le RFC 7540), un débat très vigoureux avait porté sur la possibilité de chiffrer les échanges avec TLS même si le plan de l'URL demandé était http: (et pas https:). Certains demandaient le chiffrement systématique (que l'URL commence par http: ou https:), d'autres voulaient garder la même sémantique que HTTP version 1 (TLS pour https:, en clair pour http:). Cette dernière décision l'avait emporté à l'époque, en gardant la possibilité de permettre une extension à HTTP/2. Ce nouveau RFC décrit justement une telle extension (expérimentale, pour l'instant) : en HTTP/2, on peut désormais utiliser TLS (et donc HTTPS) même pour un URL de plan http:.

Le problème à résoudre est celui de la surveillance de masse, à laquelle procèdent un certain nombre d'acteurs (les États, bien sûr, mais pas uniquement, certains FAI, certains réseaux locaux, surveillent le trafic de leurs utilisateurs). Cette surveillance de masse est considérée, à juste titre, par l'IETF comme un problème de sécurité, et contre lequel il faut donc trouver des solutions ou au moins des mitigations (RFC 7258). Chiffrer massivement le trafic Web est évidemment indispensable pour diminuer l'efficacité de la surveillance.

Mais le modèle de HTTP version 1 rend cela difficile. En HTTP/1, on accède à un URL de plan http: avec du trafic en clair, passer à TLS nécessite de changer les URL, donc les pages Web qui les contiennent, les signets des utilisateurs, etc. Des logiciels comme HTTPS Everywhere aident à cela mais ne sont pas une solution parfaite (rappelez-vous par exemple qu'une bonne partie du trafic HTTP n'est pas due aux navigateurs Web).

Il serait tentant de résoudre le problème en disant « le client HTTP qui tente d'accéder à un URL de plan http: n'a qu'à essayer en même temps HTTPS. Si ça marche, tant mieux. Si ça rate, au moins on aura essayé. » C'est ce qu'on nomme parfois le « chiffrement opportuniste » (RFC 7435). Mais cela pose trois problèmes :

  • Si on tente HTTPS d'abord, sur le port 443, et qu'un pare-feu sur le trajet absorbe ces paquets, on devra attendre l'expiration du délai de garde avant d'essayer avec succès sur le port 80. Ce problème est réel, mais soluble par l'algorithme des globes oculaires heureux, décrit dans le RFC 6555.
  • Que faire si ça réussit en HTTPS mais que le certificat du serveur ne peut pas être validé ? La difficulté et/ou le coût d'un certificat sont après tout les principales raisons pour lesquelles HTTPS n'est pas davantage déployé. (Je ne publie pas des URL https: pour mon blog car beaucoup de gens n'ont pas mon AC dans leur magasin d'autorités.) On note qu'aujourd'hui les alertes de sécurité des navigateurs Web sont souvent absurdes : si on se connecte en HTTPS mais avec un certificat expiré (qui a donc été parfaitement valable), on a des alertes plus effrayantes que si on se connecte en clair !
  • Enfin, et c'est le plus gros problème, rien ne garantit qu'on obtiendra le même contenu en HTTP et en HTTPS : la plupart des serveurs HTTP permettent de configurer deux virtual host différents sur les deux ports 80 et 443. Pas question donc de jouer à ça sans une autorisation explicite du serveur.

Bref, pour le HTTP traditionnel, il semble qu'il n'y ait pas de solution.

Celle proposée par notre RFC est d'utiliser le mécanisme des services alternatifs du RFC 7838. Le serveur va indiquer (typiquement par un en-tête Alt-Svc:) qu'il est accessible par un autre mécanisme (par exemple HTTPS). Cela a également l'avantage d'éviter les problèmes de contenu mixte qui surviendraient si on avait mis la page en HTTPS mais pas tous ses contenus. Par contre, l'indication de service alternatif n'étant pas forcément bien protégée, ce mécanisme « opportuniste » reste vulnérable aux attaques actives. En revanche, ce mécanisme devrait être suffisamment simple pour être largement déployé assez vite.

Donc, maintenant, les détails concrets (section 2 du RFC). Le serveur qui accepte de servir des URL http: avec TLS annonce le service alternatif. Notez que les clients HTTP/1 n'y arriveront pas, car ils ne peuvent pas indiquer l'URL complet (avec son plan) dans la requête à un serveur d'origine (section 5.3.1 du RFC 7230). Cette spécification est donc limitée à HTTP/2 (RFC 7540). Si le client le veut bien, il va alors effectuer des requêtes chiffrées vers la nouvelle destination. S'il ne veut pas, ou si pour une raison ou pour une autre, la session TLS ne peut pas être établie, on se rabat sur du texte en clair (chose qu'on ne ferai jamais avec un URL https:).

Si le client est vraiment soucieux de son intimité et ne veut même pas que la première requête soit en clair, il peut utiliser une commande HTTP qui ne révèle pas grand'chose, comme OPTIONS (section 4.3.7 du RFC 7231).

Le certificat client ne servirait à rien dans ce chiffrement opportuniste et donc, même si on en a un, on ne doit pas l'envoyer. Par contre, le serveur doit avoir un certificat, et valide (RFC 2818) pour le service d'origine (si le service d'origine était en foo.example et que le service alternatif est en bar.example, le certificat doit indiquer au moins foo.example). Ce service ne permet donc pas de se chiffrer sans authentification, par exemple avec un certificat expiré, ou avec une AC inconnue du client, et ne résoud donc pas un des plus gros problèmes de HTTPS. Mais c'est une exigence de la section 2.1 du RFC 7838, qui exige que le renvoi à un service alternatif soit « raisonnablement » sécurisé. (Notez que cette vérification est délicate, comme l'a montré CVE-2015-0799.)

En outre, le client doit avoir fait une requête sécurisée pour le nom bien connu (RFC 5785, pour la notion de nom bien connu) /.well-known/http-opportunistic. La réponse à cette requête doit être positive, et doit être en JSON, et contenir un tableau de chaînes de caractères dont l'une doit être le nom d'origine (pour être sûr que ce serveur autorise le service alternatif, car le certificat du serveur effectivement utilisé prouve une autorisation du serveur alternatif, et la signature d'une AC, ce qu'on peut trouver insuffisant). Ce nouveau nom bien connu figure désormais dans le registre IANA.

La section 4 de notre RFC rappelle quelques trucs de sécurité :

  • L'en-tête Alt-Svc: étant envoyé sur une liaison non sécurisée, il ne faut pas s'y fier aveuglément (d'où les vérifications faites ci-dessus).
  • Certaines applications tournant sur le serveur peuvent utiliser des drôles de moyens pour déterminer si une connexion était sécurisée ou pas (par exemple en regardant le port destination). Elles pourraient faire un faux diagnostic sur les connexions utilisant le service alternatif.
  • Il est trivial pour un attaquant actif (un « Homme du Milieu ») de retirer cet en-tête, et donc de faire croire au client que le serveur n'a pas de services alternatifs. Bref, cette technique ne protège que contre les attaques passives. Ce point a été un des plus discutés à l'IETF (débat classique, vaut-il mieux uniquement la meilleure sécurité, ou bien accepter une sécurité « au mieux », surtout quand l'alternative est pas de sécurité du tout).
  • Le client ne doit pas utiliser des indicateurs qui donneraient à l'utilisateur l'impression que c'est aussi sécurisé qu'avec du « vrai » HTTPS. Donc, pas de joli cadenas fermé et vert. (C'est une réponse au problème ci-dessus.)

Apparemment, Firefox est le seul client HTTP à mettre en œuvre ce nouveau service (mais avec une syntaxe différente pour le JSON, pas encore celle du RFC). Notez que le serveur ne nécessite pas de code particulier, juste une configuration (envoyer l'en-tête Alt-Svc:, avoir le /.well-known/http-opportunistic…) Les serveurs de Cloudflare permettent ce choix.


Téléchargez le RFC 8164


L'article seul

Fin du groupe de travail IETF dbound

Première rédaction de cet article le 15 mai 2017


L'IETF a annoncé le 24 avril 2017 la dissolution du groupe de travail DBOUND. Je n'écrirai donc pas d'article sur les RFC de ce groupe, il n'y en a eu aucun. Pourquoi cet échec ?

D'abord, voyons quel était le problème que voulait résoudre ce groupe. DBOUND signifie Domain Boundaries et il s'agissait en gros d'indiquer publiquement quelles étaient les frontières organisationnelles dans les noms de domaine. Minute, vont se dire certains lecteurs, c'est facile ! Dans www.foobar.example, la frontière est forcément entre foobar et le truc appelé à tort « extension » .example ? Mais c'est complètement faux, les coupures (passage d'une organisation à une autre) peuvent être à plein d'endroits différents et rien dans le nom de domaine ne l'indique. (Cf. mon article sur « La terminologie des parties d'un nom de domaine ».)

Et, au passage, pourquoi est-ce que c'est important de savoir que signal.eu.org et eahm.eu.org ne dépendent pas de la même organisation ? Parce que plusieurs services en dépendent. (Une liste partielle de raisons figure dans mon article « Trouver le domaine responsable ».) Par exemple, on pourrait vouloir, dans la barre d'adresses du navigtateur Web, colorier différemment le domaine enregistré le plus haut dans l'arbre, pour éviter certains trucs utilisés par le hameçonnage.

Aujourd'hui, comme il y a un vrai besoin, la plupart des utilisateurs se servent de la « Public Suffix List » de Mozilla. Cela marche « suffisamment » mais son principal inconvénient est qu'elle n'est pas administrée par les gérants de noms de domaine, et qu'elle n'est donc jamais à jour.

C'est là dessus que devait travailler le groupe DBOUND. Il devait « développer une solution unique pour déterminer les frontières organisationnelles ». Le travail a commencé sur une liste de diffusion en janvier 2014, et le groupe lui-même a été créé en avril 2015. Plusieurs documents ont été proposés mais aucun n'a réuni même un début de commencement de consensus. (Même pas le document de description du problème, draft-sullivan-dbound-problem-statement.)

Suivant un principe général de l'IETF, qu'un groupe de travail est fait pour travailler et qu'il ne faut pas maintenir en vie artificiellement des groupes qui ne produiront manifestement rien d'utile, le groupe a donc été dissous.

Pourquoi cet échec ? Il n'y a sans doute pas une raison unique. Parmi les explications :

  • Le problème est bien plus compliqué qu'il n'en a l'air (comme beaucoup de problèmes qu'on aborde avec des yakafokon), par exemple parce qu'il n'est pas évident qu'il faille les mêmes frontières pour toutes les applications,
  • Il y a un désaccord de fond entre ceux qui disent que l'indication des frontières doit être faite par le domaine parent (au-dessus de la frontière), car c'est lui qui fixe les règles d'enregistrement, et ceux qui disent qu'elle doit être faite par le domaine fils (car c'est lui qui sait son propre statut),
  • Et, tout simplement, intérêt insuffisant pour un problème dont la partie la plus urgente (les cookies) est déjà partiellement résolu. L'IETF étant une organisation de volontaires, s'il n'y a pas de volontaire, rien ne se passe.

L'article seul

Cours DNS au CNAM

Première rédaction de cet article le 14 mai 2017


Le 11 mai 2017, c'était la première édition de mon cours DNS de trois heures au CNAM. Pour l'anecdote, c'était dans le bâtiment où il y avait eu la première connexion UUCP/Usenet, et le premier serveur HTTP public, en France.

Voici les supports de l'exposé :

Désolé, pas de vidéo, ça n'est pas filmé. Mais on recommencera.

Merci à Sami Taktak pour l'idée et l'organisation, et aux élèves pour avoir posé plein de questions pas toujours faciles.


L'article seul

RFC 8165: Design considerations for Metadata Insertion

Date de publication du RFC : Mai 2017
Auteur(s) du RFC : T. Hardie
Pour information
Première rédaction de cet article le 14 mai 2017


Ce court RFC déconseille l'insertion de métadonnées dans les paquets IP, si cette insertion est faite en route, par des intermédiaires. Pourquoi ? (Essentiellement pour des raisons de vie privée.)

Le problème de la surveillance de masse que pratiquent la plupart des États (en tout cas ceux qui en ont les moyens financiers) est maintenant bien documenté (par exemple dans les RFC 7258 et RFC 7624). Une solution fréquente pour limiter cette surveillance, la rendre plus coûteuse et moins efficace est de chiffrer ses communications. Dans l'éternelle lutte de l'épée et de la cuirasse, les surveillants réagissent au chiffrement en utilisant davantage les métadonnées, en général non protégées par le chiffrement. Qui met des métadonnées dans les paquets, affaiblissant ainsi l'effet du chiffrement ?

Certaines métadonnées sont absolument indispensables au fonctionnement de l'Internet. Par exemple, l'adresse IP de destination dans un paquet doit être en clair car tous les routeurs situés sur le trajet doivent la voir, pour prendre leurs décisions. Certaines métadonnées sont inutiles au fonctionement de l'Internet, mais difficiles à dissimuler, la taille des paquets, par exemple. (C'est également un exemple d'une métadonnée implicite : contrairement à l'adresse IP, elle n'apparait pas explicitement dans le paquet.) Normalement, pour gêner la surveillance, il faut envoyer le moins de métadonnées possible.

L'Internet est souvent décrit comme reposant sur une liaison de bout en bout, où seules les deux machines situées aux extrémités de la communication ont accès à tout le contenu de la communication. Mais, en pratique, il existe souvent des équipements intermédiaires qui ont accès à des informations poour faire leur travail. Si ces middleboxes ont la mauvaise idée de mettre ces informations dans les métadonnées d'un paquet, elles affaiblissent la confidentialité des échanges. Imaginons par exemple (ce n'est pas forcément fait aujourd'hui : le RFC met en garde contre une mauvaise idée, pas toujours contre des pratiques existantes, voir à ce sujet l'examen par la direction Sécurité), imaginons par exemple un VPN qui déciderait d'indiquer l'adresse IP originale dans la communication… Notre RFC mentionne deux exemples qui sont décrits dans des RFC : le RFC 7239 qui décrit l'en-tête HTTP Forwarded: qu'un relais HTTP peut mettre pour indiquer l'adresse IP d'origine du client, et bien sûr le RFC 7871, où un résolveur DNS transmet aux serveurs faisant autorité l'adresse IP du client original.

La section 4 du RFC est la recommandation concrète : les métadonnées ne doivent pas être mises par les intermédiaires. Si ces informations peuvent être utiles aux destinataires, c'est uniquement au client d'origine de les mettre. Autrement, on trahit l'intimité du client.

Le RFC 7871, par exemple, aurait dû spécifier un mécanisme où l'adresse IP est mise par le client DNS de départ, celui qui tourne sur la machine de l'utilisateur. Cela permettrait un meilleur contrôle de sa vie privée par l'utilisateur.

Et si cette machine ne connait pas sa propre adresse IP publique, par exemple parce qu'elle est coincée derrière un NAT? Dans ce cas, notre RFC 8165 dit qu'il faut utiliser une t