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.


Capitole du Libre 2018 (et mon exposé sur « Internet et les droits humains »)

Première rédaction de cet article le 19 novembre 2018


Les 17 et 18 novembre 2018, c'était Capitole du Libre, grand rassemblement annuel de centaines de libristes à Toulouse, dans les locaux de l'ENSEEIHT. À cette occasion, j'y ai fait un exposé sur le thème « Internet et les droits humains, il y a vraiment un rapport ? ».

Si vous voulez lire les supports de cet exposé, il y a une version PDF pour lire sur écran, et une version PDF pour imprimer. Le tout est évidemment sous une licence libre (celle de ce blog, la GFDL) et vous pouvez diffuser et/ou modifier le tout. Si vous voulez le modifier, le source est en LaTeX/Beamer et cette version est disponible également.

L'exposé a été filmé et je suppose que les vidéos seront en ligne quelque part bientôt (sur PeerTube, j'espère), mais pas immédiatement.

Il y avait plein d'autres exposés à Capitole du Libre, dont beaucoup étaient passionnants. Mon préféré était celui de Vincent Privat, sur « André Cros, import Wikimedia d'un fonds d'archive exceptionnel de Toulouse ». André Cros était photographe à Sud Ouest pendant de nombreuses années et a transmis ses photos, avec les droits, à la Mairie de Toulouse, qui est partenaire de Wikimedia. Cela permettait juridiquement de tout mettre sur Wikimedia Commons, avec une licence libre. Les photos étaient déjà numérisées. Il a fallu développer un petit script pour envoyer les photos sur Wikimedia Commons et, surtout, étiqueter et classer chaque photo. On y trouve, entre autre, le premier vol du Concorde, un passage de Sylvie Vartan à Toulouse, la terrible opération de sécurité routière « Une ville rayée de la carte » à Mazamet, les inondations de la Garonne, etc.

Parmi les autres exposés:

Capitole du Libre est remarquablement organisé, avec un gros investissement de la part des bénévoles, donc merci mille fois, c'était extra.


L'article seul

Un service d'hébergement de zones DNS accessible au·à la non-geek ?

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


Dans cet article, je ne vais pas décrire un logiciel ou service existant, ni résumer un RFC. Je voudrais au contraire pointer du doigt un manque dans l'offre existante, à la fois l'offre de services non-commerciaux accessibles au·à la non-geek, et l'offre de logiciel libre. Pourquoi n'y a-t-il pas de système simple pour héberger une zone DNS ? Et, si un·e militant·e de la liberté sur Internet passe par là, ne serait-ce pas une bonne idée d'en créer un ? J'espère que cet article donnera des idées.

Un peu de contexte d'abord. Le DNS est à la base de quasiment toutes les transactions sur l'Internet. Si on se soucie de la liberté et de l'indépendance de l'utilisateur qui veut publier quelque chose, et pas être un·e simple consommat·eur·rice, avoir un nom de domaine à soi est crucial. Acheter/louer un tel nom est simple et peu coûteux (encore qu'il semble qu'il n'existe que très peu, voire pas du tout, de bureaux d'enregistrement non-commerciaux). L'héberger est une autre histoire. L'hébergeur DNS (qui n'est pas forcément le bureau d'enregistrement) joue un rôle crucial. S'il est en panne, plus rien ne marche. S'il enregistre les données, la vie privée est menacée (sur ce risque, voir le RFC 7626). Il faut donc le choisir avec soin.

Or, beaucoup de titulaires de nom de domaine choisissent une entreprise (par exemple Cloudflare) un peu au hasard, sans avoir étudié ses pratiques. Il serait évidemment préférable de faire héberger ses serveurs de noms dans un endroit plus sûr.

Un solution geek existe pour cela, l'auto-hébergement des serveurs de noms. Ce n'est pas très compliqué (un logiciel libre comme nsd fait cela très bien) et cela ne demande que peu de ressources (un serveur DNS ne représente que peu de trafic, un Raspberry Pi connecté en ADSL suffit largement pour la plupart des zones, d'autant plus que le vrai serveur primaire n'a pas forcément besoin d'être accessible de tout l'Internet, il peut être caché, visible uniquement des secondaires). Si on veut améliorer les choses, on peut demander à des cop·ain·ine·s d'héberger un serveur secondaire de sa zone. C'est ainsi qu'est hébergée la zone DNS de ce blog, bortzmeyer.org.

Mais, évidemment, c'est uniquement une solution pour geek. On ne peut pas demander à toute personne qui publie sur Internet de configurer nsd ou Knot même si, je le répète, c'est relativement simple. Et il faudrait en plus éditer son fichier de zone DNS avec un éditeur de texte, ce qui n'enchante pas le non-geek. Or, la liberté d'expression ne doit pas être réservée aux informaticiens !

Il faudrait donc une solution pour ces utilisateurices. Il y a deux moyens de fournir cette solution, via un service ou via un logiciel. Concentrons-nous d'abord sur les services car, même s'il existait un logiciel tout fait, libre et tout, il resterait à gérer l'administration quotidienne du serveur, et un serveur DNS fait parfois face à des problèmes (voir plus loin dans cet article le problème des dDoS). Des services pour M. ou Mme Toutlemonde, libres, éthiques, non-commerciaux et le reste, existent, ce sont par exemple les CHATONS, dont le plus connu est Framasoft. Mais, à l'heure actuelle, à ma connaissance, aucun CHATON ne propose d'hébergement DNS.

Esquissons brièvement le cahier des charges d'un tel service :

  • Utilisable indépendamment d'autres services (comme l'hébergement Web),
  • Permet aux internautes de s'inscrire, puis de donner une liste de domaines qu'ils souhaitent voir gérés par le service,
  • Permet de gérer le contenu de la zone DNS, en indiquant différents enregistrements, de tous les types possibles, avec leurs valeurs. L'interface proposée doit être simple d'usage, sans limiter les possibilités. Tous les hébergeurs DNS ont aujourd'hui une telle interface, typiquement via le Web et parfois via une API en prime.
  • Raisonnablement sécurisé, car il ne faut pas qu'un utilisateur puisse modifier le domaine d'un autre !
  • Ne réinventant pas la roue, donc reposant sur un logiciel de serveur DNS faisant autorité qui soit stable et sérieux comme nsd ou Knot.
  • D'un point de vue de l'utilisateur, il serait bon d'avoir un mode simple, où on donne juste l'adresse IP du serveur Web, avec des modes plus avancés pour les utilisateurs qui ne se contentent pas d'un site Web.
  • D'un point de vue politique, certains peuvent aussi souhaiter pouvoir maîtriser où sont situés les serveurs faisant autorité (on peut vouloir éviter tel ou tel pays, et migrer facilement),
  • D'un point de vue plus technique, il faudrait DNSSEC (passage des enregistrements DS mais, surtout, gestion des clés et signatures par l'hébergeur), les mises à jour dynamiques du RFC 2136 (mais une bonne API peut les remplacer).

Un tel service, à ma connaissance, n'existe pas aujourd'hui dans le monde associatif. Il faut dire qu'il existe quelques risques :

  • Le service pourrait être confronté à des menaces juridiques (la liberté d'expression ne plait pas à tout le monde), venant par exemple d'ayant-trop-de-droits si on héberge une zone DNS comme celle de Sci-Hub, ou bien venant d'adversaires politiques (cf. le cas d'IndyMedia),
  • Tout le monde ne croit pas à l'état de droit et donc les menaces pourraient être suivies, voire précédées d'attaques par déni de service. L'exemple de Dyn ou bien celui du domaine national turc invitent à être prudent. Un hébergement DNS sympa fait par deux-trois potes pourrait ne pas résister à une dDoS un peu sérieuse.
  • L'attaque par déni de service n'est pas la seule menace. Il existe aussi le risque de détournement du nom, comme c'était arrivé à Wikileaks et à beaucoup d'autres. Bien sûr, si le détournement avait exploité une faute de l'utilisateur, le logiciel n'y peut pas grand'chose. Mais certains détournements étaient dûs à une faille de sécurité du logiciel.

On peut citer quelques services possibles qui se rapprochent de ce but :

  • Le plus connu est Xname, dont la documentation est très complète, mais il est apparement abandonné (aucune nouvelle depuis 2013). Je teste et Error: bad zone name example.com. Donc, cela ne semble pas marcher. Le service semble vraiment mort, d'autant plus que leur propre zone est pleine d'erreurs.
  • J'ai essayé de tester NetLibre. Il ne fournit pas d'hébergement sec, il faut utiliser NetLibre pour acquérir son nom (pour lequel on n'a donc pas un choix complet, on est limité aux domaines de NetLibre). C'est donc plutôt l'équivalent de eu.org, avec hébergement en plus. De toute façon, le service semble peu maintenu : Oh ! Une erreur est survenue. scp failed: unable to fork ssh master: Cannot allocate memory at /home/ppittoli/dnsmanager/bin/../lib/copycat.pm line 35 (et la seule adresse de contact est sur IRC).
  • Le service Free DNS est franchement trop compliqué pour moi. Je n'ai tout simplement pas compris ce qu'on pouvait faire avec, et comment s'en servir (mon domaine est marqué comme BROKEN et la FAQ est incompréhensible, et fausse quand elle prétend The central authority for all domains on the Internet is ICANN).
  • On m'a cité nohost.me, mais, si j'ai bien compris, c'est utilisable uniquement depuis YunoHost.

Cela, c'était pour un service en ligne. Et un logiciel ? Je vois deux cas d'usage :

  • Pour ce·ux·elles qui ne font confiance à personne, et/ou ne veulent dépendre de personne. Le logiciel n'a alors pas besoin d'avoir la notion de comptes ou d'utilisateur, puisqu'il n'existe qu'un·e utilisat·eur·rice.
  • Pour ce·ux·elles qui veulent faire essaimer le service. (Dans ce cas, le logiciel pourrait être utilisé par l'éventuel service public, puis par ses spin-offs.)

Ce doit être bien sûr un logiciel libre, qui doit pouvoir être installé relativement facilement, et fournir le service décrit ci-dessus. Pour le premier cas d'usage, le cas individuel, il serait peut-être encore mieux qu'il soit intégré à une plate-forme comme Cozy. Là encore, à ma connaissance, un tel logiciel n'existe pas (il existe plusieurs trucs plus ou moins bricolés, pas évidents à installer sur sa machine, et encore moins à maintenir).

Les logiciels existants qui pourraient servir d'inspiration :

  • DNS zones manager, le logiciel derrière NetLib.re. Je n'ai pas réussi à l'installer (perlbrew install perl-5.18.0 donne « ERROR: Failed to download http://www.cpan.org/authors/id/R/RJ/RJBS/perl-5.18.0.tar.bz2 », il faut manifestement des compétences Perl plus pointues que les miennes.)
  • Il existe des logiciels bien plus génériques que des seuls gestionnaires de zones DNS, qui pourraient servir de point de départ. C'est le cas de ISPConfig ou Webmin, par exemple.

Mais notez bien que le logiciel ne résout qu'une partie du problème : il faut encore les serveurs. C'est pour cela que je pense que la solution « service hébergé » est plus adaptée aux non-geeks.

Notons enfin qu'un service limité, qui ne fournisse que le serveur secondaire, charge à l'utilisateur d'avoir son propre primaire, serait déjà très utile. Là encore, il me semble qu'il n'existe rien de tel. Peut-être :

  • Puck. L'interface est vraiment rude (elle n'indique même pas quelle est l'adresse IP à autoriser, il faut regarder son journal !) mais ça marche bien. (Pensez à mettre ensuite puck.nether.net dans l'ensemble des enregistrements NS.)
  • Au Danemark, il y a GratisDNS, depuis 1999 (documentation uniquement en danois.)
  • EntryDNS faisait un service gratuit, mais il est désormais arrêté et remplacé par un service payant (mais très bon marché).

Je serai ravi d'apprendre que j'ai mal cherché et qu'un tel service ou un tel logiciel existent. N'hésitez pas à me le faire savoir. Et, si je ne me suis pas trompé, si un·e courageu·x·se pouvait le réaliser…


L'article seul

RFC 8484: DNS Queries over HTTPS (DoH)

Date de publication du RFC : Octobre 2018
Auteur(s) du RFC : P. Hoffman (ICANN), P. McManus (Mozilla)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF doh
Première rédaction de cet article le 22 octobre 2018


Voici un nouveau moyen d'envoyer des requêtes DNS, DoH (DNS over HTTPS). Requêtes et réponses, au lieu de voyager directement sur UDP ou TCP sont encapsulées dans HTTP, plus exactement HTTPS. Le but ? Il s'agit essentiellement de contourner la censure, en fournissant un canal sécurisé avec un serveur supposé digne de confiance. Et le chiffrement sert également à préserver la vie privée du client. Toutes ces fonctions pourraient être assurées en mettant le DNS sur TLS (RFC 7858) mais DoH augmente les chances de succès puisque le trafic HTTPS est rarement bloqué par les pare-feux, alors que le port 853 utilisé par DNS-sur-TLS peut être inaccessible, vu le nombre de violations de la neutralité du réseau. DoH marque donc une nouvelle étape dans la transition vers un Internet « port 443 seulement ».

La section 1 du RFC détaille les buts de DoH. Deux buts principaux sont décrits :

  • Le premier, et le plus important, est celui indiqué au paragraphe précédent : échapper aux middleboxes qui bloquent le trafic DNS, ou bien le modifient. De telles violations de la neutralité du réseau sont fréquentes, imposées par les États ou les entreprises à des fins de censure, ou bien décidées par les FAI pour des raisons commerciales. Dans la lutte sans fin entre l'épée et la cuirasse, le DNS est souvent le maillon faible : infrastructure indispensable, il offre une cible tentante aux attaquants. Et il est utilisable pour la surveillance (cf. RFC 7626). DNS-sur-TLS, normalisé dans le RFC 7858, était une première tentative de protéger l'intégrité et la confidentialité du trafic DNS par la cryptographie. Mais il utilise un port dédié, le port 853, qui peut être bloqué par un intermédiaire peu soucieux de neutralité du réseau. Dans de nombreux réseaux, hélas, le seul protocole qui soit à peu près sûr de passer partout est HTTPS sur le port 443. D'où la tendance actuelle à tout mettre sur HTTP.
  • Il y a une deuxième motivation à DoH, moins importante, la possibilité de faire des requêtes DNS complètes (pas seulement des demandes d'adresses IP) depuis des applications JavaScript tournant dans le navigateur, et tout en respectant CORS.

L'annexe A de notre RFC raconte le cahier des charges du protocole DoH de manière plus détaillée :

  • Sémantique habituelle de HTTP (on ne change pas HTTP),
  • Possibilité d'exprimer la totalité des requêtes et réponses DNS actuelles, d'où le choix de l'encodage binaire du DNS et pas d'un nouvel encodage, par exemple en JSON, plus simple mais limité à un sous-ensemble du DNS,
  • Et aussi des « non-considérations » : DoH n'avait pas à traiter le cas des réponses synthétisées, comme DNS64, ni celui des réponses à la tête du client, ni le HTTP tout nu sans TLS.

Passons maintenant à la technique. DoH est un protocole très simple. Au hackathon de l'IETF en mars 2018 à Londres, les sept ou huit personnes travaillant sur DoH avaient très vite réussi à créer clients et serveurs, et à les faire interopérer (même moi, j'y étais arrivé). Vous pouvez lire le compte-rendu du hackathon, et la présentation quelques jours après au groupe de travail DoH.)

DoH peut s'utiliser de plusieurs façons : c'est une technique, pas une politique. Néanmoins, son principal usage sera entre un résolveur simple, situé sur la machine de l'utilisateur ou quelque part dans son réseau local, et un résolveur complet situé plus loin dans le réseau (section 1 du RFC). Ce résolveur simple peut être un démon comme stubby ou systemd, ou bien l'application elle-même (ce qui me semble personnellement une mauvaise idée, car cela empêche de partager configuration et cache). À l'heure actuelle, les serveurs faisant autorité ne parlent pas DoH et il n'est pas prévu qu'ils s'y mettent à brève échéance. Le schéma suivant montre l'utilisation typique de DoH : l'application (le client final) parle à un résolveur simple, en utilisant le protocole DNS (ce qui évite de mettre du DoH dans toutes les applications), le résolveur simple parlera en DoH avec un résolveur de confiance situé quelque part dans l'Internet, et ce résolveur de confiance utilisera le DNS pour parler aux serveurs faisant autorité (soit il sera lui-même un résolveur complet, soit il parlera à un résolveur classique proche de lui, et le serveur DoH sera alors un proxy comme décrit dans le RFC 5625) : doh.png

Les requêtes et réponses DNS (RFC 1034 et RFC 1035) ont leur encodage habituel (le DNS est un protocole binaire, donc je ne peux pas faire de copier/coller pour montrer cet encodage), la requête est mise dans le chemin dans l'URL ou dans le corps d'une requête HTTP (RFC 7540), la réponse se trouvera dans le corps de la réponse HTTP. Toute la sécurité (intégrité et confidentialité) est assurée par TLS (RFC 8446), via HTTPS (RFC 2818). Un principe essentiel de DoH est d'utiliser HTTP tel quel, avec ses avantages et ses inconvénients. Cela permet de récupérer des services HTTP comme la négociation de contenu, la mise en cache, l'authentification, les redirections, etc.

La requête HTTP elle-même se fait avec les méthodes GET ou POST (section 4 du RFC), les deux devant être acceptées (ce qui fut le sujet d'une assez longue discussion à l'IETF.) Quand la méthode utilisée est GET, la variable nommée dns est le contenu de la requête DNS, suivant l'encodage habituel du DNS, surencodée en Base64, plus exactement la variante base64url normalisée dans le RFC 4648. Et, avec GET, le corps de la requête est vide (RFC 7231, section 4.3.1). Quand on utilise POST, la requête DNS est dans le corps de la requête HTTP et n'a pas ce surencodage. Ainsi, la requête avec POST sera sans doute plus petite, mais par contre GET est certainement plus apprécié par les caches.

On l'a dit, DoH utilise le HTTP habituel. L'utilisation de HTTP/2, la version 2 de HTTP (celle du RFC 7540) est très recommandée, et clients et serveurs DoH peuvent utiliser la compression et le remplissage que fournit HTTP/2 (le remplissage étant très souhaitable pour la vie privée). HTTP/2 a également l'avantage de multiplexer plusieurs ruisseaux (streams) sur la même connexion HTTP, ce qui évite aux requêtes DoH rapides de devoir attendre le résultat d'une requête lente qui aurait été émise avant. (HTTP 1, lui, impose le respect de l'ordre des requêtes.) HTTP/2 n'est pas formellement imposé, car on ne peut pas forcément être sûr du comportement des bibliothèques utilisées, ni de celui des différents relais sur le trajet.

Requêtes et réponses ont actuellement le type MIME application/dns-message, mais d'autres types pourront apparaitre dans le futur (par exemple fondés sur JSON et non plus sur l'encodage binaire du DNS, qui n'est pas amusant à analyser en JavaScript). Le client DoH doit donc inclure un en-tête HTTP Accept: pour indiquer quels types MIME il accepte. En utilisant HTTP, DoH bénéfice également de la négociation de contenu HTTP (RFC 7231, section 3.4).

Petit détail DNS : le champ ID (identification de la requête) doit être mis à zéro. Avec UDP, il sert à faire correspondre une requête et sa réponse mais c'est inutile avec HTTP, et cela risquerait d'empêcher la mise en cache de deux réponses identiques mais avec des ID différents.

En suivant la présentation des requêtes HTTP du RFC 7540 (rappelez-vous que HTTP/2, contrairement à la première version de HTTP, a un encodage binaire), cela donnerait, par exemple :

:method = GET
:scheme = https
:authority = dns.example.net
:path = /?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB
accept = application/dns-message
    

(Si vous vous le demandez, AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB est une requête DNS pour l'adresse IPv4 de www.example.com.)

Et la réponse HTTP ? Aujourd'hui, elle est forcément de type MIME application/dns-message, mais d'autres types pourront apparaitre. En attendant, le corps de la réponse HTTP est une réponse DNS avec son encodage binaire habituel normalisé dans le RFC 1035, section 4.1 (tel qu'utilisé pour UDP ; notez que HTTP permettant d'indiquer la longueur du message, les deux octets de longueur utilisés par le DNS au-dessus de TCP ne sont pas nécessaires et sont donc absents).

Le serveur DoH doit mettre un code de retour HTTP (RFC 7231). 200 signifie que la requête HTTP a bien été traitée. Mais cela ne veut pas dire que la requête DNS, elle, ait connu un succès. Si elle a obtenu une erreur DNS NXDOMAIN (nom non trouvé) ou SERVFAIL (échec de la requête), le code de retour HTTP sera quand même 200, indiquant qu'il y a une réponse DNS, même négative. Le client DoH, en recevant ce 200, devra donc analyser le message DNS et y trouver le code de retour DNS (NOERROR, NXDOMAIN, REFUSED, etc). Le serveur DoH ne mettra un code d'erreur HTTP que s'il n'a pas du tout de réponse DNS à renvoyer. Il mettra 403 s'il refuse de servir ce client DoH, 429 si le client fait trop de requêtes (RFC 6585), 500 si le serveur DoH a une grosse bogue, 415 si le type MIME utilisé n'est pas connu du serveur, et bien sûr 404 si le serveur HTTP ne trouve rien à l'URL indiqué par exemple parce que le service a été retiré. Dans tous ces cas, il n'y a pas de réponse DNS incluse. La sémantique de ces codes de retour, et le comportement attendu du client, suit les règles habituelles de HTTP, sans spécificité DoH. (C'est un point important et général de DoH : c'est du DNS normal sur du HTTP normal). Par exemple, lorsque le code de retour commence par un 4, le client n'est pas censé réessayer la même requête sur le même serveur : elle donnera forcément le même résultat.

Voici un exemple de réponse DoH :

:status = 200
content-type = application/dns-message
content-length = 61
cache-control = max-age=3709
[Les 61 octets, ici représentés en hexadécimal pour la lisibilité]
   00 00 81 80 00 01 00 01  00 00 00 00 03 77 77 77
   07 65 78 61 6d 70 6c 65  03 63 6f 6d 00 00 1c 00
   01 c0 0c 00 1c 00 01 00  00 0e 7d 00 10 20 01 0d
   b8 ab cd 00 12 00 01 00  02 00 03 00 04
    

La réponse DNS signifie « l'adresse de www.example.com est 2001:db8:abcd:12:1:2:3:4 et le TTL est de 3709 secondes [notez comme il est repris dans le Cache-control: HTTP] ».

Comment un client DoH trouve-t-il le serveur ? La section 3 du RFC répond à cette question. En gros, c'est manuel. DoH ne fournit pas de mécanisme de sélection automatique. Concevoir un tel mécanisme et qu'il soit sécurisé est une question non triviale, et importante : changer le résolveur DNS utilisé par une machine revient quasiment à pirater complètement cette machine. Cela avait fait une très longue discussion au sein du groupe de travail DoH à l'IETF, entre ceux qui pensaient qu'un mécanisme automatique de découverte du gabarit faciliterait nettement la vie de l'utilisateur, et ceux qui estimaient qu'un tel mécanisme serait trop facile à subvertir. Donc, pour l'instant, le client DoH reçoit manuellement un gabarit d'URI (RFC 6570) qui indique le serveur DoH à utiliser. Par exemple, un client recevra le gabarit https://dns.example.net/{?dns}, et il fera alors des requêtes HTTPS à dns.example.net, en passant ?dns=[valeur de la requête].

Notez que l'URL dans le gabarit peut comporter un nom de domaine, qui devra lui-même être résolu via le DNS, créant ainsi un amusant problème d'œuf et de poule (cf. section 10 de notre RFC). Une solution possible est de ne mettre que des adresses IP dans l'URL, mais cela peut poser des problèmes pour l'authentification du serveur DoH, toutes les autorités de certification n'acceptant pas de mettre des adresses IP dans le certificat (cf. RFC 6125, section 1.7.2, et annexe B.2).

La section 5 du RFC détaille quelques points liés à l'intégration avec HTTP. D'abord, les caches. DNS et HTTP ont chacun son système. Et entre le client et le serveur aux extrémités, il peut y avoir plusieurs caches DNS et plusieurs caches HTTP, et ces derniers ne connaissent pas forcément DoH. Que se passe-t-il si on réinjecte dans le DNS des données venues d'un cache HTTP ? En général, les réponses aux requêtes POST ne sont pas mises en cache (elles le peuvent, en théorie) mais les requêtes GET le sont, et les implémenteurs de DoH doivent donc prêter attention à ces caches. La méthode recommandée est de mettre une durée de validité explicite dans la réponse HTTP (comme dans l'exemple plus haut avec Cache-control:), en suivant le RFC 7234, notamment sa section 4.2. La durée de validité doit être inférieure ou égale au plus petit TTL de la section principale de la réponse DNS. Par exemple, si un serveur DoH renvoie cette réponse DNS :

ns1.bortzmeyer.org.	27288 IN AAAA 2605:4500:2:245b::42
ns2.bortzmeyer.org.	26752 IN AAAA 2400:8902::f03c:91ff:fe69:60d3
ns4.bortzmeyer.org.	26569 IN AAAA 2001:4b98:dc0:41:216:3eff:fe27:3d3f
    

alors, la réponse HTTP aura un Cache-Control: max-age=26569, le plus petit des TTL.

Si la réponse DNS varie selon le client, le serveur DoH doit en tenir compte pour construire la réponse HTTP. Le but est d'éviter que cette réponse adaptée à un client spécifique soit réutilisée. Cela peut se faire avec Cache-Control: max-age=0 ou bien avec un en-tête Vary: (RFC 7231, section 7.1.4 et RFC 7234, section 4.1) qui va ajouter une condition supplémentaire à la réutilisation des données mises en cache.

S'il y a un en-tête Age: dans la réponse HTTP (qui indique depuis combien de temps cette information était dans un cache Web, RFC 7234, section 5.1), le client DoH doit en tenir compte pour calculer le vrai TTL. Si le TTL DNS dans la réponse est de 600 secondes, mais que Age: indiquait que cette réponse avait séjourné 250 secondes dans le cache Web, le client DoH doit considérer que cette réponse n'a plus que 350 secondes de validité. Évidemment, un client qui veut des données ultra-récentes peut toujours utiliser le Cache-control: no-cache dans sa requête HTTP, forçant un rafraichissement. (Il est à noter que le DNS n'a aucun mécanisme équivalent, et qu'un serveur DoH ne saura donc pas toujours rafraichir son cache DNS.)

La définition formelle du type MIME application/dns-message figure en section 6 de notre RFC, et ce type est désormais enregistré à l'IANA.

La section 8 du RFC est consacrée aux questions de vie privée. C'est à la fois un des principaux buts de DoH (empêcher l'écoute par un tiers) et un point qui a fait l'objet de certaines polémiques, puisque DoH peut être utilisé pour envoyer toutes les requêtes à un gros résolveur public auquel on ne fait pas forcément confiance. Le RFC 7626 traite séparément deux problèmes : l'écoute sur le réseau, et l'écoute effectuée par le serveur. Sur le réseau, DoH protège : tout est chiffré, via un protocole bien établi, TLS. Du fait que le serveur est authentifié, l'écoute par un homme du milieu est également empêchée. DNS sur TLS (RFC 7858) a exactement les mêmes propriétés, mais pour principal inconvénient d'utiliser un port dédié, le 853, trop facile à bloquer. Au contraire, le trafic DoH, passant au milieu d'autres échanges HTTP sur le port 443, est bien plus difficile à restreindre.

Mais et sur le serveur de destination ? Une requête DNS normale contient peu d'informations sur le client (sauf si on utilise la très dangereuse technique du RFC 7871). Au contraire, une requête HTTP est bien trop bavarde : cookies (RFC 6265), en-têtes User-Agent: et Accept-Language:, ordre des en-têtes sont trop révélateurs de l'identité du client. L'utilisation de HTTP présente donc des risques pour la vie privée du client, risques connus depuis longtemps dans le monde HTTP mais qui sont nouveaux pour le DNS. Il avait été envisagé, pendant la discussion à l'IETF, de définir un sous-ensemble de HTTP ne présentant pas ces problèmes, mais cela serait rentré en contradiction avec les buts de DoH (qui étaient notamment de permettre l'utilisation du code HTTP existant). Pour l'instant, c'est donc au client DoH de faire attention. Si la bibliothèque HTTP qu'il utilise le permet, il doit veiller à ne pas envoyer de cookies, à envoyer moins d'en-têtes, etc.

Notez que la question de savoir si les requêtes DoH doivent voyager sur la même connexion que le trafic HTTPS normal (ce que permet HTTP/2, avec son multiplexage) reste ouverte. D'un côté, cela peut aider à les dissimuler. De l'autre, les requêtes HTTP typiques contiennent des informations qui peuvent servir à reconnaitre le client, alors qu'une connexion servant uniquement à DoH serait moins reconnaissable, le DNS étant nettement moins sensible au fingerprinting.

Comme TLS ne dissimule pas la taille des messages, et qu'un observateur passif du trafic, et qui peut en plus envoyer des requêtes au serveur DNS, peut en déduire les réponses reçues, le RFC recommande aux clients DoH de remplir les requêtes DNS selon le RFC 7830.

Le choix de Mozilla d'utiliser DoH pour son navigateur Firefox (voir un compte-rendu de la première expérience) et le fait que, dans certaines configurations, le serveur DoH de Cloudflare était systématiquement utilisé a été très discuté (cf. cette discussion sur le forum des développeurs et cet article du Register). Mais cela n'a rien à voir avec DoH : c'est le choix d'utiliser un résolveur public géré par un GAFA qui est un problème, pas la technique utilisée pour accéder à ce résolveur public. DNS-sur-TLS aurait posé exactement le même problème. Si Mozilla a aggravé les choses avec leur discours corporate habituel (« nous avons travaillé très dur pour trouver une entreprise de confiance »), il faut rappeler que le problème de la surveillance et de la manipulation des requête et réponses DNS par les FAI est un problème réel (essayez de demander à votre FAI s'il s'engage à ne jamais le faire). On a vu plus haut que DoH ne prévoit pas de système de découverte du serveur. Il faut donc que cela soit configuré en dur (un travail supplémentaire pour les utilisateurs, s'il n'y a pas de résolveur par défaut). En tout cas, le point important est que DoH (ou DNS-sur-TLS) ne protège la vie privée que si le serveur DoH est honnête. C'est une limitation classique de TLS : « TLS permet de s'assurer qu'on communique bien avec Satan, et qu'un tiers ne peut pas écouter ». Mais DoH n'impose pas d'utiliser un serveur public, et impose encore moins qu'il s'agisse d'un serveur d'un GAFA.

La section 9 de notre RFC traite des autres problèmes de sécurité. D'abord, sur la relation entre DoH et DNSSEC. C'est simple, il n'y en a pas. DNSSEC protège les données, DoH protège le canal (une distinction que les promoteurs de DNSCurve n'ont jamais comprise). DNSSEC protège contre les modifications illégitimes des données, DoH (ou bien DNS-sur-TLS) protège contre l'écoute illégitime. Ils résolvent des problèmes différents, et sont donc tous les deux nécessaires.

Quant à la section 10 du RFC, elle expose diverses considérations pratiques liées à l'utilisation de DoH. Par exemple, si un serveur faisant autorité sert des réponses différentes selon l'adresse IP source du client (RFC 6950, section 4), utiliser un résolveur public, qu'on y accède via DoH ou par tout autre moyen, ne donnera pas le résultat attendu, puisque l'adresse IP vue par le serveur faisant autorité sera celle du résolveur public, probablement très distincte de celle du « vrai » client. Un exemple similaire figure dans le RFC : une technique comme DNS64 (RFC 6147) risque fort de ne pas marcher avec un résolveur DNS extérieur au réseau local.

Quelles sont les mises en œuvre de DoH ? Le protocole est assez récent donc votre système favori n'a pas forcément DoH, ou alors c'est seulement dans les toutes dernières versions. Mais DoH est très simple à mettre en œuvre (c'est juste la combinaison de trois protocoles bien maitrisés, et pour lesquels il existe de nombreuses bibliothèques, DNS, HTTP et TLS) et le déploiement ne devrait donc pas poser de problème.

Voyons maintenant ce qui existe, question logiciels et serveurs. On a vu que Cloudflare a un serveur public, le fameux 1.1.1.1 étant accessible en DoH (et également en DNS-sur-TLS). Je ne parlerai pas ici de la question de la confiance qu'on peut accorder à ce serveur (je vous laisse lire sa politique de vie privée et l'évaluer vous-même), qui avait été contestée lors de la polémique Mozilla citée plus haut. Cloudflare fournit également une bonne documentation sur DoH, avec une explication de l'encodage. Enfin, Cloudflare fournit un résolveur simple (comme stubby ou systemd cités plus haut) qui est un client DoH, cloudflared.

Un autre serveur DoH public, cette fois issu du monde du logiciel libre, est celui de l'équipe PowerDNS, https://doh.powerdns.org/ (cf. leur annonce). Il utilise leur logiciel dnsdist.

Vous trouverez une liste de serveurs DoH publics chez DefaultRoutes ou bien chez curl ou encore sur le portail dnsprivacy.org. Testons ces serveurs DoH publics avec le programme doh-nghttp.c, qui avait été écrit au hackathon IETF 101, on lui donne l'URL du serveur DoH, et le nom à résoudre, et il cherche l'adresse IPv4 correspondant à ce nom :

% ./doh-nghttp https://doh.powerdns.org/ www.bortzmeyer.org
The address is 204.62.14.153

% ./doh-nghttp  https://1.1.1.1/dns-query www.bortzmeyer.org 
The address is 204.62.14.153

% ./doh-nghttp  https://doh.defaultroutes.de/dns-query www.bortzmeyer.org 
The address is 204.62.14.153

% ./doh-nghttp   https://mozilla.cloudflare-dns.com/dns-query www.bortzmeyer.org
The address is 204.62.14.153
    

Parfait, tout a bien marché. Un autre serveur DoH a la particularité d'être un résolveur menteur (regardez son nom) :

% ./doh-nghttp    https://doh.cleanbrowsing.org/doh/family-filter/ www.bortzmeyer.org
The address is 204.62.14.153

% ./doh-nghttp    https://doh.cleanbrowsing.org/doh/family-filter/ pornhub.com       
The search had no results, and a return value of 8. Exiting.

% ./doh-nghttp https://doh.powerdns.org/   pornhub.com      
The address is 216.18.168.16
    

Bon, et si je veux faire mon propre serveur DoH, on a quelles solutions ? Voyons d'abord le doh-proxy de Facebook. On lui indique le résolveur qu'il va utiliser (il n'est pas inclus dans le code, il a besoin d'un résolveur complet, a priori sur la même machine ou le même réseau local) :


% doh-proxy --port=9443 --upstream-resolver=192.168.2.254 --certfile=server.crt --keyfile=server.key --uri=/
2018-09-27 10:04:21,997:     INFO: Serving on <Server sockets=[<socket.socket fd=6, family=AddressFamily.AF_INET6, type=2049, proto=6, laddr=('::1', 9443, 0, 0)>]>

Et posons-lui des questions avec le même client doh-nghttp :

% ./doh-nghttp  https://ip6-localhost:9443/ www.bortzmeyer.org 
The address is 204.62.14.153
   

C'est parfait, il a marché et affiche les visites :

   
2018-09-27 10:04:24,264:     INFO: [HTTPS] ::1 www.bortzmeyer.org. A IN 0 RD
2018-09-27 10:04:24,264:     INFO: [DNS] ::1 www.bortzmeyer.org. A IN 56952 RD
2018-09-27 10:04:24,639:     INFO: [DNS] ::1 www.bortzmeyer.org. A IN 56952 QR/RD/RA 1/0/0 -1/0/0 NOERROR 374ms
2018-09-27 10:04:24,640:     INFO: [HTTPS] ::1 www.bortzmeyer.org. A IN 0 QR/RD/RA 1/0/0 -1/0/0 NOERROR 375ms

Au même endroit, il y a aussi un client DoH :

% doh-client --domain 1.1.1.1 --uri /dns-query --qname www.bortzmeyer.org
2018-09-27 10:14:12,191:    DEBUG: Opening connection to 1.1.1.1
2018-09-27 10:14:12,210:    DEBUG: Query parameters: {'dns': 'AAABAAABAAAAAAAAA3d3dwpib3J0em1leWVyA29yZwAAHAAB'}
2018-09-27 10:14:12,211:    DEBUG: Stream ID: 1 / Total streams: 0
2018-09-27 10:14:12,219:    DEBUG: Response headers: [(':status', '200'), ('date', 'Thu, 27 Sep 2018 08:14:12 GMT'), ('content-type', 'application/dns-message'), ('content-length', '103'), ('access-control-allow-origin', '*'), ('cache-control', 'max-age=5125'), ('expect-ct', 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"'), ('server', 'cloudflare-nginx'), ('cf-ray', '460c83ee69f73c53-CDG')]
id 0
opcode QUERY
rcode NOERROR
flags QR RD RA AD
edns 0
payload 1452
;QUESTION
www.bortzmeyer.org. IN AAAA
;ANSWER
www.bortzmeyer.org. 5125 IN AAAA 2001:4b98:dc0:41:216:3eff:fe27:3d3f
www.bortzmeyer.org. 5125 IN AAAA 2605:4500:2:245b::42
;AUTHORITY
;ADDITIONAL
2018-09-27 10:14:12,224:    DEBUG: Response trailers: {}

Ainsi qu'un résolveur simple (serveur DNS et client DoH).

Il ne faut pas confondre ce doh-proxy écrit en Python avec un logiciel du même nom écrit en Rust (je n'ai pas réussi à le compiler, celui-là, des compétences Rust plus avancées que les miennes sont nécessaires).

Et les clients, maintenant ? Commençons par le bien connu curl, qui a DoH est depuis la version 7.62.0 (pas encore publiée à l'heure où j'écris, le développement est documenté ici.) curl (comme Mozilla Firefox) fait la résolution DNS lui-même, ce qui est contestable (il me semble préférable que cette fonction soit dans un logiciel partagé par toutes les applications). Voici un exemple :


% ./src/.libs/curl -v --doh-url  https://doh.powerdns.org/ www.bortzmeyer.org
... [Se connecter au serveur DoH]
* Connected to doh.powerdns.org (2a01:7c8:d002:1ef:5054:ff:fe40:3703) port 443 (#2)
* ALPN, offering h2
...
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* Server certificate:
*  subject: CN=doh.powerdns.org
... [Envoyer la requête DoH]
* Using Stream ID: 1 (easy handle 0x5631606cbd50)
> POST / HTTP/2
> Host: doh.powerdns.org
> Accept: */*
> Content-Type: application/dns-message
> Content-Length: 36
...
< HTTP/2 200 
< server: h2o/2.2.5
< date: Thu, 27 Sep 2018 07:39:14 GMT
< content-type: application/dns-message
< content-length: 92
... [On a trouvé la réponse DoH]
* DOH Host name: www.bortzmeyer.org
* TTL: 86392 seconds
* DOH A: 204.62.14.153
* DOH AAAA: 2001:4b98:0dc0:0041:0216:3eff:fe27:3d3f
* DOH AAAA: 2605:4500:0002:245b:0000:0000:0000:0042
... [On peut maintenant se connecter au serveur HTTP - le but
principal de curl - maintenant qu'on a son adresse IP]
* Connected to www.bortzmeyer.org (204.62.14.153) port 80 (#0)
> GET / HTTP/1.1
> Host: www.bortzmeyer.org
> User-Agent: curl/7.62.0-20180927
> Accept: */*
> 

Pour les programmeurs Go, l'excellente bibliothèque godns n'a hélas pas DoH (pour des raisons internes). Du code expérimental avait été écrit dans une branche mais a été abandonné. Les amateurs de Go peuvent essayer à la place cette mise en œuvre (on notera que ce client sait parler DoH mais aussi le protocole spécifique et non-standard du résolveur public Google Public DNS).

Pour les programmeurs C, la référence est la bibliothèque getdns (notez que c'est elle qui a été utilisée pour le client doh-nghttp cité plus haut). Le code DoH est, à la parution du RFC, toujours en cours de développement et pas encore dans un dépôt public. Une fois que cela sera fait, stubby, qui utilise getdns, pourra parler DoH.

Voilà, si vous n'êtes pas épuisé·e·s, il y a encore des choses à lire :


Téléchargez le RFC 8484


L'article seul

Les générateurs de site Web statiques, et mon choix de Pelican

Première rédaction de cet article le 19 octobre 2018


J'ai récemment dû faire deux sites Web et leur cahier des charges permettait de faire des sites Web statiques, ce qui a de nombreux avantages. Parmi les dizaines de logiciels qui permettent de faire un site Web statique, j'ai choisi Pelican.

Pourquoi un site Web statique, et non pas généré à la demande via un CMS ? Cela présente plusieurs avantages :

  • Le principal est de performance et de résistance à la charge : un site Web dépendant d'un CMS doit exécuter des milliers de lignes de PHP ou de Java à chaque requête. Cela le rend lent et, surtout, une attaque par déni de service, même modérée (par exemple avec un outil primitif comme LOIC) suffit à le rendre inutilisable. Même pas besoin d'attaque, d'ailleurs, parfois, une simple utilisation un peu intense (une mention sur Mastodon, par exemple) peut le rendre inaccessible. Un site Web dynamique doit donc être hébergé sur de grosses machines coûteuses, alors qu'un site statique peut tenir une charge bien plus élevée. Tous les serveurs HTTP savent servir des fichiers statiques très rapidement.
  • Un deuxième avantage est de dépendance. Le site statique ne dépend que du serveur HTTP. Au contraire, un site dynamique dépend de plusieurs logiciels supplémentaires (comme par exemple MariaDB) dont la panne peut le rendre inutilisable, souvent avec des messages d'erreur incompréhensibles.
  • Enfin, le site statique présente un gros avantage de sécurité : au contraire du site dynamique, qui peut souvent être piraté, par exemple par injection SQL, le site statique n'exécutant pas de code et étant composé de simples fichiers de données, n'a pas de failles de sécurité, autres que celles du serveur HTTP et de la machine sous-jacente.

Bref, pas question d'utiliser WordPress ou Drupal. Mais, alors, comment faire ? Une solution évidente est de tout faire à la main, éditant le HTML du site avec un éditeur texte ordinaire. Cette solution n'est pas forcément agréable. Éditer du HTML n'est pas très amusant (même si, au début du Web, tout le monde faisait comme cela car il n'y avait pas le choix), et surtout, cela rend très difficile le maintien d'une apparence cohérente entre toutes les pages du site. Bien sûr, l'utilisation d'un fichier CSS identique pour toutes les pages fera que des questions esthétiques comme la couleur du fond ou comme la police utilisée seront traitées de manière identique pour toutes les pages. Mais cela ne règle pas le cas d'éléments qu'on veut voir partout, et que CSS ne sait pas gérer, ou en tout cas pas facilement avec les navigateurs actuels. Par exemple, si on veut un pied de page identique partout, avec une adresse du webmestre et un menu, faire les pages à la main nécessiterait du copier/coller pénible et, surtout, rendrait difficile toute modification ultérieure de ce pied de page.

Une solution possible serait d'utiliser un CMS pour créer du contenu, ce qui permettrait aux utilisateurs attachés à ces outils de rédiger du contenu, puis de transformer le site Web dynamique en statique, avec un outil comme httrack, et de faire servir ensuite le contenu statique. Mais, ici, comme j'étais le seul à ajouter du contenu, le problème d'utilisation d'un éditeur ne se posait pas, je suis plus à l'aise avec un éditeur de texte qu'avec les éditeurs des CMS.

Les générateurs de site Web statiques utilisent tous le concept de gabarit. On écrit un gabarit que doivent respecter les pages, puis on écrit les pages et le générateur de site Web statique réalise l'incarnation de ces pages en fichiers HTML qui suivent le gabarit. De tels outils sont presque aussi anciens que le Web. Le premier que j'ai utilisé était wml, puis Blosxom a été le plus à la mode, puis Jekyll. Et il en existe aujourd'hui une quantité étonnante, beaucoup de programmeurs ayant apparemment eu envie d'écrire le leur. Pour en choisir un, mon cahier des charges était logiciel libre, et disponible sous forme d'un paquetage dans la version stable de Debian (car je souhaitais réaliser des sites Web, pas passer du temps à de l'administration système et de l'installation de logiciels).

Voyons maintenant les différents générateurs de sites Web statiques que j'ai testé, en commençant par celui que j'ai finalement choisi, Pelican. Pelican est écrit en Python (cela peut avoir son importance si vous souhaitez en étendre les fonctions, et même le fichier de configuration d'un site est en Python). Il est surtout prévu pour des blogs, avec une notion de chronologie, alors que mon utilisation est différente, juste un ensemble de pages. La plupart de ces générateurs de sites statiques sont, comme Pelican, plutôt orientés vers le blog.

Une fois installé (paquetage Debian pelican), on crée un répertoire pour les fichiers de son site, on se rend dans ce répertoire, et on lance la commande pelican-quickstart qui va interactivement vous guider et générer les fichiers nécessaires. Comme toutes les commandes interactives, c'est pénible à utiliser mais cela peut être utile aux débutants. La plupart des questions sont simples et la valeur par défaut est raisonnable, sauf pour les dernières, où pelican-quickstart vous demande comment installer les pages produites sur votre site Web. Il propose FTP, SSH, et des commandes spécifiques pour des environnements fermés chez des GAFA comme Dropbox ou S3. Personnellement, je n'aime pas que la réponse à la question « Do you want to specify a URL prefix? » soit Oui par défaut. Dans ce cas, Pelican définit une variable SITEURL dans la configuration et tous les liens sont préfixés par cet URL, ce qui empêche de tester le site en local, un comble pour un générateur de sites statiques. Je réponds donc Non à cette question (ou bien je mets RELATIVE_URLS à True dans publishconf.py).

Ensuite, nous pouvons rédiger le premier article. Pelican accepte des articles faits en Markdown et reST. Ici, j'utilise Markdown. J'édite un premier article content/bidon.md, et j'y mets :

Title: C'est trop tôt
Date: 2018-10-11

Test *bidon*, vraiment.
    

Les deux premières lignes sont des métadonnées spécifiques à Pelican. Il faut indiquer le titre de l'article et la date (rappelez-vous que Pelican est optimisé pour des blogs, où les articles sont classés par ordre rétro-chronologique). Si, comme moi, vous faites un site Web qui n'est pas un blog, la date n'est pas nécessaire (les détails suivent). Ensuite, pelican-quickstart ayant créé le Makefile qui va bien, un simple make publish suffit à fabriquer les fichiers HTML, dans le répertoire ./output. On peut alors pointer son navigateur Web favori vers output/index.html. (pelican content donne le même résultat que make publish. Tapez make sans argument pour voir la liste des possibilités.) Pour copier les fichiers vers la destination, si vous avez configuré un des mécanismes de téléversement, vous pouvez faire make XXX_upload, où XXX est votre méthode de téléversement. Par exemple, si vous avez configuré SSH, ce sera make ssh_upload (mais vous pouvez évidemment éditer le Makefile pour donner un autre nom).

Si vous voulez changer la configuration du site après, les fichiers de configuration sont eux-même écrits en Python, donc il est utile de connaitre un peu ce langage pour éditer pelicanconf.py et publishconf.py. Par exemple, si vous voulez que les dates soient affichées en français, avoir répondu fr aux questions de pelican-quickstart ne suffit pas, il faut ajouter LOCALE = ('fr_FR', 'fr') à pelicanconf.py.

En mode blog, Pelican met chaque article dans une catégorie, indiquée dans les métadonnées (si vous ne mettez rien, comme dans l'exemple de fichier Markdown plus haut, c'est mis dans la catégorie « misc »).

Je l'avais dit, les deux sites Web où j'utilise Pelican ne sont pas de type blog, mais plutôt des sites classiques, avec un ensemble de pages, sans notion d'ordre chronologique. Pour cela, il faut mettre ces pages (Pelican appelle les fichiers d'un blog chronologique « articles » et les autres fichiers, par exemple les mentions légales, des « pages ») dans un répertoire content/pages, et dire à Pelican sous quel nom enregistrer l'HTML produit, sinon, ses choix peuvent être différents de ce qu'on aurait choisi (mais on n'a plus à mettre la date) :

% mkdir content/pages
% emacs content/pages/mentions-legales.md
% make publish
Done: Processed 3 articles, 0 drafts, 1 page and 0 hidden pages in 0.12 seconds.
    

Vous voyez que vous avez maintenant « 1 page », en plus des articles. Ces pages apparaitront en haut de la page d'accueil (tout ceci peut évidemment se changer, voyez plus loin quand on parlera des thèmes).

Pour avoir les pages enregistrées sous un nom de fichier de son choix, on utilise save_as :

% cat content/pages/mentions-legales.md
Title: Mentions légales
save_as: legal.html

Faites ce que vous voulez avec ce site.
    

(Voir la bonne FAQ sur ce sujet.) Évidemment, dans ce cas, le menu automatique ne marchera plus et il faudra, dans le thème qu'on développe, mettre son menu à soi. Mais je n'ai pas encore expliqué les thèmes. Avant cela, notons qu'on peut redéfinir la page d'accueil de la même façon, avec save_as: index.html.

Alors, les thèmes, c'est quoi ? C'est un ensemble de gabarits HTML, de fichiers CSS et d'images qui, ensemble, vont assurer la cohérence du site Web, ses menus, son apparence graphique. Si vous avez fait votre propre site en suivant les instructions ci-dessus, vous avez utilisé le thème par défaut, qui se nomme « simple » et qui est livré avec Pelican. C'est lui qui définit par exemple le pied de page « Proudly powered by Pelican, which takes great advantage of Python ». S'il ne vous plait pas, vous pouvez avoir votre propre thème, soit en l'écrivant en partant de zéro (c'est bien documenté) soit en utilisant un thème existant (on en trouve en ligne, par exemple en http://pelicanthemes.com/). Et, bien sûr, vous pouvez aussi prendre un thème existant et le modifier. Comme exemple, je vais faire un thème ultra-simple en partant de zéro.

Appelons-le « red » :

% mkdir -p themes/red
% mkdir -p themes/red/static/css
% mkdir -p themes/red/templates
% emacs themes/red/templates/base.html
% emacs themes/red/templates/page.html
% emacs themes/red/templates/article.html
      

Le code de base.html est disponible dans ce fichier, celui de page.html dans celui-ci et enfin celui de article.html à cet endroit. base.html définit les éléments communs aux articles et aux pages. Ainsi, il contient <footer>Mon joli site.</footer>, qui spécifie le pied qu'on trouvera dans tous les fichiers HTML.

Pelican permet également d'utiliser des variables et des tests, avec le moteur de gabarit Jinja. C'est ainsi qu'on spécifie le titre d'un article : {{ SITENAME }} - {{ article.title }} indique que le titre comprend d'abord le nom du site Web, puis un tiret, puis la variable article.titre, obtenue via la métadonnée Title: mise plus haut dans le source Markdown.

Une fois le thème créé, on peut l'utiliser en le spécifiant sur la ligne de commande :

% pelican -t themes/red content

Ou bien si on veut utiliser make publish comme avant, on ajoute juste dans pelicanconf.py :

    
THEME = 'themes/red'

Notez qu'on n'est pas obligé de mettre le thème dans un sous-dossier de themes. Si on ne compte pas changer de thème de temps en temps, on peut aussi placer static et templates dans le dossier themes, voire directement à la racine du répertoire de développement.

On a rarement un thème qui marche du premier coup, il y a plusieurs essais, donc un truc utile est la commande make regenerate, qui tourne en permanence, surveille les fichiers, et regénère automatiquement et immédiatement les fichiers HTML en cas de changement. Une autre commande utile est make serve qui lance un serveur Web servant le contenu créé, le site étant désormais accessible en http://localhost:8000/ (personnellement, x-www-browser output/index.html me suffit).

Si les fonctions de base ne suffisent pas, Pelican peut ensuite être étendu de plusieurs façons différentes. Notez d'abord que Jinja fournit beaucoup de possibilités (cf. sa documentation). Ensuite, imaginons par exemple qu'on veuille inclure des données CSV dans ses articles ou pages. On peut écrire un plugin (c'est bien documenté) mais il existe aussi plein de plugins tout faits, n'hésitez pas à chercher d'abord si l'un d'eux vous convient (ici, peut-être Load CSV). Mais on peut aussi envisager un programme dans le langage de son choix, qui lise le CSV et produise le Markdown. Ou bien, puisque les fichiers de configuration sont du Python, on peut y mettre du code Python, produisant des variables qu'on utilisera ensuite dans son thème (notez qu'on peut définir ses propres métadonnées).

Dernière solution, étendre Markdown avec une extension au paquetage Python markdown, qu'utilise Pelican. Ces extensions sont bien documentées, et il existe un tutoriel. On peut alors programmer dans l'extension tout ce qu'on veut, et produire le HTML résultant, avec Element Tree. C'est personnellement ce que j'utilise le plus souvent.

Pour terminer avec Pelican, quelques bonnes lectures :

Voici pour Pelican. Et les autres générateurs de sites statiques, du moins les quelques-uns que j'ai testés, parmi la grande variété disponible ? Actuellement, Hugo semble le plus à la mode. Il est programmé en Go. Comme Pelican, il est très nettement orienté vers la réalisation de blogs chronologiques. Le paquetage Debian est très ancien par rapport à la syntaxe toujours changeante, et je n'ai pas réussi à traiter un seul des thèmes existants, n'obtenant que des messages incompréhensibles. Je ne suis pas allé plus loin, je ne sais notamment pas si Hugo peut être utilisé pour des sites qui sont des ensembles de pages plutôt que des blogs.

Au contraire d'Hugo, tout beau et tout neuf, Jekyll est stable et fait du bon travail. Un coup de jekyll build et on a son site dans le répertoire _site. On peut aussi faire un site non-blog. Jekyll est lui, écrit en Ruby. Par contre, je n'ai pas bien compris comment on créait son thème, et c'est une des raisons pour lesquelles j'ai choisi Pelican.

Au contraire de Jekyll, Gutenberg est récent, et développé dans un langage récent, Rust. Trop nouveau pour moi, il nécessite le gestionnaire de paquetages Cargo qui n'existe apparemment pas sur Debian stable.

Écrit dans un langage bien moins commun, Hakyll est en Haskell, et même le fichier de configuration est un programme Haskell. Mais il est inutilisable sur Debian stable, en raison d'une bogue qui affiche un message très clair :

AesonException "Error in $.packages.cassava.constraints.flags: failed
to parse field packages: failed to parse field constraints: failed to
parse field flags: Invalid flag name: \"bytestring--lt-0_10_4\""

Enfin, j'ai regardé du côté de PyBlosxom mais j'ai renoncé, trouvant la documentation peu claire pour moi.

Les solutions les plus simples étant souvent les meilleures, il faut se rappeler qu'on peut tout simplement faire un site statique avec pandoc. pandoc prend des fichiers Markdown (et bien d'autres formats) et un coup de pandoc --standalone --to html5 -o index.html index.md (qu'on peut automatiser avec make) produit de l'HTML. Si on veut donner une apparence cohérente à ses pages Web, pandoc a égakement un système de gabarits (mais, la plupart du temps, il n'est même pas nécessaire, utiliser le gabarit par défaut et définir quelques variables sur la ligne de commande de pandoc peut suffire).

Comme je l'ai dit au début, il y a vraiment beaucoup de générateurs de sites statiques et je suis très loin de les avoir tous testés. Rien que dans les paquetages Debian, il y a encore staticsite et blogofile.

Vu le grand nombre de générateurs de sites Web statiques, on ne s'étonne pas qu'il existe plusieurs articles de comparaison :

Et, évidemment, il existe toujours la solution bien geek consistant à développer son propre outil, comme je l'avais fait pour ce blog.


L'article seul

Fiche de lecture : Click here to kill everybody

Auteur(s) du livre : Bruce Schneier
Éditeur : Norton
978-0393-60888-5
Publié en 2018
Première rédaction de cet article le 6 octobre 2018


D'accord, le titre est vraiment putaclic mais il résume bien le livre. Bruce Schneier se pose la question de la sécurité de l'Internet des Objets, faisant notamment remarquer que le conséquences d'une panne ou d'une attaque changent. Contrairement à la sécurité informatique classique, avec l'Internet des Objets, il peut y avoir des conséquences dans le monde physique, y compris des conséquences mortelles.

Schneier est l'auteur de nombreux livres sur la sécurité (pas forcément uniquement la sécurité informatique). Il explique toujours bien et synthétise avec talent les questions de sécurité. C'est d'autant plus méritoire que la sécurité est un sujet hautement politisé, où il est difficile de parler sérieusement. Pensons par exemple aux mesures adoptées pour lutter contre l'ennemi djihadiste. Dès qu'on fait entendre un point de vue critique, ou simplement nuancé, on se fait accuser d'être « trop mou face au terrorisme » voire « complice des terroristes ». Schneier appelle au contraire à envisager la sécurité comme un sujet sérieux, et donc à s'abstenir de ce genre d'accusations tranchantes. Dans ses livres (comme « Beyond Fear ») et sur son blog, il remet sans cesse en cause les certitudes, critique le « show sécuritaire », demande qu'on évalue les mesures de sécurité, leur efficacité et leur coût, au lieu de simplement dire « il faut faire quelque chose, peu importe quoi ».

Le sujet de ce livre (plutôt un essai relativement court) est l'Internet des Objets. C'est un terme marketing, flou et mal défini. Schneier lui préfère celui d'« Internet+ », dont il reconnait qu'il n'est pas meilleur mais qu'il a l'avantage de forcer à reconsidérer de quoi on parle. En gros, il y a aujourd'hui énormément d'« objets » connectés à l'Internet. Ils ont en commun d'être des ordinateurs, mais de ne pas être perçus comme tels. Ce sont des ordinateurs, car ils en ont le matériel et surtout le logiciel, avec ses bogues et ses failles de sécurité. (Pour paraphraser l'auteur, « Un grille-pain moderne est un ordinateur avec des résistances chauffantes en plus ».) Mais ils ne sont pas perçus comme tels, donc le logiciel ne fait l'objet d'aucune analyse de sécurité, le problème de la mise à jour n'est pas envisagé, et les sociétés qui produisent ces objets n'ont aucune culture de sécurité, et refont en 2018 les erreurs que l'industrie informatique faisait il y a 20 ans (mots de passe par défaut, menaces judiciaires contre ceux qui signalent des failles de sécurité, tentative d'empêcher la rétro-ingénierie, affirmations grotesques du genre « notre système est parfaitement sécurisé »). La sécurité des objets connectés, de l'« Internet+ » est donc abyssalement basse. À chaque conférence de sécurité, on a de nombreux exposés montrant la facilité avec laquelle ces objets peuvent être piratés. Schneier cite une classe de politique du monde numérique où, au cours d'un travail pratique, la moitié des étudiants ont réussi à pirater une poupée connectée, alors même qu'ils et elles sont des juristes ou des étudiants en sciences politiques, pas des pentesteurs.

Tant que l'object connecté est une brosse à dents, et que le piratage a pour seule conséquence d'empêcher cette brosse de fonctionner, ce n'est pas trop grave. Mais beaucoup d'objets ont des capacités bien plus étendues, et qui touchent le monde physique (d'où le titre sensationnaliste du livre). Si l'objet est une voiture, ou un dispositif de sécurité d'une usine, ou un appareil électrique potentiellement dangereux, les conséquences peuvent être bien plus graves. On est loin des problèmes de sécurité de WordPress, où la seule conséquence en cas de piratage est l'affichage d'un message moqueur sur la page d'accueil du site Web !

(Je rajoute, à titre personnel - ce n'est pas dans le livre, qu'il est scandaleux que, pour beaucoup d'objets, l'acheteur n'ait plus le choix. Aujourd'hui, acheter une télévision ou une voiture qui ne soit pas connectée, est devenu difficile, et demain, ce sera impossible. Un changement aussi crucial dans nos vies a été décidé sans que le citoyen ait eu son mot à dire.)

Schneier explique en détail les raisons techniques, pratiques et financières derrière l'insécurité informatique mais il note que cette insécurité ne déplait pas à tout le monde. Des services étatiques comme la NSA (dont la mission est justement de pirater des systèmes informatiques) aux entreprises qui gagnent de l'argent en exploitant des données personnelles, des tas d'organisations profitent de cette insécurité, ce qui est une des raisons pour laquelle elle ne se réduit guère. (Pour la NSA, Schneier préconise de la séparer en deux organisations radicalement distinctes, une chargée de l'attaque et une de la défense. Actuellement, la NSA est censée faire les deux, et, en pratique, l'attaque est toujours jugée plus importante. Cela amène la NSA, par exemple, à ne pas transmettre aux auteurs de logiciels les failles de sécurité détectées, de façon à pouvoir les exploiter. Le système français a aussi ses défauts mais, au moins, l'attaque - armée et DGSE - et la défense - ANSSI - sont clairement séparées, et la défense ne dépend pas de l'armée ou de la police, qui sont intéressées à conserver l'insécurité informatique.)

Notez que le livre est clairement écrit d'un point de vue états-unien et parle surtout de la situation dans ce pays.

Et les solutions ? Parce que ce n'est pas tout de faire peur aux gens, avec des scénarios qui semblent sortis tout droit des séries télé « Black Mirror » ou « Mr Robot ». Il faut chercher des solutions. L'auteur expose successivement le quoi, le comment et le qui. Le quoi, c'est le paysage que nous voudrions voir émerger, celui d'un « Internet+ » dont la sécurité ne soit pas risible, comme elle l'est actuellement. Les solutions ne vont pas de soi, car elles auront forcément un coût, et pas uniquement en argent, mais aussi en facilité d'usage et en générativité (pour reprendre le terme de Jonathan Zittrain, qui désigne ainsi la capacité d'une technique à faire des choses non prévues par ses concepteurs). Un exemple d'un choix difficile : le logiciel de l'objet doit-il être mis à jour automatiquement et à distance ? Du point de vue de la sécurité, c'est clairement oui, mais cela ouvre des tas de problèmes, comme la possibilité pour le vendeur de garder un contrôle sur l'objet vendu (cf. RFC 8240 pour les détails). Autre point qui sera difficile à avaler (et l'auteur n'en parle d'ailleurs que très peu) : il ne faudra pas tout connecter. Connecter des frigos et des télés à l'Internet est peut-être pratique et sexy mais c'est dangereusement irresponsable.

Le comment, ce sont les moyens d'y arriver. Et le qui, c'est la question brûlante de savoir quelle organisation va devoir agir.

Schneier ne croit pas au marché, qui a largement démontré son incapacité à résoudre les problèmes de sécurité. Cela semble une évidence mais il ne faut pas oublier que Schneier écrit pour un public états-unien, pour qui le marché est sacré, et toute suggestion comme quoi le marché n'est pas parfait est vite assimilée au communisme. Bruce Schneier suggère donc un rôle essentiel pour l'État, une position courageuse quand on écrit aux États-Unis. Il ne se fait pas d'illusions sur l'État (il décrit plusieurs cas où l'État, ses lois, ses règles et ses pratiques ont contribué à aggraver le problème, comme par exemple la loi DMCA) mais il ne voit pas d'autre option réaliste, en tout cas certainement pas l'« auto-régulation » (autrement dit le laisser-faire) chère à la Silicon Valley.

Bruce Schneier est bien conscient qu'il n'y a pas de solution idéale, et que la sécurisation de l'Internet+ sera douloureuse. Si vous lisez ce livre, ce que je vous recommande fortement, vous ne serez certainement pas d'accord avec tout, comme moi. (Par exemple, la proposition de faire du FAI le responsable de la sécurité des réseaux des utilisateurs à la maison m'inquiète. Et sa suggestion d'ajouter encore des règles et des processus, alors qu'au contraire cela sert en général à encourager l'irresponsabilité n'est pas idéale non plus.) Mais ne nous faisons pas d'illusion : on n'aura pas de solution parfaite. Et, si nous ne faisons rien, nous aurons peut-être des « solutions » catastrophiques, par exemple des règles ultra-liberticides imposées en mode panique par des politiciens affolés, après une grosse crise due à un objet connecté.

Vous serez peut-être également intéressé·e par cet exposé de l'auteur au sujet de ce livre.


L'article seul

Conférence « Web et vie privée » au Centre Social des Abeilles

Première rédaction de cet article le 30 septembre 2018


Le 28 septembre, au Centre Social des Abeilles à Quimper, j'ai participé à un apéro/discussion sur le thème « Web et vie privée ». Je faisais l'introduction à la discussion

Voici les supports de cette introduction :

La vidéo est en ligne. La longue discussion qui a suivi ne l'est pas, pour respecter la vie privée des participants. (Une autre copie de la vidéo est sur le site original.)

Merci à Brigitte pour l'idée et l'organisation, à Salim pour l'accueil, à René pour la vidéo et à tou·te·s les participant·e·s, nombreu·x·ses et acti·f·ve·s. Un compte-rendu de cet apéro-discussion, avec plans pour le futur a été écrit par Brigitte. (Il y a aussi mon interview à France Bleu mais le titre choisi par la rédaction est trompeuse.)


L'article seul

[DNS] CNAME à l'apex d'une zone, pourquoi et comment ?

Première rédaction de cet article le 23 septembre 2018


Un problème courant que rencontrent les techniciens débutant en DNS est « je voudrais mettre un enregistrement CNAME - un alias - à l'apex de ma zone DNS mais l'ordinateur ne veut pas ». Pourquoi est-ce refusé ? Comment l'autoriser sans casser tout l'Internet ? La discussion dure depuis de nombreuses années et, je vous révèle tout de suite la conclusion de cet article, n'est pas près de se terminer.

Commençons par une description concrète du problème. Alice, technicienne DNS, a été informée par le webmestre de sa boîte (nommée Michu SA et ayant le nom de domaine michu.example) que le serveur Web de ladite boîte est désormais hébergé par un CDN nommé Example et qu'il est accessible par le nom michu-sa.example-cdn.net. Et le webmestre voudrait que les visiteurs puissent juste taper https://michu.example/. Alice connait assez le DNS pour savoir qu'il y a des alias (un type d'enregistrement nommé CNAME) et elle met donc dans le fichier de zone :

michu.example. IN CNAME michu-sa.example-cdn.net.
    

Ça devrait contenter tout le monde, pense-t-elle. Mais, si elle connait assez le DNS pour savoir que le type d'enregistrement CNAME existe, elle ne le connait pas assez pour avoir lu le RFC 1034, section 3.6.2 : If a CNAME RR is present at a node [a node in the domain name tree, so a domain name], no other data should be present. Et c'est le drame, au chargement de la zone, NSD dit error: /etc/nsd/michu.example:19: CNAME and other data at the same name. Si Alice avait utilisé BIND, elle aurait eu une erreur similaire : dns_master_load: michu.example:19: michu.example: CNAME and other data. L'enregistrement CNAME rentre en conflit avec les enregistrements qu'on trouve à l'apex d'une zone, comme le SOA et les NS. La solution simple ne marche donc pas.

www.michu.example. IN CNAME michu-sa.example-cdn.net.      
    

aurait marché mais le service communication voudrait un URL sans www devant.

Au passage, pourquoi est-ce que c'est interdit de mettre un alias et d'autres données au même nom ? La principale raison est qu'un enregistrement CNAME est censé créer un alias, un synonyme. Si on pouvait écrire :

michu.example. IN AAAA 2001:db8::ff
michu.example. IN CNAME michu-sa.example-cdn.net.      
    

Et qu'un client DNS demande l'enregistrement de type AAAA pour michu.example, que faudrait-il lui répondre ? 2001:db8::ff, ou bien l'adresse IPv6 de michu-sa.example-cdn.net ? Avec l'interdiction de coexistence du CNAME et d'autres données, le problème est réglé.

Bon, se dit Alice, ça ne marche pas, mais on peut essayer autrement : cherchons l'adresse IP correspondant au nom michu-sa.example-cdn.net et mettons-là dans le fichier de zone :

% dig +short AAAA michu-sa.example-cdn.net
2001:db8:1000:21::21:4
    
michu.example. IN AAAA 2001:db8:1000:21::21:4 
    

Cette fois, ça marche, mais il y a plusieurs inconvénients :

  • Le principal est que le CDN peut changer l'adresse IP à tout moment, sans prévenir. Au minimum, il faudra donc retester l'adresse, et changer la zone. À la main, aucune chance que ce soit fait assez souvent (il faut respecter le TTL du CDN). Il faudra donc l'automatiser.
  • Des CDN renvoient une adresse IP différente selon l'adresse IP source du client DNS, afin de diriger vers un serveur « proche ». L'astuce d'Alice casse ce service.

Bref, que faire ? Plusieurs sociétés ont développé une solution non-standard ne marchant que chez eux. L'IETF, dont c'est le rôle de développer des solutions standards, s'est penchée à de nombreuses reprises sur la question, et d'innombrables brouillons ont été produits, sans qu'un consensus ne se dégage. Déjà, il n'y a pas d'accord clair sur le cahier des charges. (Le scénario d'usage présenté plus haut n'est qu'un des scénarios possibles.) Je ne vais pas présenter tous ces brouillons, juste dégager les pistes de solution. Mais, comme vous le verrez, aucune n'est parfaite. Une des contraintes fortes est qu'on sait bien que, quelle que soit la décision prise par l'IETF, le nouveau logiciel, mettant en œuvre la décision, ne sera pas déployé immédiatement : il faudra des années, voire davantage, pour qu'une part significative des clients et serveurs DNS soient à jour. Il faudra donc, non seulement modifier les règles du DNS, mais également spécifier ce qu'il faudra faire pendant la très longue période de transition. (Et, s'il vous plait, pas de yakafokon du genre « les gens n'ont qu'à mettre à jour leur logiciel plus souvent ». Cela n'arrivera pas.)

Voici maintenant les diverses pistes envisagées. Avant de dire « ah, celle-ci a l'air cool », prudence. Rappelez-vous qu'il existe de nombreux cas (minimisation de la requête - RFC 7816 - ou pas, résolveur chaud - ayant déjà des informations dans sa mémoire - ou pas), et que la période de transition va être très longue, période pendant laquelle il faut que cela marche pour les anciens et pour les modernes.

Première idée, décider qu'il n'y a pas de problème. On laisse le DNS comme il est et le problème doit être entièrement traité côté avitaillement (le type qui édite le fichier de zone, ou bien le logiciel qui le produit). C'est l'idée de base du hack utilisé par Alice plus haut. Cela peut se faire avec un script shell du genre :

#!/bin/sh

zonefile=$1

while :
do
    target=$(awk '/ALIAS/ {print $3}'  $zonefile)
    ttl=$(dig A $target | awk "/^$target.*IN\s+A/ {print \$2}" | head -n 1)
    ip=$(dig A $target | awk "/^$target.*IN\s+A/ {print \$5}" | head -n 1)
    sed "s/IN.*ALIAS.*/  IN $ttl   A  $ip/" $zonefile > ${zonefile}-patched
    echo "Patched with IP address $ip"
    sleep $ttl
done
   

(En production, on voudra probablement quelque chose de plus propre, notamment en gestion d'erreurs, et de traitement des réponses multiples.)

Comme indiqué plus haut, cela marche, mais cela ne permet pas de tirer profit des caractéristiques du CDN, par exemple de la réponse différente selon le client. On peut voir cette variation de la réponse, en demandant à cent sondes RIPE Atlas :

% blaeu-resolve --requested 100 --type A www.elysee.fr                    
[208.178.167.254 4.26.226.126 4.27.28.126 8.27.4.254] : 1 occurrences 
[8.254.28.126 8.254.45.254 8.27.151.253 8.27.226.126] : 1 occurrences 
[205.128.73.126 206.33.35.125 209.84.20.126 8.27.243.253] : 1 occurrences 
[207.123.56.252 4.26.228.254 4.27.28.126 8.26.223.254] : 1 occurrences 
[205.128.90.126 209.84.9.126 8.254.214.254 8.254.94.254] : 1 occurrences 
[8.254.164.126 8.254.209.126 8.254.210.126 8.27.9.254] : 1 occurrences 
[4.23.62.126 4.26.227.126 8.254.173.254 8.254.95.126] : 1 occurrences 
[207.123.39.254 8.254.196.126 8.254.219.254 8.27.5.254] : 2 occurrences 
[120.28.42.254 36.66.10.126] : 1 occurrences 
...
   

Deuxième idée, relâcher la contrainte donnée dans le RFC 1034 et autoriser le CNAME à l'apex (note au passage : beaucoup de gens sont nuls en arbrologie et appellent incorrectement l'apex la racine, ce qui a un tout autre sens dans le DNS). L'expérience a été tentée par Ondřej Surý au hackathon de l'IETF à Montréal en juillet 2018 (les résultats complets de l'équipe DNS sont dans cette présentation). Une fois le serveur faisant autorité modifié pour autoriser le CNAME à l'apex, on l'interroge via un résolveur. En gros, dans la plupart des cas, le CNAME masque les autres enregistrements (ce qui est logique puisqu'il est censé être seul). Ainsi, si on a :

michu.example.     IN  CNAME   foobar.example.com.
                   IN  MX  10    gimmedata.gafa.example.
   

Un client DNS qui demande le MX de michu.example pour lui envoyer du courrier récupérera le MX de foobar.example.com, le vrai MX étant masqué.

Plus ennuyeux est le fait que cela dépend : dans certains cas, un autre enregistrement que celui du CNAME est récupéré. On ne peut rien reprocher aux logiciels qui font cela : ils sont conformes aux RFC actuels. Cette variabilité rend difficile de simplement autoriser le CNAME à l'apex.

Troisième possibilité : on peut aussi décider que le résolveur fera l'essentiel du boulot. On crée un nouveau type d'enregistrement, mettons SEEALSO, qui peut coexister avec les types existants :

michu.example.   IN SEEALSO  michu-sa.example-cdn.net.          
michu.example.   IN MX 10 gimmedata.gafa.example.
   

Le serveur faisant autorité n'aurait rien de particulier à faire, il renvoie juste le SEEALSO au résolveur (en terminologie DNS, on dit « il n'y a pas de traitement additionnel »). Le résolveur va alors, recevant le SEEALSO, le suivre. Le principal problème de cette approche est qu'initialement, peu de résolveurs auront le nouveau code, ce qui ne motivera pas les gens qui gèrent les zones à ajouter ce SEEALSO.

Quatrième idée, toujours avec un nouveau type d'enregistrement (et donc avec les problèmes de déploiement que cela pose dans un Internet non centralisé), généralement appelé ANAME ou ALIAS. L'idée est qu'on mettra dans la zone :

michu.example.   IN ANAME  michu-sa.example-cdn.net.          
michu.example.   IN MX 10 gimmedata.gafa.example.
   

Le ANAME, contrairement au CNAME, a le droit de coexister avec d'autres enregistrements, ici un MX. L'idée est que le serveur faisant autorité, chargeant la zone, va résoudre la cible (la partie droite du ANAME) et répondre avec l'adresse IP de la cible lorsqu'on l'interrogera. Le résolveur ne voit donc pas le CDN, si l'adresse au CDN est 2001:db8::ff, le résolveur recevra :

michu.example.   3600 IN AAAA 2001:db8::ff   
   

Le serveur faisant autorité, ayant chargé la zone, sera responsable de changer cette valeur lorsque le TTL expirera.

On voit que cette solution nécessite que le serveur faisant autorité soit également résolveur. C'est considéré comme une mauvaise pratique, car cela complique sérieusement le déboguage : on ne sait plus d'où viennent les données, et celles du résolveur peuvent potentiellement masquer celles qui font autorité. D'ailleurs, les meilleurs logiciels serveur faisant autorité, comme NSD, n'ont pas du tout de code pour faire de la résolution (cette simplicité améliore grandement la sécurité).

D'autre part, introduire un nouveau type de données DNS n'est jamais évident, cela nécessite de modifier les serveurs faisant autorité (les résolveurs, eux, n'ont pas besoin d'être modifiés, pour ce ANAME), mais également les logiciels d'avitaillement (interfaces web chez l'hébergeur DNS permettant de gérer sa zone).

Cette idée de ANAME pose également des problèmes à DNSSEC. Comme le serveur faisant autorité va devoir modifier le contenu de la zone fréquemment (les TTL des CDN sont souvent courts), il faudra qu'il signe les enregistrements créés, ce qui obligera à avoir la clé privée disponible (on préfère parfois la garder hors-ligne, pour des raisons de sécurité, ce qui est opérationnellement faisable si on ne modifie pas la zone trop souvent). Et cela nécessitera un serveur qui peut signer dynamiquement, ou bien des bricolages particuliers.

On peut avoir des tas de variantes sur cette idée. C'est d'ailleurs une des raisons pour laquelle le débat est compliqué. Chaque idée a des sous-idées. Par exemple, puisque dans ce cas, le serveur faisant autorité est également résolveur, on pourrait renvoyer le ANAME au client DNS, avec l'adresse IP du CDN, pour gagner du temps. La réponse serait :

;; ANSWER SECTION:
michu.example.   3600 IN ANAME michu-sa.example-cdn.net.          

;; ADDITIONAL SECTION:
michu-sa.example-cdn.net. 600 IN AAAA 2001:db8::ff   
   

Plus de problèmes avec DNSSEC cette fois, puisqu'on n'importerait plus de données extérieures dans la zone michu.example. Le principal problème de cette variante est que l'optimisation serait probablement inutile : un résolveur DNS raisonnablement paranoïaque, craignant une attaque par empoisonnement, ignorerait la section additionnelle, puisque le serveur interrogé ne fait autorité que pour michu.example, pas pour example-cdn.net. On serait donc ramené à la troisième idée (le résolveur fait tout).

Cinquième idée, résoudre le problème côté client, comme dans la troisième, mais en modifiant les applications et non plus les résolveurs. Après tout, le principal scénario d'usage est pour HTTP. Ce sont les gens du Web qui se plaignent de ne pas pouvoir mettre des CNAME à l'apex. Les gens de SMTP ou de XMPP ne se plaignent pas, car ils ont un système d'indirection. On indique dans le DNS le nom du serveur pour un domaine donné (enregistrement MX pour SMTP et les plus généraux enregistrements SRV pour XMPP). HTTP est hélas le seul protocole normalisé qui a « oublié » de faire une indirection entre domaine et serveur. C'est une de ses plus graves fautes de conception. (Un protocole comme SSH est un cas à part puisque son but est de se connecter à une machine spécifique.) Donc, la meilleure solution, du point de vue de l'architecture de l'Internet, est de modifier HTTP pour que les clients HTTP utilisent SRV. La zone serait alors :

michu.example.   IN SRV 0 1 80  michu-sa.example-cdn.net.          
; Autres types
michu.example.   IN MX 10 gimmedata.gafa.example.
   

SRV, normalisé dans le RFC 2782, a plein d'autres possibilités très pratiques, comme d'indiquer plusieurs serveurs, avec des poids différents (fournissant ainsi un système non centralisé de répartition de charge) et des priorités différentes (les serveurs de faible priorité n'étant sollicité qu'en cas de panne des autres).

Tout cela pose évidemment un réel problème de déploiement puisqu'il faudrait modifier tous les clients HTTP (rappelez-vous qu'il n'y a pas que les navigateurs !) Tant qu'il n'y aura pas de déploiement significatif, les titulaires de noms de domaine devront avoir le SRV et les enregistrements classiques. D'autre part, HTTP ayant évolué sans les enregistrements SRV, il y a quelques points d'accrochage. Par exemple, SRV permet d'indiquer le port et cela peut rentrer en conflit avec le concept d'origine du Web, qui est essentiel pour sa sécurité (http://example.com/ et http://example.com:3000/ sont des origines différentes, cf. RFC 6454). Il y a aussi quelques pièges liés au SRV (voir mes notes à ce sujet et, cette discussion au sujet des SRV dans Mastodon.)

Et je n'ai pas cité toutes les idées, comme la possibilité d'utiliser Alt-Svc (RFC 7838).

Conclusion ? Le problème est bien sûr réel et se pose à beaucoup d'acteurs de l'Internet. Mais il n'y a pas de solution idéale. Il faudra, soit continuer comme actuellement si l'IETF n'arrive pas à un accord, soit adopter une solution qui, de toute façon, créera ses propres problèmes. Personnellement, je pense que la solution la plus propre serait de modifier HTTP, pour utiliser une indirection, comme tous les autres protocoles. Si cela n'est pas possible, il vaut encore mieux ne rien faire : le dromadaire est assez chargé comme cela.


L'article seul

Fiche de lecture : Le livre-échange

Auteur(s) du livre : Mariannig Le Béchec, Dominique Boullier, Maxime Crépel
Éditeur : C & F Éditions
978-2-915825-76-3
Publié en 2018
Première rédaction de cet article le 30 août 2018


Le livre, sous ses deux formes, papier ou numérique, est un objet de passion depuis longtemps. Ce ne sont pas juste des lettres qu'on lit. Les lecteurices ont des usages, des pratiques, ils et elles se prêtent les livres, les annotent, les commentent, les échangent, en parlent sur leur blog…

Ce livre est une étude de ces pratiques. Que font les lecteurs de leur livre ? Pour chaque pratique, des lecteurices sont interrogé·e·s, des experts commentent parfois. On ne parle pas du texte, uniquement des usages que les lecteurices font du livre.

Les usages du livre sur papier forment l'essentiel de cet ouvrage, qui note que le livre numérique fait apparemment peu l'objet de ces diverses formes d'échange. On le lit, mais c'est tout.

Les usages du livre papier sont très variés (sans compter, dirait le cynique, celui de caler les meubles). Et les avis des lecteurices sont également divers. L'annotation, par exemple, est sans doute le chapitre où les avis sont les plus tranchés, entre celles et ceux qui considèrent le livre comme « sacré », et qui ne se permettraient pas d'y ajouter la moindre marque, et ceux et celles qui griffonnent, corrigent, et ajoutent des commentaires au stylo rouge. Le livre n'est clairement pas un objet comme les autres. À propos de l'achat de livres d'occasion, une lectrice explique qu'elle n'y recourt pas, car elle lit au lit et qu'elle ne veut pas faire rentrer dans son lit, espace intime, des livres déjà manipulés par un inconnu…

En résumé, un bel hommage à l'abondance de pratiques sociales autour du livre.

Déclaration de conflit d'intérêt : j'ai reçu un exemplaire de ce livre gratuitement.


L'article seul

RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3

Date de publication du RFC : Août 2018
Auteur(s) du RFC : E. Rescorla (RTFM)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 11 août 2018


Après un très long processus, et d'innombrables polémiques, la nouvelle version du protocole de cryptographie TLS, la 1.3, est enfin publiée. Les changements sont nombreux et, à bien des égards, il s'agit d'un nouveau protocole (l'ancien était décrit dans le RFC 5246, que notre nouveau RFC remplace).

Vous pouvez voir l'histoire de ce RFC sur la Datatracker de l'IETF. Le premier brouillon a été publié en avril 2014, plus de trois années avant le RFC. C'est en partie pour des raisons techniques (TLS 1.3 est très différent de ses prédécesseurs) et en partie pour des raisons politiques. C'est que c'est important, la sécurité ! Cinq ans après les révélations de Snowden, on sait désormais que des acteurs puissants et sans scrupules, par exemple les États, espionnent massivement le trafic Internet. Il est donc crucial de protéger ce trafic, entre autres par la cryptographie. Mais dire « cryptographie » ne suffit pas ! Il existe des tas d'attaques contre les protocoles de cryptographie, et beaucoup ont réussi contre les prédécesseurs de TLS 1.3. Il était donc nécessaire de durcir le protocole TLS, pour le rendre moins vulnérable. Et c'est là que les ennuis ont commencé. Car tout le monde ne veut pas de la sécurité. Les États veulent continuer à espionner (le GCHQ britannique s'était clairement opposé à TLS 1.3 sur ce point). Les entreprises veulent espionner leurs employés (et ont pratiqué un lobbying intense contre TLS 1.3). Bref, derrière le désir de « sécurité », partagé par tout le monde, il y avait un désaccord de fond sur la surveillance. À chaque réunion de l'IETF, une proposition d'affaiblir TLS pour faciliter la surveillance apparaissait, à chaque fois, elle était rejetée et, tel le zombie des films d'horreur, elle réapparaissait, sous un nom et une forme différente, à la réunion suivante. Par exemple, à la réunion IETF de Prague en juillet 2017, l'affrontement a été particulièrement vif, alors que le groupe de travail TLS espérait avoir presque fini la version 1.3. Des gens se présentant comme enterprise networks ont critiqué les choix de TLS 1.3, notant qu'il rendait la surveillance plus difficile (c'était un peu le but…) gênant notamment leur déboguage. Ils réclamaient un retour aux algorithmes n'ayant pas de sécurité persistante. Le début a suivi le schéma classique à l'IETF : « vous réclamez un affaiblissement de la sécurité » vs. « mais si on ne le fait pas à l'IETF, d'autres le feront en moins bien », mais, au final, l'IETF est restée ferme et n'a pas accepté de compromissions sur la sécurité de TLS. (Un résumé du débat est dans « TLS 1.3 in enterprise networks ».)

Pour comprendre les détails de ces propositions et de ces rejets, il faut regarder un peu en détail le protocole TLS 1.3.

Revenons d'abord sur les fondamentaux : TLS est un mécanisme permettant aux applications client/serveur de communiquer au travers d'un réseau non sûr (par exemple l'Internet) tout en empêchant l'écoute et la modification des messages. TLS suppose un mécanisme sous-jacent pour acheminer les bits dans l'ordre, et sans perte. En général, ce mécanisme est TCP. Avec ce mécanisme de transport, et les techniques cryptographiques mises en œuvre par dessus, TLS garantit :

  • L'authentification du serveur (celle du client est facultative), authentification qui permet d'empêcher l'attaque de l'intermédiaire, et qui se fait en général via la cryptographie asymétrique,
  • La confidentialité des données (mais attention, TLS ne masque pas la taille des données, permettant certaines analyses de trafic),
  • L'intégrité des données (qui est inséparable de l'authentification : il ne servirait pas à grand'chose d'être sûr de l'identité de son correspondant, si les données pouvaient être modifiées en route).

Ces propriétés sont vraies même si l'attaquant contrôle complètement le réseau entre le client et le serveur (le modèle de menace est détaillé dans la section 3 - surtout la 3.3 - du RFC 3552, et dans l'annexe E de notre RFC).

TLS est un protocole gros et compliqué (ce qui n'est pas forcément optimum pour la sécurité). Le RFC fait 147 pages. Pour dompter cette complexité, TLS est séparé en deux composants :

  • Le protocole de salutation (handshake protocol), chargé d'organiser les échanges du début, qui permettent de choisir les paramètres de la session (c'est un des points délicats de TLS, et plusieurs failles de sécurité ont déjà été trouvées dans ce protocole pour les anciennes versions de TLS),
  • Et le protocole des enregistrements (record protocol), au plus bas niveau, chargé d'acheminer les données chiffrées.

Pour comprendre le rôle de ces deux protocoles, imaginons un protocole fictif simple, qui n'aurait qu'un seul algorithme de cryptographie symétrique, et qu'une seule clé, connue des deux parties (par exemple dans leur fichier de configuration). Avec un tel protocole, on pourrait se passer du protocole de salutation, et n'avoir qu'un protocole des enregistrements, indiquant comment encoder les données chiffrées. Le client et le serveur pourraient se mettre à communiquer immédiatement, sans salutation, poignée de mains et négociation, réduisant ainsi la latence. Un tel protocole serait très simple, donc sa sécurité serait bien plus facile à analyser, ce qui est une bonne chose. Mais il n'est pas du tout réaliste : changer la clé utilisée serait complexe (il faudrait synchroniser exactement les deux parties), remplacer l'algorithme si la cryptanalyse en venait à bout (comme c'est arrivé à RC4, cf. RFC 7465) créerait un nouveau protocole incompatible avec l'ancien, communiquer avec un serveur qu'on n'a jamais vu serait impossible (puisque on ne partagerait pas de clé commune), etc. D'où la nécessité du protocole de salutation, où les partenaires :

  • S'authentifient avec leur clé publique (ou, si on veut faire comme dans le protocole fictif simple, avec une clé secrète partagée),
  • Sélectionnent l'algorithme de cryptographie symétrique qui va chiffrer la session, ainsi que ses paramètres divers,
  • Choisir la clé de la session TLS (et c'est là que se sont produites les plus grandes bagarres lors de la conception de TLS 1.3).

Notez que TLS n'est en général pas utilisé tel quel mais via un protocole de haut niveau, comme HTTPS pour sécuriser HTTP. TLS ne suppose pas un usage particulier : on peut s'en servir pour HTTP, pour SMTP (RFC 7672), pour le DNS (RFC 7858), etc. Cette intégration dans un protocole de plus haut niveau pose parfois elle-même des surprises en matière de sécurité, par exemple si l'application utilisatrice ne fait pas attention à la sécurité (Voir mon exposé à Devoxx, et ses transparents.)

TLS 1.3 est plutôt un nouveau protocole qu'une nouvelle version, et il n'est pas directement compatible avec son prédécesseur, TLS 1.2 (une application qui ne connait que 1.3 ne peut pas parler avec une application qui ne connait que 1.2.) En pratique, les bibliothèques qui mettent en œuvre TLS incluent en général les différentes versions, et un mécanisme de négociation de la version utilisée permet normalement de découvrir la version maximum que les deux parties acceptent (historiquement, plusieurs failles sont venues de ce point, avec des pare-feux stupidement configurés qui interféraient avec la négociation).

La section 1.3 de notre RFC liste les différences importantes entre TLS 1.2 (qui était normalisé dans le RFC 5246) et 1.3 :

  • La liste des algorithmes de cryptographie symétrique acceptés a été violemment réduite. Beaucoup trop longue en TLS 1.2, offrant trop de choix, comprenant plusieurs algorithmes faibles, elle ouvrait la voie à des attaques par repli. Les « survivants » de ce nettoyage sont tous des algorithmes à chiffrement intègre.
  • Un nouveau service apparait, 0-RTT (zero round-trip time, la possibilité d'établir une session TLS avec un seul paquet, en envoyant les données tout de suite), qui réduit la latence du début de l'échange. Attention, rien n'est gratuit en ce monde, et 0-RTT présente des nouveaux dangers, et ce nouveau service a été un des plus controversés lors de la mise au point de TLS 1.3, entrainant de nombreux débats à l'IETF.
  • Désormais, la sécurité future est systématique, la compromission d'une clé secrète ne permet plus de déchiffrer les anciennes communications. Plus de clés publiques statiques, tout se fera par clés éphémères. C'était le point qui a suscité le plus de débats à l'IETF, car cela complique sérieusement la surveillance (ce qui est bien le but) et le déboguage. L'ETSI, représentante du patronat, a même normalisé son propre TLS délibérement affaibli, eTLS.
  • Plusieurs messages de négociation qui étaient auparavant en clair sont désormais chiffrés. Par contre, l'indication du nom du serveur (SNI, section 3 du RFC 6066) reste en clair et c'est l'une des principales limites de TLS en ce qui concerne la protection de la vie privée. Le problème est important, mais très difficile à résoudre (voir par exemple la proposition ESNI, Encrypted SNI.)
  • Les fonctions de dérivation de clé ont été refaites.
  • La machine à états utilisée pour l'établissement de la connexion également (elle est détaillée dans l'annexe A du RFC).
  • Les algorithmes asymétriques à courbes elliptiques font maintenant partie de la définition de base de TLS (cf. RFC 7748), et on voit arriver des nouveaux comme ed25519 (cf. RFC 8422).
  • Par contre, DSA a été retiré.
  • Le mécanisme de négociation du numéro de version (permettant à deux machines n'ayant pas le même jeu de versions TLS de se parler) a changé. L'ancien était très bien mais, mal implémenté, il a suscité beaucoup de problèmes d'interopérabilité. Le nouveau est censé mieux gérer les innombrables systèmes bogués qu'on trouve sur l'Internet (la bogue ne provenant pas tant de la bibliothèque TLS utilisée que des pare-feux mal programmés et mal configurés qui sont souvent mis devant).
  • La reprise d'une session TLS précédente fait l'objet désormais d'un seul mécanisme, qui est le même que celui pour l'usage de clés pré-partagées. La négociation TLS peut en effet être longue, en terme de latence, et ce mécanisme permet d'éviter de tout recommencer à chaque connexion. Deux machines qui se parlent régulièrement peuvent ainsi gagner du temps.

Un bon résumé de ce nouveau protocole est dans l'article de Mark Nottingham.

Ce RFC concerne TLS 1.3 mais il contient aussi quelques changements pour la version 1.2 (section 1.4 du RFC), comme un mécanisme pour limiter les attaques par repli portant sur le numéro de version, et des mécanismes de la 1.3 « portés » vers la 1.2 sous forme d'extensions TLS.

La section 2 du RFC est un survol général de TLS 1.3 (le RFC fait 147 pages, et peu de gens le liront intégralement). Au début d'une session TLS, les deux parties, avec le protocole de salutation, négocient les paramètres (version de TLS, algorithmes cryptographiques) et définissent les clés qui seront utilisées pour le chiffrement de la session. En simplifiant, il y a trois phases dans l'établissement d'une session TLS :

  • Définition des clés de session, et des paramètres cryptographiques, le client envoie un ClientHello, le serveur répond avec un ServerHello,
  • Définition des autres paramètres (par exemple l'application utilisée au-dessus de TLS, ou bien la demande CertificateRequest d'un certificat client), cette partie est chiffrée, contrairement à la précédente,
  • Authentification du serveur, avec le message Certificate (qui ne contient pas forcément un certificat, cela peut être une clé brute - RFC 7250 ou une clé d'une session précédente - RFC 7924).

Un message Finished termine cette ouverture de session. (Si vous êtes fana de futurisme, notez que seule la première étape pourrait être remplacée par la distribution quantique de clés, les autres resteraient indispensables. Contrairement à ce que promettent ses promoteurs, la QKD ne dispense pas d'utiliser les protocoles existants.)

Comment les deux parties se mettent-elles d'accord sur les clés ? Trois méthodes :

  • Diffie-Hellman sur courbes elliptiques qui sera sans doute la plus fréquente,
  • Clé pré-partagée,
  • Clé pré-partagée avec Diffie-Hellman,
  • Et la méthode RSA, elle, disparait de la norme (mais RSA peut toujours être utilisé pour l'authentification, autrement, cela ferait beaucoup de certificats à jeter…)

Si vous connaissez la cryptographie, vous savez que les PSK, les clés partagées, sont difficiles à gérer, puisque devant être transmises de manière sûre avant l'établissement de la connexion. Mais, dans TLS, une autre possibilité existe : si une session a été ouverte sans PSK, en n'utilisant que de la cryptographie asymétrique, elle peut être enregistrée, et resservir, afin d'ouvrir les futures discussions plus rapidement. TLS 1.3 utilise le même mécanisme pour des « vraies » PSK, et pour celles issues de cette reprise de sessions précédentes (contrairement aux précédentes versions de TLS, qui utilisaient un mécanisme séparé, celui du RFC 5077, désormais abandonné).

Si on a une PSK (gérée manuellement, ou bien via la reprise de session), on peut même avoir un dialogue TLS dit « 0-RTT ». Le premier paquet du client peut contenir des données, qui seront acceptées et traitées par le serveur. Cela permet une importante diminution de la latence, dont il faut rappeler qu'elle est souvent le facteur limitant des performances. Par contre, comme rien n'est idéal dans cette vallée de larmes, cela se fait au détriment de la sécurité :

  • Plus de confidentialité persistante, si la PSK est compromise plus tard, la session pourra être déchiffrée,
  • Le rejeu devient possible, et l'application doit donc savoir gérer ce problème.

La section 8 du RFC et l'annexe E.5 détaillent ces limites, et les mesures qui peuvent être prises.

Le protocole TLS est décrit avec un langage spécifique, décrit de manière relativement informelle dans la section 3 du RFC. Ce langage manipule des types de données classiques :

  • Scalaires (uint8, uint16),
  • Tableaux, de taille fixe - Datum[3] ou variable, avec indication de la longueur au début - uint16 longer<0..800>,
  • Énumérations (enum { red(3), blue(5), white(7) } Color;),
  • Enregistrements structurés, y compris avec variantes (la présence de certains champs dépendant de la valeur d'un champ).

Par exemple, tirés de la section 4 (l'annexe B fournit la liste complète), voici, dans ce langage, la liste des types de messages pendant les salutations, une énumération :

       enum {
          client_hello(1),
          server_hello(2),
          new_session_ticket(4),
          end_of_early_data(5),
          encrypted_extensions(8),
          certificate(11),
          certificate_request(13),
          certificate_verify(15),
          finished(20),
          key_update(24),
          message_hash(254),
          (255)
      } HandshakeType;
    

Et le format de base d'un message du protocole de salutation :

      struct {
          HandshakeType msg_type;    /* handshake type */
          uint24 length;             /* bytes in message */
          select (Handshake.msg_type) {
              case client_hello:          ClientHello;
              case server_hello:          ServerHello;
              case end_of_early_data:     EndOfEarlyData;
              case encrypted_extensions:  EncryptedExtensions;
              case certificate_request:   CertificateRequest;
              case certificate:           Certificate;
              case certificate_verify:    CertificateVerify;
              case finished:              Finished;
              case new_session_ticket:    NewSessionTicket;
              case key_update:            KeyUpdate;
          };
      } Handshake;     
    

La section 4 fournit tous les détails sur le protocole de salutation, notamment sur la délicate négociation des paramètres cryptographiques. Notez que la renégociation en cours de session a disparu, donc un ClientHello ne peut désormais plus être envoyé qu'au début.

Un problème auquel a toujours dû faire face TLS est celui de la négociation de version, en présence de mises en œuvre boguées, et, surtout, en présence de boitiers intermédiaires encore plus bogués (pare-feux ignorants, par exemple, que des DSI ignorantes placent un peu partout). Le modèle original de TLS pour un client était d'annoncer dans le ClientHello le plus grand numéro de version qu'on gère, et de voir dans ServerHello le maximum imposé par le serveur. Ainsi, un client TLS 1.2 parlant à un serveur qui ne gère que 1.1 envoyait ClientHello(client_version=1.2) et, en recevant ServerHello(server_version=1.1), se repliait sur TLS 1.1, la version la plus élevée que les deux parties gèraient. En pratique, cela ne marche pas aussi bien. On voyait par exemple des serveurs (ou, plus vraisemblablement, des pare-feux bogués) qui raccrochaient brutalement en présence d'un numéro de version plus élevé, au lieu de suggérer un repli. Le client n'avait alors que le choix de renoncer, ou bien de se lancer dans une série d'essais/erreurs (qui peut être longue, si le serveur ou le pare-feu bogué ne répond pas).

TLS 1.3 change donc complètement le mécanisme de négociation. Le client annonce toujours la version 1.2 (en fait 0x303, pour des raisons historiques), et la vraie version est mise dans une extension, supported_versions (section 4.2.1), dont on espère qu'elle sera ignorée par les serveurs mal gérés. (L'annexe D du RFC détaille ce problème de la négociation de version.) Dans la réponse ServerHello, un serveur 1.3 doit inclure cette extension, autrement, il faut se rabattre sur TLS 1.2.

En parlant d'extensions, concept qui avait été introduit originellement dans le RFC 4366, notre RFC reprend des extensions déjà normalisées, comme le SNI (Server Name Indication) du RFC 6066, le battement de cœur du RFC 6520, le remplissage du ClientHello du RFC 7685, et en ajoute dix, dont supported_versions. Certaines de ces extensions doivent être présentes dans les messages Hello, car la sélection des paramètres cryptographiques en dépend, d'autres peuvent être uniquement dans les messages EncryptedExtensions, une nouveauté de TLS 1.3, pour les extensions qu'on n'enverra qu'une fois le chiffrement commencé. Le RFC en profite pour rappeler que les messages Hello ne sont pas protégés cryptographiquement, et peuvent donc être modifiés (le message Finished résume les décisions prises et peut donc protéger contre ce genre d'attaques).

Autrement, parmi les autres nouvelles extensions :

  • Le petit gâteau (cookie), pour tester la joignabilité,
  • Les données précoces (early data), extension qui permet d'envoyer des données dès le premier message (« O-RTT »), réduisant ainsi la latence, un peu comme le fait le TCP Fast Open du RFC 7413,
  • Liste des AC (certificate authorities), qui, en indiquant la liste des AC connues du client, peut aider le serveur à choisir un certificat qui sera validé (par exemple en n'envoyant le certificat CAcert que si le client connait cette AC).

La section 5 décrit le protocole des enregistrements (record protocol). C'est ce sous-protocole qui va prendre un flux d'octets, le découper en enregistrements, les protéger par le chiffrement puis, à l'autre bout, déchiffrer et reconstituer le flux… Notez que « protégé » signifie à la fois confidentialité et intégrité puisque TLS 1.3, contrairement à ses prédécesseurs, impose AEAD (RFC 5116).

Les enregistrements sont typés et marqués handshake (la salutation, vue dans la section précédente), change cipher spec, alert (pour signaler un problème) et application data (les données elle-mêmes) :

enum {
          invalid(0),
          change_cipher_spec(20),
          alert(21),
          handshake(22),
          application_data(23),
          (255)
      } ContentType;
    

Le contenu des données est évidemment incompréhensible, en raison du chiffrement (voici un enregistrement de type 23, données, vu par tshark) :

    TLSv1.3 Record Layer: Application Data Protocol: http-over-tls
        Opaque Type: Application Data (23)
        Version: TLS 1.2 (0x0303)
        Length: 6316
        Encrypted Application Data: eb0e21f124f82eee0b7a37a1d6d866b075d0476e6f00cae7...
    

Et décrite par la norme dans son langage formel :

struct {
          ContentType opaque_type = application_data; /* 23 */
          ProtocolVersion legacy_record_version = 0x0303; /* TLS v1.2 */
          uint16 length;
          opaque encrypted_record[TLSCiphertext.length];
      } TLSCiphertext;
    

(Oui, le numéro de version reste à TLS 1.2 pour éviter d'énerver les stupides middleboxes.) Notez que des extensions à TLS peuvent introduire d'autres types d'enregistrements.

Une faiblesse classique de TLS est que la taille des données chiffrées n'est pas dissimulée. Si on veut savoir à quelle page d'un site Web un client HTTP a accédé, on peut parfois le déduire de l'observation de cette taille. D'où la possibilité de faire du remplissage pour dissimuler cette taille (section 5.4 du RFC). Notez que le RFC ne suggère pas de politique de remplissage spécifique (ajouter un nombre aléatoire ? Tout remplir jusqu'à la taille maximale ?), c'est un choix compliqué. Il note aussi que certaines applications font leur propre remplissage, et qu'il n'est alors pas nécessaire que TLS le fasse.

La section 6 du RFC est dédiée au cas des alertes. C'est un des types d'enregistrements possibles, et, comme les autres, il est chiffré, et les alertes sont donc confidentielles. Une alerte a un niveau et une description :

 struct {
          AlertLevel level;
          AlertDescription description;
      } Alert;
    

Le niveau indiquait si l'alerte est fatale mais n'est plus utilisé en TLS 1.2, où il faut se fier uniquement à la description, une énumération des problèmes possibles (message de type inconnu, mauvais certificat, enregistrement non décodable - rappelez-vous que TLS 1.3 n'utilise que du chiffrement intègre, problème interne au client ou au serveur, extension non acceptée, etc). La section 6.2 donne une liste des erreurs fatales, qui doivent mener à terminer immédiatement la session TLS.

La section 8 du RFC est entièrement consacrée à une nouveauté délicate, le « 0-RTT ». Ce terme désigne la possibilité d'envoyer des données dès le premier paquet, sans les nombreux échanges de paquets qui sont normalement nécessaires pour établir une session TLS. C'est très bien du point de vue des performances, mais pas forcément du point de vue de la sécurité puisque, sans échanges, on ne peut plus vérifier à qui on parle. Un attaquant peut réaliser une attaque par rejeu en envoyant à nouveau un paquet qu'il a intercepté. Un serveur doit donc se défendre en se souvenant des données déjà envoyées et en ne les acceptant pas deux fois. (Ce qui peut être plus facile à dire qu'à faire ; le RFC contient une bonne discussion très détaillée des techniques possibles, et de leurs limites. Il y en a des subtiles, comme d'utiliser des systèmes de mémorisation ayant des faux positifs, comme les filtres de Bloom, parce qu'ils ne produiraient pas d'erreurs, ils rejetteraient juste certains essais 0-RTT légitimes, cela ne serait donc qu'une légère perte de performance.)

La section 9 de notre RFC se penche sur un problème difficile, la conformité des mises en œuvres de TLS. D'abord, les algorithmes obligatoires. Afin de permettre l'interopérabilité, toute mise en œuvre de TLS doit avoir la suite de chiffrement TLS_AES_128_GCM_SHA256 (AES en mode GCM avec SHA-256). D'autres suites sont recommandées (cf. annexe B.4). Pour l'authentification, RSA avec SHA-256 et ECDSA sont obligatoires. Ainsi, deux programmes différents sont sûrs de pouvoir trouver des algorithmes communs. La possibilité d'authentification par certificats PGP du RFC 6091 a été retirée.

De plus, certaines extensions à TLS sont obligatoires, un pair TLS 1.3 ne peut pas les refuser :

  • supported_versions, nécessaire pour annoncer TLS 1.3,
  • cookie,
  • signature_algorithms, signature_algorithms_cert, supported_groups et key_share,
  • server_name, c'est à dire SNI (Server Name Indication), souvent nécessaire pour pouvoir choisir le bon certificat (cf. section 3 du RFC 6066).

La section 9 précise aussi le comportement attendu des équipements intermédiaires. Ces dispositifs (pare-feux, par exemple, mais pas uniquement) ont toujours été une plaie pour TLS. Alors que TLS vise à fournir une communication sûre, à l'abri des équipements intermédiaires, ceux-ci passent leur temps à essayer de s'insérer dans la communication, et souvent la cassent. Normalement, TLS 1.3 est conçu pour que ces interférences ne puissent pas mener à un repli (le repli est l'utilisation de paramètres moins sûrs que ce que les deux machines auraient choisi en l'absence d'interférence).

Il y a deux grandes catégories d'intermédiaires, ceux qui tripotent la session TLS sans être le client ou le serveur, et ceux qui terminent la session TLS de leur côté. Attention, dans ce contexte, « terminer » ne veut pas dire « y mettre fin », mais « la sécurité TLS se termine ici, de manière à ce que l'intermédiaire puisse accéder au contenu de la communication ». Typiquement, une middlebox qui « termine » une session TLS va être serveur TLS pour le client et client TLS pour le serveur, s'insérant complètement dans la conversation. Normalement, l'authentification vise à empêcher ce genre de pratiques, et l'intermédiaire ne sera donc accepté que s'il a un certificat valable. C'est pour cela qu'en entreprise, les machines officielles sont souvent installées avec une AC contrôlée par le vendeur du boitier intermédiaire, de manière à permettre l'interception.

Le RFC ne se penche pas sur la légitimité de ces pratiques, uniquement sur leurs caractéristiques techniques. (Les boitiers intermédiaires sont souvent programmés avec les pieds, et ouvrent de nombreuses failles.) Le RFC rappelle notamment que l'intermédiaire qui termine une session doit suivre le RFC à la lettre (ce qui devrait aller sans dire…)

Depuis le RFC 4346, il existe plusieurs registres IANA pour TLS, décrits en section 11, avec leurs nouveautés. En effet, plusieurs choix pour TLS ne sont pas « câblés en dur » dans le RFC mais peuvent évoluer indépendamment. Par exemple, le registre de suites cryptographiques a une politique d'enregistrement « spécification nécessaire » (cf. RFC 8126, sur les politiques d'enregistrement). La cryptographie fait régulièrement des progrès, et il faut donc pouvoir modifier la liste des suites acceptées (par exemple lorsqu'il faudra y ajouter les algorithmes post-quantiques) sans avoir à toucher au RFC (l'annexe B.4 donne la liste actuelle). Le registre des types de contenu, lui, a une politique d'enregistrement bien plus stricte, « action de normalisation ». On crée moins souvent des types que des suites cryptographiques. Même chose pour le registre des alertes ou pour celui des salutations.

L'annexe C du RFC plaira aux programmeurs, elle donne plusieurs conseils pour une mise en œuvre correcte de TLS 1.3 (ce n'est pas tout d'avoir un protocole correct, il faut encore qu'il soit programmé correctement). Pour aider les développeurs à déterminer s'ils ont correctement fait le travail, un futur RFC fournira des vecteurs de test.

Un des conseils les plus importants est évidemment de faire attention au générateur de nombres aléatoires, source de tant de failles de sécurité en cryptographie. TLS utilise des nombres qui doivent être imprévisibles à un attaquant pour générer des clés de session. Si ces nombres sont prévisibles, toute la cryptographie s'effondre. Le RFC conseille fortement d'utiliser un générateur existant (comme /dev/urandom sur les systèmes Unix) plutôt que d'écrire le sien, ce qui est bien plus difficile qu'il ne semble. (Si on tient quand même à le faire, le RFC 4086 est une lecture indispensable.)

Le RFC conseille également de vérifier le certificat du partenaire par défaut (quitte à fournir un moyen de débrayer cette vérification). Si ce n'est pas le cas, beaucoup d'utilisateurs du programme ou de la bibliothèque oublieront de le faire. Il suggère aussi de ne pas accepter certains certificats trop faibles (clé RSA de seulement 1 024 bits, par exemple).

Il existe plusieurs moyens avec TLS de ne pas avoir d'authentification du serveur : les clés brutes du RFC 7250 (à la place des certificats), ou bien les certificats auto-signés. Dans ces conditions, une attaque de l'homme du milieu est parfaitement possibe, et il faut donc prendre des précautions supplémentaires (par exemple DANE, normalisé dans le RFC 6698, que le RFC oublie malheureusement de citer).

Autre bon conseil de cryptographie, se méfier des attaques fondées sur la mesure du temps de calcul, et prendre des mesures appropriées (par exemple en vérifiant que le temps de calcul est le même pour des données correctes et incorrectes).

Il n'y a aucune bonne raison d'utiliser certains algorithmes faibles (comme RC4, abandonné depuis le RFC 7465), et le RFC demande que le code pour ces algorithmes ne soit pas présent, afin d'éviter une attaque par repli (annexes C.3 et D.5 du RFC). De la même façon, il demande de ne jamais accepter SSL v3 (RFC 7568).

L'expérience a prouvé que beaucoup de mises en œuvre de TLS ne réagissaient pas correctement à des options inattendues, et le RFC rappelle donc qu'il faut ignorer les suites cryptographiques inconnues (autrement, on ne pourrait jamais introduire une nouvelle suite, puisqu'elle casserait les programmes), et ignorer les extensions inconnues (pour la même raison).

L'annexe D, elle, est consacrée au problème de la communication avec un vieux partenaire, qui ne connait pas TLS 1.3. Le mécanisme de négociation de la version du protocole à utiliser a complètement changé en 1.3. Dans la 1.3, le champ version du ClientHello contient 1.2, la vraie version étant dans l'extension supported_versions. Si un client 1.3 parle avec un serveur <= 1.2, le serveur ne connaitra pas cette extension et répondra sans l'extension, avertissant ainsi le client qu'il faudra parler en 1.2 (ou plus vieux). Ça, c'est si le serveur est correct. S'il ne l'est pas ou, plus vraisemblablement, s'il est derrière une middlebox boguée, on verra des problèmes comme par exemple le refus de répondre aux clients utilisant des extensions inconnues (ce qui sera le cas pour supported_versions), soit en rejettant ouvertement la demande soit, encore pire, en l'ignorant. Arriver à gérer des serveurs/middleboxes incorrects est un problème complexe. Le client peut être tenté de re-essayer avec d'autres options (par exemple tenter du 1.2, sans l'extension supported_versions). Cette méthode n'est pas conseillée. Non seulement elle peut prendre du temps (attendre l'expiration du délai de garde, re-essayer…) mais surtout, elle ouvre la voie à des attaques par repli : l'attaquant bloque les ClientHello 1.3 et le client, croyant bien faire, se replie sur une version plus ancienne et sans doute moins sûre de TLS.

En parlant de compatibilité, le « 0-RTT » n'est évidemment pas compatible avec les vieilles versions. Le client qui envoie du « 0-RTT » (des données dans le ClientHello) doit donc savoir que, si la réponse est d'un serveur <= 1.2, la session ne pourra pas être établie, et il faudra donc réessayer sans 0-RTT.

Naturellement, les plus gros problèmes ne surviennent pas avec les clients et les serveurs mais avec les middleboxes. Plusieurs études ont montré leur caractère néfaste (cf. présentation à l'IETF 100, mesures avec Chrome (qui indique également que certains serveurs TLS sont gravement en tort, comme celui installé dans les imprimantes Canon), mesures avec Firefox, et encore d'autres mesures). Le RFC suggère qu'on limite les risques en essayant d'imiter le plus possible une salutation de TLS 1.2, par exemple en envoyant des messages change_cipher_spec, qui ne sont plus utilisés en TLS 1.3, mais qui peuvent rassurer la middlebox (annexe D.4).

Enfin, le RFC se termine par l'annexe E, qui énumère les propriétés de sécurité de TLS 1.3 : même face à un attaquant actif (RFC 3552), le protocole de salutation de TLS garantit des clés de session communes et secrètes, une authentification du serveur (et du client si on veut), et une sécurité persistante, même en cas de compromission ultérieure des clés (sauf en cas de 0-RTT, un autre des inconvénients sérieux de ce service, avec le risque de rejeu). De nombreuses analyses détaillées de la sécurité de TLS sont listées dans l'annexe E.1.6. À lire si vous voulez travailler ce sujet.

Quant au protocole des enregistrements, celui de TLS 1.3 garantit confidentialité et intégrité (RFC 5116).

TLS 1.3 a fait l'objet de nombreuses analyses de sécurité par des chercheurs, avant même sa normalisation, ce qui est une bonne chose (et qui explique en partie les retards). Notre annexe E pointe également les limites restantes de TLS :

  • Il est vulnérable à l'analyse de trafic. TLS n'essaie pas de cacher la taille des paquets, ni l'intervalle de temps entre eux. Ainsi, si un client accède en HTTPS à un site Web servant quelques dizaines de pages aux tailles bien différentes, il est facile de savoir quelle page a été demandée, juste en observant les tailles. (Voir « I Know Why You Went to the Clinic: Risks and Realization of HTTPS Traffic Analysis », de Miller, B., Huang, L., Joseph, A., et J. Tygar et « HTTPS traffic analysis and client identification using passive SSL/TLS fingerprinting », de Husak, M., Čermak, M., Jirsik, T., et P. Čeleda). TLS fournit un mécanisme de remplissage avec des données bidon, permettant aux applications de brouiller les pistes. Certaines applications utilisant TLS ont également leur propre remplissage (par exemple, pour le DNS, c'est le RFC 7858). De même, une mise en œuvre de TLS peut retarder les paquets pour rendre l'analyse des intervalles plus difficile. On voit que dans les deux cas, taille des paquets et intervalle entre eux, résoudre le problème fait perdre en performance (c'est pour cela que ce n'est pas intégré par défaut).
  • TLS peut être également vulnérable à des attaques par canal auxiliaire. Par exemple, la durée des opérations cryptographiques peut être observée, ce qui peut donner des informations sur les clés. TLS fournit quand même quelques défenses : l'AEAD facilite la mise en œuvre de calculs en temps constant, et format uniforme pour toutes les erreurs, empêchant un attaquant de trouver quelle erreur a été déclenchée.

Le 0-RTT introduit un nouveau risque, celui de rejeu. (Et 0-RTT a sérieusement contribué aux délais qu'à connu le projet TLS 1.3, plusieurs participants à l'IETF protestant contre cette introduction risquée.) Si l'application est idempotente, ce n'est pas très grave. Si, par contre, les effets d'une requête précédentes peuvent être rejoués, c'est plus embêtant (imaginez un transfert d'argent répété…) TLS ne promet rien en ce domaine, c'est à chaque serveur de se défendre contre le rejeu (la section 8 donne des idées à ce sujet). Voilà pourquoi le RFC demande que les requêtes 0-RTT ne soient pas activées par défaut, mais uniquement quand l'application au-dessus de TLS le demande. (Cloudflare, par exemple, n'active pas le 0-RTT par défaut.)

Voilà, vous avez maintenant fait un tour complet du RFC, mais vous savez que la cryptographie est une chose difficile, et pas seulement dans les algorithmes cryptographiques (TLS n'en invente aucun, il réutilise des algorithmes existants comme AES ou ECDSA), mais aussi dans les protocoles cryptographiques, un art complexe. N'hésitez donc pas à lire le RFC en détail, et à vous méfier des résumés forcément toujours sommaires, comme cet article.

À part le 0-RTT, le plus gros débat lors de la création de TLS 1.3 avait été autour du concept que ses partisans nomment « visibilité » et ses adversaires « surveillance ». C'est l'idée qu'il serait bien pratique si on (on : le patron, la police, le FAI…) pouvait accéder au contenu des communications TLS. « Le chiffrement, c'est bien, à condition que je puisse lire les données quand même » est l'avis des partisans de la visibilité. Cela avait été proposé dans les Internet-Drafts draft-green-tls-static-dh-in-tls13 et draft-rhrd-tls-tls13-visibility. Je ne vais pas ici pouvoir capturer la totalité du débat, juste noter quelques points qui sont parfois oubliés dans la discussion. Côté partisans de la visibilité :

  • Dans une entreprise capitaliste, il n'y pas de citoyens, juste un patron et des employés. Les ordinateurs appartiennent au patron, et les employés n'ont pas leur mot à dire. Le patron peut donc décider d'accéder au contenu des communications chiffrées.
  • Il existe des règles (par exemple PCI-DSS dans le secteur financier ou HIPAA dans celui de la santé) qui requièrent de certaines entreprises qu'elles sachent en détail tout ce qui circule sur le réseau. Le moyen le plus simple de le faire est de surveiller le contenu des communications, même chiffrées. (Je ne dis pas que ces règles sont intelligentes, juste qu'elles existent. Notons par exemple que les mêmes règles imposent d'utiliser du chiffrement fort, sans faille connue, ce qui est contradictoire.)
  • Enregistrer le trafic depuis les terminaux est compliqué en pratique : applications qui n'ont pas de mécanisme de journalisation du trafic, systèmes d'exploitation fermés, boîtes noires…
  • TLS 1.3 risque de ne pas être déployé dans les entreprises qui tiennent à surveiller le trafic, et pourrait même être interdit dans certains pays, où la surveillance passe avant les droits humains.

Et du côté des adversaires de la surveillance :

  • La cryptographie, c'est compliqué et risqué. TLS 1.3 est déjà assez compliqué comme cela. Lui ajouter des fonctions (surtout des fonctions délibérement conçues pour affaiblir ses propriétés de sécurité) risque fort d'ajouter des failles de sécurité. D'autant plus que TLS 1.3 a fait l'objet de nombreuses analyses de sécurité avant son déploiement, et qu'il faudrait tout recommencer.
  • Contrairement à ce que semblent croire les partisans de la « visibilité », il n'y a pas que HTTPS qui utilise TLS. Ils ne décrivent jamais comment leur proposition marcherait avec des protocoles autres que HTTPS.
  • Pour HTTPS, et pour certains autres protocoles, une solution simple, si on tient absolument à intercepter tout le trafic, est d'avoir un relais explicite, configuré dans les applications, et combiné avec un blocage dans le pare-feu des connexions TLS directes. Les partisans de la visibilté ne sont en général pas enthousiastes pour cette solution car ils voudraient faire de la surveillance furtive, sans qu'elle se voit dans les applications utilisées par les employés ou les citoyens.
  • Les partisans de la « visibilité » disent en général que l'interception TLS serait uniquement à l'intérieur de l'entreprise, pas pour l'Internet public. Mais, dans ce cas, tous les terminaux sont propriété de l'entreprise et contrôlés par elle, donc elle peut les configurer pour copier tous les messages échangés. Et, si certains de ces terminaux sont des boîtes noires, non configurables et dont on ne sait pas bien ce qu'ils font, eh bien, dans ce cas, on se demande pourquoi des gens qui insistent sur leurs obligations de surveillance mettent sur leur réseau des machines aussi incontrôlables.
  • Dans ce dernier cas (surveillance uniquement au sein d'une entreprise), le problème est interne à l'entreprise, et ce n'est donc pas à l'IETF, organisme qui fait des normes pour l'Internet, de le résoudre. Après tout, rien n'empêche ces entreprises de garder TLS 1.2.

Revenons maintenant aux choses sérieuses, avec les mises en œuvre de TLS 1.3. Il y en existe au moins une dizaine à l'heure actuelle mais, en général, pas dans les versions officiellement publiées des logiciels. Notons quand même que Firefox 61 sait faire du TLS 1.3. Les autres mises en œuvre sont prêtes, même si pas forcément publiées. Prenons l'exemple de la bibliothèque GnuTLS. Elle dispose de TLS 1.3 depuis la version 3.6.3. Pour l'instant, il faut compiler cette version avec l'option ./configure --enable-tls13-support, qui n'est pas encore activée par défaut. Un bon article du mainteneur de GnuTLS explique bien les nouveautés de TLS 1.3.

Une fois GnuTLS correctement compilé, on peut utiliser le programme en ligne de commande gnutls-cli avec un serveur qui accepte TLS 1.3 :

% gnutls-cli  gmail.com 
...
- Description: (TLS1.3)-(ECDHE-X25519)-(RSA-PSS-RSAE-SHA256)-(AES-256-GCM)
- Ephemeral EC Diffie-Hellman parameters
 - Using curve: X25519
 - Curve size: 256 bits
- Version: TLS1.3
- Key Exchange: ECDHE-RSA
- Server Signature: RSA-PSS-RSAE-SHA256
- Cipher: AES-256-GCM
- MAC: AEAD
...

Et ça marche, on fait du TLS 1.3. Si vous préférez écrire le programme vous-même, regardez ce petit programme. Si GnuTLS est en /local, il se compilera avec cc -I/local/include -Wall -Wextra -o test-tls13 test-tls13.c -L/local/lib -lgnutls et s'utilisera avec :

% ./test-tls13 www.ietf.org      
TLS connection using "TLS1.3 AES-256-GCM"

%  ./test-tls13 gmail.com  
TLS connection using "TLS1.3 AES-256-GCM"

%  ./test-tls13 mastodon.gougere.fr
TLS connection using "TLS1.2 AES-256-GCM"

% ./test-tls13 www.bortzmeyer.org
TLS connection using "TLS1.2 AES-256-GCM"

% ./test-tls13 blog.cloudflare.com
TLS connection using "TLS1.3 AES-256-GCM"
  

Cela vous donne une petite idée des serveurs qui acceptent TLS 1.3.

Un pcap d'une session TLS 1.3 est disponible en tls13.pcap. Notez que le numéro de version n'est pas encore celui du RFC (0x304). Ici, 0x7f1c désigne l'Internet-Draft numéro 28. Voici la session vue par tshark :

    1   0.000000 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TCP 94 36866 → https(443) [SYN] Seq=0 Win=28800 Len=0 MSS=1440 SACK_PERM=1 TSval=3528788861 TSecr=0 WS=128
    2   0.003052 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TCP 86 https(443) → 36866 [SYN, ACK] Seq=0 Ack=1 Win=24400 Len=0 MSS=1220 SACK_PERM=1 WS=1024
    3   0.003070 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TCP 74 36866 → https(443) [ACK] Seq=1 Ack=1 Win=28800 Len=0
    4   0.003354 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TLSv1 403 Client Hello
    5   0.006777 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TCP 74 https(443) → 36866 [ACK] Seq=1 Ack=330 Win=25600 Len=0
    6   0.011393 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TLSv1.3 6496 Server Hello, Change Cipher Spec, Application Data
    7   0.011413 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TCP 74 36866 → https(443) [ACK] Seq=330 Ack=6423 Win=41728 Len=0
    8   0.011650 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TLSv1.3 80 Change Cipher Spec
    9   0.012685 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TLSv1.3 148 Application Data
   10   0.015693 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TCP 74 https(443) → 36866 [ACK] Seq=6423 Ack=411 Win=25600 Len=0
   11   0.015742 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TLSv1.3 524 Application Data
   12   0.015770 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TCP 74 36866 → https(443) [RST] Seq=411 Win=0 Len=0
   13   0.015788 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TCP 74 https(443) → 36866 [FIN, ACK] Seq=6873 Ack=411 Win=25600 Len=0
   14   0.015793 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TCP 74 36866 → https(443) [RST] Seq=411 Win=0 Len=0

Et, complètement décodée par tshark :

Secure Sockets Layer [sic]
    TLSv1 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Handshake Protocol: Client Hello
            Handshake Type: Client Hello (1)
            Version: TLS 1.2 (0x0303)
...
            Extension: supported_versions (len=9)
                Type: supported_versions (43)
                Length: 9
                Supported Versions length: 8
                Supported Version: Unknown (0x7f1c)
                Supported Version: TLS 1.2 (0x0303)
                Supported Version: TLS 1.1 (0x0302)
                Supported Version: TLS 1.0 (0x0301)

Le texte complet est en tls13.txt. Notez bien que la négociation est en clair.

Quelques autres articles à lire :


Téléchargez le RFC 8446


L'article seul

RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier

Date de publication du RFC : Août 2018
Auteur(s) du RFC : Y. Nir (Check Point), S. Josefsson (SJD AB), M. Pegourie-Gonnard (Independent / PolarSSL)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 7 août 2018


Ce RFC décrit les algorithmes cryptographiques à base de courbes elliptiques utilisés dans TLS. Il remplace le RFC 4492.

Plus exactement, il normalise les algorithmes utilisés dans les versions de TLS allant jusqu'à 1.2 incluse. L'utilisation des courbes elliptiques par TLS 1.3 est décrite dans le RFC sur TLS 1.3, le RFC 8446. Les deux points importants de ce nouveau RFC sont :

  • L'utilisation de courbes elliptiques pour un échange Diffie-Hellman de la clé de session TLS (ce qu'on nomme ECDHE),
  • Les algorithmes de signature à courbes elliptiques ECDSA et EdDSA pour authentifier le pair TLS.

Commençons par l'échange de clés de session (section 2). TLS nécessite que les deux pairs se mettent d'accord sur une clé de chiffrement symétrique qui sera ensuite utilisée pendant toute la session, avec des algorithmes comme AES. Une des façons de synchroniser cette clé de session est qu'un des pairs la génère aléatoirement, puis la chiffre avec la clé publique (chiffrement asymétrique) de son pair avant de lui transmettre (cela marche avec RSA mais je n'ai pas l'impression qu'il y ait un moyen normalisé de faire cela avec les courbes elliptiques). Une autre façon est d'utiliser un échange Diffie-Hellman. Contrairement à l'échange Diffie-Hellman originel, celui présenté dans ce RFC, ECDHE, utilise la cryptographie sur courbes elliptiques. (J'ai simplifié pas mal : par exemple, l'échange ECDHE ne donnera pas directement la clé de session, celle-ci sera en fait dérivée de la clé obtenue en ECDHE.) Le principal avantage de Diffie-Hellman est de fournir de la sécurité même en cas de compromission ultérieure de la clé privée.

Notre RFC présente trois variantes d'ECDHE, selon la manière dont l'échange est authentifié, l'une utilisant ECDSA ou EdDSA, l'autre utilisant le traditionnel RSA, et la troisième n'authentifiant pas du tout, et étant donc vulnérable aux attaques de l'Homme du Milieu, sauf si une authentification a lieu en dehors de TLS. (Attention, dans le cas de ECDHE_RSA, RSA n'est utilisé que pour authentifier l'échange, la génération de la clé se fait bien en utilisant les courbes elliptiques.)

Lorsque l'échange est authentifié (ECDHE_ECDSA - qui, en dépit de son nom, inclut EdDSA - ou bien ECDHE_RSA), les paramètres ECDH (Diffie-Hellman avec courbes elliptiques) sont signés par la clé privée (ECDSA, EdDSA ou RSA). S'il n'est pas authentifié (ECDH_anon, mais notez que le nom est trompeur, bien que le E final - ephemeral - manque, la clé est éphémère), on n'envoie évidemment pas de certificat, ou de demande de certificat.

Voilà, avec cette section 2, on a pu générer une clé de session avec Diffie-Hellman, tout en authentifiant le serveur avec des courbes elliptiques. Et pour l'authentification du client ? C'est la section 3 de notre RFC. Elle décrit un mécanisme ECDSA_sign (là encore, en dépit du nom du mécanisme, il fonctionne aussi bien pour EdDSA), où le client s'authentifie en signant ses messages avec un algorithme à courbes elliptiques.

Les courbes elliptiques ont quelques particularités qui justifient deux extensions à TLS que présente la section 4 du RFC. Il y a Supported Elliptic Curves Extension et Supported Point Formats Extension, qui permettent de décrire les caractéristiques de la courbe elliptique utilisée (on verra plus loin que la deuxième extension ne sert plus guère). Voici, vue par tshark, l'utilisation de ces extensions dans un ClientHello TLS envoyé par OpenSSL :

Extension: elliptic_curves
                Type: elliptic_curves (0x000a)
                Length: 28
                Elliptic Curves Length: 26
                Elliptic curves (13 curves)
                    Elliptic curve: secp256r1 (0x0017)
                    Elliptic curve: secp521r1 (0x0019)
                    Elliptic curve: brainpoolP512r1 (0x001c)
                    Elliptic curve: brainpoolP384r1 (0x001b)
                    Elliptic curve: secp384r1 (0x0018)
                    Elliptic curve: brainpoolP256r1 (0x001a)
                    Elliptic curve: secp256k1 (0x0016)
...
Extension: ec_point_formats
                Type: ec_point_formats (0x000b)
                Length: 4
                EC point formats Length: 3
                Elliptic curves point formats (3)
                    EC point format: uncompressed (0)
                    EC point format: ansiX962_compressed_prime (1)
                    EC point format: ansiX962_compressed_char2 (2)

La section 5 du RFC donne les détails concrets. Par exemple, les deux extensions citées plus haut s'écrivent, dans le langage de TLS (cf. section 4 du RFC 5246) :

enum {
          elliptic_curves(10),
          ec_point_formats(11)
} ExtensionType;

La première extension permet d'indiquer les courbes utilisées. Avec celles du RFC 7748, cela donne, comme possibilités :

enum {
               deprecated(1..22),
               secp256r1 (23), secp384r1 (24), secp521r1 (25),
               x25519(29), x448(30),
               reserved (0xFE00..0xFEFF),
               deprecated(0xFF01..0xFF02),
               (0xFFFF)
} NamedCurve;  

secp256r1 est la courbe P-256 du NIST, x25519 est la Curve-25519 de Bernstein. Notez que beaucoup des courbes de l'ancien RFC 4492, jamais très utilisées, ont été abandonnées. (Les courbes se trouvent dans un registre IANA.)

Normalement, dans TLS, on peut choisir séparément l'algorithme de signature et celui de condensation (cf. section 7.4.1.4.1 du RFC 5246). Avec certains algorithmes comme EdDSA dans sa forme « pure », il n'y a pas de condensation séparée et un « algorithme » bidon, Intrinsic (valeur 8) a été créé pour mettre dans le champ « algorithme de condensation » de l'extension signature_algorithms.

Voici une négociation TLS complète, vue par curl :

%  curl -v https://www.nextinpact.com
...
* Connected to www.nextinpact.com (2400:cb00:2048:1::6819:f815) port 443 (#0)
...
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
...
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; ST=CA; L=San Francisco; O=CloudFlare, Inc.; CN=nextinpact.com
...
> GET / HTTP/1.1
> Host: www.nextinpact.com
> User-Agent: curl/7.52.1
> Accept: */*

On voit que l'algorithme utilisé par TLS est ECDHE-ECDSA-AES128-GCM-SHA256, ce qui indique ECDHE avec ECDSA. Le certificat du serveur doit donc inclure une clé « courbe elliptique ». Regardons ledit certificat :

% openssl s_client -connect www.nextinpact.com:443 -showcerts | openssl x509 -text
Certificate:
...
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO ECC Domain Validation Secure Server CA 2
...
        Subject: OU = Domain Control Validated, OU = PositiveSSL Multi-Domain, CN = ssl378410.cloudflaressl.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
...
                ASN1 OID: prime256v1
                NIST CURVE: P-256
...
            X509v3 Subject Alternative Name: 
                DNS:ssl378410.cloudflaressl.com, DNS:*.baseballwarehouse.com, DNS:*.campusgroups.com, DNS:*.cretedoc.gr, DNS:*.groupment.com, DNS:*.icstage.com, DNS:*.ideacouture.com, DNS:*.industrialtour-deloitte.com, DNS:*.jonessnowboards.com, DNS:*.nextinpact.com, DNS:*.pcinpact.com, DNS:*.pinkapple.com, DNS:*.softballrampage.com, DNS:*.undercovercondoms.co.uk, DNS:baseballwarehouse.com, DNS:campusgroups.com, DNS:cretedoc.gr, DNS:groupment.com, DNS:icstage.com, DNS:ideacouture.com, DNS:industrialtour-deloitte.com, DNS:jonessnowboards.com, DNS:nextinpact.com, DNS:pcinpact.com, DNS:pinkapple.com, DNS:softballrampage.com, DNS:undercovercondoms.co.uk
    Signature Algorithm: ecdsa-with-SHA256

On a bien une clé sur la courbe P-256.

Quel est l'état des mises en œuvre de ces algorithmes dans les bibliothèques TLS existantes ? ECDHE et ECDSA avec les courbes NIST sont très répandus. ECDHE avec la courbe Curve25519 est également dans plusieurs bibliothèques TLS. Par contre, EdDSA, ou ECDHE avec la courbe Curve448, sont certes implémentés mais pas encore largement déployés.

Les changements depuis le RFC 4492 sont résumés dans l'annexe B. Souvent, une norme récente ajoute beaucoup de choses par rapport à l'ancienne mais, ici, pas mal de chose ont au contraire été retirées :

  • Plusieurs courbes peu utilisées disparaissent,
  • Il n'y a plus qu'un seul format de point accepté (uncompressed),
  • Des algorithmes comme toute la famille ECDH_ECDSA (ECDH et pas ECDHE, donc ceux dont la clé n'est pas éphémère) sont retirés.

Parmi les ajouts, le plus important est évidemment l'intégration des « courbes Bernstein », Curve25519 et Curve448, introduites par le RFC 7748. Et il y avait également plusieurs erreurs techniques dans le RFC 4492, qui sont corrigées par notre nouveau RFC.

Et, pendant que j'y suis, si vous voulez générer un certificat avec les courbes elliptiques, voici comment faire avec OpenSSL :

% openssl ecparam -out ec_key.pem -name prime256v1 -genkey
% openssl req -new -key ec_key.pem  -nodes -days 1000 -out cert.csr
  

J'ai utilisé ici la courbe P-256 (prime256v1 est encore un autre identificateur pour la courbe NIST P-256, chaque organisme qui normalise dans ce domaine ayant ses propres identificateurs). Si vous voulez la liste des courbes que connait OpenSSL :

% openssl ecparam -list_curves 

Ce blog est accessible en TLS mais pas avec des courbes elliptiques. En effet, l'AC que j'utilise, CAcert, ne les accepte hélas pas (« The keys you supplied use an unrecognized algorithm. For security reasons these keys can not be signed by CAcert. ») Il y a des raisons pour cela mais c'est quand même déplorable. (Enfin, j'accepte quand même ECDHE.)

Enfin, un échange TLS complet vu par tshark est visible ici.

Merci à Manuel Pégourié-Gonnard pour sa relecture vigilante.


Téléchargez le RFC 8422


L'article seul

RFC 8399: Internationalization Updates to RFC 5280

Date de publication du RFC : Mai 2018
Auteur(s) du RFC : R. Housley (Vigil Security)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF lamps
Première rédaction de cet article le 30 juillet 2018


Ce court RFC ajoute aux certificats PKIX du RFC 5280 la possibilité de contenir des adresses de courrier électronique dont la partie locale est en Unicode. Et il modifie légèrement les règles pour les noms de domaine en Unicode dans les certificats.

Les certificats sur l'Internet sont normalisés dans le RFC 5280, qui décrit un profil de X.509 nommé PKIX (définir un profil était nécessaire car la norme X.509 est bien trop riche et complexe). Ce RFC 5280 permettait des noms de domaine en Unicode (sections 4.2.1.10 et 7 du RFC 5280) mais il suivait l'ancienne norme IDN, celle des RFC 3490 et suivants. Depuis, les IDN sont normalisés dans le RFC 5890 et suivants, et notre nouveau RFC 8399 modifie très légèrement le RFC 5280 pour s'adapter à cette nouvelle norme de noms de domaines en Unicode. Les noms de domaine dans un certificat peuvent être présents dans les champs Sujet (titulaire du certificat) et Émetteur (AC ayant signé le certificat) mais aussi dans les contraintes sur le nom (une autorité de certification peut être limitée à des noms se terminant en example.com, par exemple).

Notez que, comme avant, ces noms sont exprimés dans le certificat en Punycode (RFC 3492, xn--caf-dma.fr au lieu de café.fr). C'est un bon exemple du fait que les limites qui rendaient difficiles d'utiliser des noms de domaine en Unicode n'avaient rien à voir avec le DNS (qui n'a jamais été limité à ASCII, contrairement à ce qu'affirme une légende courante). En fait, le problème venait des applications (comme PKIX), qui ne s'attendaient pas à des noms en Unicode. Un logiciel qui traite des certificats aurait été bien étonné de voir des noms de domaines non-ASCII, et aurait peut-être planté. D'où ce choix du Punycode.

Nouveauté plus importante de notre RFC 8399, les adresses de courrier électronique en Unicode. Elles étaient déjà permises par la section 7.5 du RFC 5280, mais seulement pour la partie domaine (à droite du @). Désormais, elles sont également possibles dans la partie locale (à gauche du @). Le RFC 8398 donne tous les détails sur ce sujet.

Reste à savoir quelles AC vont accepter Unicode. J'ai testé avec Let's encrypt (avec le client Dehydrated, en mettant le Punycode dans domains.txt) et ça marche, regardez le certificat de https://www.potamochère.fr/. Le voici, affiché par GnuTLS :

% gnutls-cli www.potamochère.fr
...
- subject `CN=www.xn--potamochre-66a.fr', issuer `CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US', serial 0x03ed9617bb88bab3ad5b236675d1dd6e5d27, ...
    

D'autres AC acceptent ces noms en Unicode : Gandi le fait aussi, regardez le certificat de https://réussir-en.fr. On notera que le célèbre service de test de la qualité des configurations TLS, SSLlabs, gère bien les IDN : ssllabs-potamochere.png


Téléchargez le RFC 8399


L'article seul

Testing DNS-over-TLS servers with the RIPE Atlas probes

First publication of this article on 18 July 2018


The RIPE Atlas probes can now perform DNS-over-TLS measurements, following RFC 7858. Several DNS-over-TLS servers exist. This article shows rapidly a few measurements.

To ask the RIPE Atlas probes to perform DNS-over-TLS tests, we will use the Blaeu software. Blaeu is made for one-off measurements, so the results here are not long-term measurements of the evolution of DNS-over-TLS servers. To do a DNS test with Blaeu, you use blaeu-resolve :

% blaeu-resolve mamot.fr
[2001:67c:288::14] : 5 occurrences 
Test #15279055 done at 2018-07-18T21:06:14Z
    

By default, the Atlas probes will use the locally configured resolver. But we can direct the probes to a specific resolver, with --nameserver :

% blaeu-resolve --nameserver dns.quad9.net  www.france-ix.net
Nameserver dns.quad9.net
[2a00:a4c0:1:1::69] : 5 occurrences 
Test #15279064 done at 2018-07-18T21:09:11Z
    

By default, this will use the usual DNS, in clear text, over UDP. But you can now ask for DNS over TLS, with --tls :

    
% blaeu-resolve --nameserver dns.quad9.net  --tls www.bortzmeyer.org
Nameserver dns.quad9.net
[2001:4b98:dc0:41:216:3eff:fe27:3d3f 2605:4500:2:245b::42] : 5 occurrences 
Test #15279068 done at 2018-07-18T21:10:29Z

The --tls option will instruct the Atlas probes to use TLS, here is the entire JSON request which has been sent:

% blaeu-resolve --nameserver dnsovertls.sinodun.com --verbose  --tls  signal.eu.org    
{'is_oneoff': True, 'definitions': [{'description': 'DNS resolution of signal.eu.org/AAAA via nameserver dnsovertls.sinodun.com', 'af': 6, 'type': 'dns', 'query_argument': 'signal.eu.org', 'query_class': 'IN', 'query_type': 'AAAA', 'set_rd_bit': True, 'tls': True, 'protocol': 'TCP', 'use_probe_resolver': False, 'target': 'dnsovertls.sinodun.com'}], 'probes': [{'requested': 5, 'type': 'area', 'value': 'WW', 'tags': {'include': ['system-ipv6-works']}}]}
...

The 'tls': True is the part that triggers DNS-over-TLS.

Now that we have this tool, what can we do? First, let's check if DNS-over-TLS works from everywhere. Some people voiced concerns that port 853, used by DNS-over-TLS, may be blocked on some networks. Let's try:

% blaeu-resolve --requested 1000 --nameserver getdnsapi.net --tls femen.org
Nameserver getdnsapi.net
[2607:5300:60:9fb5::2] : 126 occurrences 
[TUCONNECT (may be a TLS negotiation error)] : 845 occurrences 
[TIMEOUT] : 20 occurrences 
Test #15279078 done at 2018-07-18T21:13:45Z
 

First problem, a lot of TLS negotiation errors. That's because most Atlas probes have old TLS code, and this specific server requires very recent TLS options and ciphers (we have the same problem with Cloudflare's 1.1.1.1 server and, indeed, with many servers). The probes are currently being upgraded but it is far from complete. Let's move to a server which is may be more lax:

% blaeu-resolve --requested 1000 --nameserver ns0.ldn-fai.net --tls femen.org
Nameserver ns0.ldn-fai.net
[TIMEOUT] : 970 occurrences 
[TUCONNECT (may be a TLS negotiation error)] : 12 occurrences 
[2607:5300:60:9fb5::2] : 10 occurrences 
Test #15279106 done at 2018-07-18T21:21:59Z
 

OK, way too many timeouts. It could be because the small server cannot handle all the Atlas probes banging at the same time (Atlas has an option to add jitter but it is not used here). Let's try with another server:

% blaeu-resolve --requested 1000 --nameserver  unicast.censurfridns.dk  --tls  gitlab.isc.org 
Nameserver unicast.censurfridns.dk
[2001:4f8:3:d::126] : 937 occurrences 
[TIMEOUT] : 32 occurrences 
[TUCONNECT (may be a TLS negotiation error)] : 12 occurrences 
Test #15279169 done at 2018-07-18T21:42:11Z

OK, this is better, 3 % timeouts only. But it is still too much. Is it because of TLS? Let's try with regular DNS over TCP, without TLS, using the same set of probes (option --old_measurement):

 
% blaeu-resolve --requested 1000 --old_measurement 15279169 --nameserver unicast.censurfridns.dk  --tcp  gitlab.isc.org
Warning: --requested=1000 ignored since a list of probes was requested
Nameserver unicast.censurfridns.dk
[2001:4f8:3:d::126] : 482 occurrences 
[TUCONNECT (may be a TLS negotiation error)] : 6 occurrences 
[TIMEOUT] : 5 occurrences 
Test #15279208 done at 2018-07-18T21:51:15Z

This time, we have 1.0 % of timeouts, so it seems TLS has indeed problems. (The error message "may be a TLS negotiation error" is spurious. The problems are instead connections refused by the server, or by a middlebox on the path.)

OK, may be the problem of timeouts is because the server unicast.censurfridns.dk is not well connected? Let's try with a well managed and powerful server, Quad9 :

 
% blaeu-resolve --requested 1000 --nameserver 9.9.9.9 --tls www.ietf.org
Nameserver 9.9.9.9
[TIMEOUT] : 106 occurrences 
[2400:cb00:2048:1::6814:155 2400:cb00:2048:1::6814:55] : 872 occurrences 
[ERROR: SERVFAIL] : 1 occurrences 
[TUCONNECT (may be a TLS negotiation error)] : 11 occurrences 
Test #15277228 done at 2018-07-18T17:44:54Z

10 % of timeouts, that's certainly too much, and it proves that the 3 % problems before were not because the server was too weak. (Note there are also TLS negotiation errors, that shouldn't happen, but may have been triggered by middleboxes.) Again, let's try with ordinary DNS, using the same set of probes (unfortunately, many were not available for this comparison, so we have less results):

% blaeu-resolve --nameserver 9.9.9.9 --old_measurement 15277228 www.ietf.org
Nameserver 9.9.9.9
[2400:cb00:2048:1::6814:155 2400:cb00:2048:1::6814:55] : 474 occurrences 
[ERROR: SERVFAIL] : 2 occurrences 
[TIMEOUT] : 19 occurrences 
Test #15277243 done at 2018-07-18T17:53:42Z
 

Only 3.8 % of timeouts, that's better. So, it seems there is indeed a problem specific to port 853, but it seems quite server-specific. (A routing problem would have give the same results on port 53 and 853.) Remember to take these results with a serious grain of salt: it's one measurement, on a specific day and hour, and the Internet is always changing. Serious measurements would require doing it again at different times.

And, even when it works, is DNS-over-TLS much slower? Let's display the RTT of the requests. First with TLS:

%  blaeu-resolve --nameserver dns.quad9.net --requested 1000 --tls --displayrtt  www.afnic.fr
Nameserver dns.quad9.net
[2001:67c:2218:30::24] : 849 occurrences Average RTT 2235 ms
[TIMEOUT] : 129 occurrences Average RTT 0 ms
[TUCONNECT (may be a TLS negotiation error)] : 15 occurrences Average RTT 0 ms
Test #15279140 done at 2018-07-18T21:34:30Z

Then with ordinary TCP:

%  blaeu-resolve --nameserver dns.quad9.net --old_measurement 15279140 --tcp --displayrtt  www.afnic.fr
Nameserver dns.quad9.net
[2001:67c:2218:30::24] : 473 occurrences Average RTT 142 ms
[TUCONNECT (may be a TLS negotiation error)] : 10 occurrences Average RTT 0 ms
[TIMEOUT] : 9 occurrences Average RTT 0 ms
...
Test #15279164 done at 2018-07-18T21:41:37Z

And finally with good old UDP:

%  blaeu-resolve --nameserver dns.quad9.net --old_measurement 15279140  --displayrtt  www.afnic.fr 
Nameserver dns.quad9.net
[2001:67c:2218:30::24] : 471 occurrences Average RTT 237 ms
[TIMEOUT] : 24 occurrences Average RTT 0 ms
[NETWORK PROBLEM WITH RESOLVER] : 1 occurrences Average RTT 0 ms
Test #15279176 done at 2018-07-18T21:45:42Z

Clearly, TLS is much slower, because we have to establish a TLS session first (in real-world use, DNS-over-TLS relies on session reuse.) Note also that TCP seems faster than UDP, which will require more investigation, may be the Atlas is not taking into account the time to establish a TCP connection.

Note that another public resolver show a different picture:

% blaeu-resolve --requested 1000 --nameserver 1.1.1.1 --tls www.ietf.org
Nameserver 1.1.1.1
[TUCONNECT (may be a TLS negotiation error)] : 907 occurrences 
[TIMEOUT] : 35 occurrences 
[2400:cb00:2048:1::6814:155 2400:cb00:2048:1::6814:55] : 49 occurrences 
Test #15277231 done at 2018-07-18T17:47:12Z

% blaeu-resolve --requested 1000 --nameserver 1.1.1.1 --old_measurement 15277231 www.ietf.org
Warning: --requested=1000 ignored since a list of probes was requested
Nameserver 1.1.1.1
[2400:cb00:2048:1::6814:155 2400:cb00:2048:1::6814:55] : 465 occurrences 
[TIMEOUT] : 29 occurrences 
[] : 1 occurrences 
Test #15277240 done at 2018-07-18T17:53:30Z
 

Here, we have more timeouts with UDP than with TLS+TCP.


L'article seul

Conférence « Cryptographie post-quantique » à Pas Sage en Seine

Première rédaction de cet article le 30 juin 2018
Dernière mise à jour le 7 juillet 2018


Le 29 juin, au festival Pas Sage en Seine , j'ai eu le plaisir de faire un exposé technique sur la cryptographie post-quantique. Vu les progrès des calculateurs quantiques, faut-il jeter tout de suite les algorithmes classiques comme RSA ?

Voici les supports de l'exposé :

Les calculateurs quantiques utilisés, simulés ou réels :

Les logiciels post-quantiques utilisés :

Il y avait plein d'autres trucs géniaux (comme d'habitude) à Pas Sage en Seine, n'hésitez pas à regarder le programme. Mes préférés (c'est complètement subjectif) : celui de Luckylex sur les lanceurs d'alerte, dénonçant la répression dont ils sont victimes, celui très concret de Nanar DeNanardon sur les innombrables traces numériques que nous laissons, avec plein de détails peu connus, comme le rôle de DHCP (cf. RFC 7819 pour l'exposé du problème, et RFC 7844 pour une solution), l'exposé de David Legrand (Next Inpact) sur « 20 ans d’évolution du numérique et de Next INpact », retraçant l'aventure d'un fanzine devenu un pilier de l'information libre sur le numérique (cf. leur dernier projet, La presse libre), celui de Shaft « Un panda roux peut-il avoir une vie privée ? » montrant que Firefox a de sérieuses failles en matière de protection de la vie privée (mais les autres navigateurs sont pires), en partie parce que la Fondation Mozilla est trop proche des GAFA. Mais le meilleur exposé était celui de Suzanne Vergnolle et Benoît Piédallu sur leur projet « GDPRBookClub », un très intéressant projet de travail en commun sur le RGPD.

Sinon, sur les phénomènes quantiques, et notamment sur l'intrication, Marc Kaplan m'a fait découvrir cette excellente BD qui l'explique très bien, et avec humour.


L'article seul

Aurait-il fallu faire IPv6 « compatible » avec IPv4 ?

Première rédaction de cet article le 7 juin 2018


Le déploiement du protocole IPv6 continue, mais à un rythme très réduit par rapport à ce qui était prévu et espéré. À cette vitesse, on aura encore de l'IPv4 dans 20 ou 30 ans. Face à cette lenteur exaspérante, on entend souvent des Monsieur Jesaistout affirmer que c'est la conséquence d'une erreur fondamentale au début de la conception du protocole IPv6 : il aurait fallu le faire « compatible avec IPv4 ». Qu'est-ce que cela veut dire, et est-ce que cela aurait été possible ?

On trouve par exemple ce regret d'une absence de compatibilité dans le livre de Milton Mueller, « Will the Internet Fragment?: Sovereignty, Globalization and Cyberspace ». Cet auteur, quoique non-technicien, fait en général très attention à être rigoureux sur les questions techniques et à ne pas écrire de bêtises. Pourtant, là, il en a fait une belle (« in a fateful blunder, [...] the IETF did not make it backwards compatible with the old one »). Pour comprendre en quoi, voyons d'abord en quoi IPv6 est incompatible avec IPv4. Cela concerne notamment les routeurs et les applications. En effet, le format de l'en-tête des paquets IPv6 est très différent de celui de l'en-tête des paquets IPv4. Un routeur ne connaissant qu'IPv4 ne peut rien faire d'un paquet IPv6, il ne peut même pas l'analyser. IPv6 imposait donc une mise à jour de tous les routeurs (aujourd'hui largement faite, même sur le bas de gamme). Et les applications ? Normalement, une bonne partie des applications n'a pas besoin de connaitre les détails de la couche réseau. Après tout, c'est un des buts du modèle en couches que d'isoler les applications des particularités du réseau. Mais il y a des exceptions (application serveur ayant des ACL et devant donc manipuler des adresses IP, par exemple), et, surtout, beaucoup d'applications ne sont pas écrites avec une API de haut niveau : le programmeur ou la programmeuse a utilisé, par exemple, l'API socket, qui expose des tas de détails inutiles, comme la taille des adresses IP, liant ainsi l'application à un protocole réseau particulier. IPv6 impose donc de mettre à jour pas mal d'applications, ce qui est fait depuis longtemps pour les grands logiciels libres connus (Apache, Unbound, Postfix, etc) mais pas forcément pour les petits logiciels locaux développés par l'ESN du coin.

Aurait-on pu s'en tirer en concevant IPv6 différemment ? En gros, non. Pour voir pourquoi, il faut repartir du cahier des charges d'IPv6 : le principal problème était l'épuisement des adresses IPv4. Il fallait donc des adresses plus longues (elles font 128 bits pour IPv6 contre 32 pour IPv4). Même si cela avait été le seul changement dans le format de l'en-tête des paquets, cela aurait suffit à le rendre incompatible, et donc à obliger à changer les routeurs, ainsi que les applications dépendant d'IPv4. Regretter que l'IETF ait changé d'autres aspects de l'en-tête, qu'on aurait pu laisser tranquilles, n'a pas de sens : rien que le changement de taille des adresses invalide tout le code IPv4. Cela ne serait pas le cas si les en-têtes des paquets IP étaient encodés en TLV, ou bien dans un autre format avec des champs de taille variable. Mais, pour des raisons de performance (un routeur peut avoir à traiter des centaines de millions de paquets par seconde), les paquets IP ont un encodage en binaire, avec des champs de taille fixe. Toute modification de la taille d'un de ces champs nécessite donc de changer tout le code de traitement des paquets, tous les ASIC des routeurs.

Même en l'absence de ce problème d'encodage « sur le câble », il n'est pas sûr que tous les programmes existants supporteraient le changement de taille des adresses. Combien d'applications anciennes tiennent pour acquis que les adresses IP ont une taille de seulement 32 bits et, si elles sont écrites en C, les mettent dans des int (entier qui fait en général 32 bits) ?

Néanmoins, malgré ces faits connus depuis longtemps, on croise relativement souvent des affirmations du genre « l'IETF aurait juste dû rajouter des bits aux adresses mais sans changer le format ». Comme on l'a vu, tout changement de taille des adresses change le format. Et, si on ne change pas la taille des adresses, pourquoi utiliser un nouveau protocole ? À moins que, malgré Shannon, on ne croit à la possibilité de mettre 128 bits dans 32 bits ?

Cela ne veut pas dire qu'IPv4 et IPv6 doivent être incapables de se parler, comme « des navires qui se croisent dans la nuit ». On peut penser qu'une solution de traduction d'adresses permettrait au moins certains échanges. Mais attention à ne pas copier simplement le NAT d'IPv4 : IPv4 utilise les ports de TCP et UDP pour identifier une session particulière et savoir où envoyer les paquets. Il n'y a que 16 bits pour stocker les ports, et cela ne suffirait donc pas pour permettre de représenter toutes les adresses IPv6 dans des adresses IPv4 (il manquerait encore 80 bits à trouver…) Il y a bien des solutions avec traduction d'adresses, comme NAT64 (RFC 6146) mais elles ne peuvent s'utiliser que dans des cas limités (pour NAT64, entre un client purement IPv6 et un serveur purement IPv4), et entrainent des dépendances supplémentaires (pour NAT64, la nécessité de disposer d'un résolveur DNS spécial, cf. RFC 6147). Bref, enfonçons le clou : il n'existe pas et il ne peut pas exister de mécanisme permettant une compatibilité complète entre un protocole qui utilise des adresses de 32 bits et un protocole qui utilise des adresses de 128 bits. Il y a des solutions partielles (la plus simple, qu'on oublie souvent, est d'avoir un relais applicatif), mais pas de solution complète.

Bien sûr, c'est en supposant qu'on veut garder la compatibilité avec les anciennes machines et logiciels. Si on repartait de zéro, on pourrait faire un protocole de couche 3 aux adresses de taille variable, mais ce ne serait plus IP, et un tel protocole serait encore plus difficile et coûteux à déployer qu'une nouvelle version d'IP, comme IPv6.

Est-ce simplement moi qui ne vois pas de solution, ou bien est-ce vraiment un problème de fond ? Jusqu'à présent, plein de gens ont râlé « il aurait fallu faire un IPv6 compatible avec IPv4 » mais je n'ai encore vu aucune proposition détaillée indiquant comment faire cela. Il y a plein d'idées « dos de l'enveloppe », de ces idées griffonnées en deux minutes mais qui n'iront jamais plus loin. Écrire un tweet, c'est une chose. Spécifier, même partiellement, un protocole, c'est autre chose. On voit par exemple quelqu'un qui sort de son domaine de compétence (la cryptographie) écrire « they designed the IPv6 address space as an alternative to the IPv4 address space, rather than an extension to the IPv4 address space »). Mais il n'est pas allé plus loin. Au moins, l'auteur du ridicule projet baptisé IPv10 avait fait l'effort de détailler un peu sa proposition (le même auteur avait commis un projet de connecter les satellites par des fibres optiques). C'est d'ailleurs le fait que sa proposition soit relativement détaillée qui permet de voir qu'elle ne tient pas la route : le format des paquets (la seule chose qu'il spécifie de manière un peu précise) étant différent, son déploiement serait au moins aussi lent que celui d'IPv6. Le cryptographe cité plus haut, lui, ne s'est même pas donné cette peine.

Si vous n'êtes pas convaincus par mon raisonnement, je vous propose d'essayer de spécifier un « IPv4 bis » qui serait compatible avec IPv4 tout en offrant davantage d'adresses. Vous pouvez écrire cette spécification sous forme d'un Internet-Draft, d'un article scientifique, ou d'un article sur votre blog. Mettre en œuvre n'est pas obligatoire. Envoyez-moi l'URL ensuite, je suis curieux de voir les résultats. (Rappel : les vagues propositions, trop vagues pour être réfutées, ne sont pas acceptées, il faut une vraie spécification, qu'on puisse soumettre à une analyse technique.) Quelques exemples :

  • IPv10, déjà cité,
  • EnIP (notez son optimisme sur les pare-feux qui, tout à coup, respecteraient ce qu'ils ne connaissent pas).

L'article seul

Version 11 d'Unicode

Première rédaction de cet article le 6 juin 2018


Aujourd'hui 6 juin, la nouvelle version d'Unicode est sortie, la 11.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  
--------
 137439

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

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

684 nouveaux, bien moins que dans les versions précédentes. Quels sont ces nouveaux caractères ?

ucd=> SELECT To_U(codepoint) AS Codepoint, name FROM Characters WHERE version='11.0';
 codepoint |                                    name                                    
-----------+----------------------------------------------------------------------------
...
 U+1F9B8   | SUPERHERO
 U+1F9B9   | SUPERVILLAIN
 U+1F9C1   | CUPCAKE
...
 U+10D00   | HANIFI ROHINGYA LETTER A
...
 U+16E60   | MEDEFAIDRIN SMALL LETTER M
...
 U+1D2E0   | MAYAN NUMERAL ZERO
...
 U+1F12F   | COPYLEFT SYMBOL
...
 U+1F99D   | RACCOON
 U+1F99E   | LOBSTER
 U+1F99F   | MOSQUITO
...
 U+1F9B0   | EMOJI COMPONENT RED HAIR
 

Outre les habituels emojis plus ou moins utiles, et le symbole du copyleft (enfin !) qui plaira aux libristes, on trouve aussi six écritures plus ou moins nouvelles comme le medefaidrin, les chiffres mayas ou comme le hanifi. Les Rohingyas se font massacrer mais au moins leur écriture est désormais dans Unicode.

Toujours dans les emojis, on notera que la norme a précisé que les emojis n'ont pas forcément de genre. Et elle a ajouté des modificateurs permettant de faire varier l'image comme le U+1F9B0 pour mettre des cheveux roux à un personnage, ou comme les changements de direction. Une des erreurs les plus souvent commises à propos des emojis (et d'ailleurs à propos d'Unicode en général) est de croire que l'image proposée par Unicode est normative : ce n'est qu'un exemple, et chaque auteur de police peut l'adapter (comme l'a récemment montré l'affaire de la salade Google). Ainsi, si l'image proposée d'un coureur est un homme aux cheveux sombres, rien n'empêche une police Unicode d'utiliser une femme aux cheveux blonds. Pour les cas où il faut préciser, Unicode offre des mécanismes de modification d'un emoji comme les séquences ZWJ. Si elles sont gérées par votre logiciel (cela semble rare aujourd'hui dans le monde Unix libre mais ça marche, par exemple, chez Apple), vous devriez voir ici un coureur et une coureuse : 🏃‍♂ 🏃‍♀. Si vous voyez au contraire un personnage puis le symbole mâle ou femelle, c'est que votre logiciel ne traite pas ces séquences ZWJ. Voici ce que cela donne avec un Safari sur Mac : zwj-safari-11.1.1-macos-10.13.5.png

Tiens, d'ailleurs, combien de caractères Unicode 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 
-------
  7110

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 
-----------------+-------
 Other_Symbol    |  5984
 Math_Symbol     |   948
 Modifier_Symbol |   121
 Currency_Symbol |    57
(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 8373: Negotiating Human Language in Real-Time Communications

Date de publication du RFC : Mai 2018
Auteur(s) du RFC : R. Gellens (Core Technology Consulting)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF slim
Première rédaction de cet article le 26 mai 2018


Le groupe de travail SLIM de l'IETF s'occupe de définir des mécanismes pour le choix d'une langue lors de la communication. Son premier RFC, le RFC 8255, concernait le courrier électronique. Ce nouveau RFC concerne, lui, les communications « en temps réel », comme la téléphonie sur IP.

Un scénario d'usage typique est celui d'un client qui appelle le support d'une société internationale. Mettons que le client a pour langue maternelle l'ourdou mais peut se débrouiller en anglais. On veut qu'il puisse préciser cet ordre de préférences, et, idéalement, que le logiciel utilisé dans le call center le route automatiquement vers un employé qui parle ourdou ou, si aucun n'est disponible, vers un employé qui parle anglais. Plus vital, le scénario d'un appel d'urgence où un touriste danois en vacances en Italie appelle le 112 et où il faut trouver très vite quelqu'un qui peut parler une langue qu'il comprend (sachant qu'en situation d'urgence, on est moins à l'aise avec les langues étrangères). Comme le dit avec euphémisme le RFC « avoir une langue en commun est utile pour la communication ». Pour gérer tous ces scénarios, notre RFC va utiliser les attributs de SDP (RFC 4566, SDP est un format - pas un protocole, en dépit de son nom - déjà très utilisé dans les protocoles de communication instantanée pour transmettre des métadonnées au sujet d'une communication).

Parfois, on a déjà l'information disponible (si on appelle une personne qu'on connait et qui nous connait), et déjà choisi une langue (par exemple une audioconférence dans une entreprise où la règle est que tout se fasse en anglais). Notre RFC traite le cas où on n'a pas cette information, et il faut donc une négociation au début de la communication. Cela implique que le logiciel des deux côtés ait été configuré avec les préférences et capacités linguistiques des deux parties (une question d'interface utilisateur, non spécifiée par ce RFC).

Notez qu'il peut y avoir plusieurs langues différentes utilisées, une pour chaque flux de données. Par exemple, l'appelant peut parler dans une langue que son interlocuteur comprend, mais qu'il a du mal à parler, et il utilisera donc une autre langue pour la réponse. Notez aussi que la communication n'est pas uniquement orale, elle peut être écrite, par exemple pour les malentendants. Le RFC rappelle à juste titre qu'un sourd n'est pas forcément muet et qu'il ou elle peut donc choisir l'oral dans une direction et le texte dans une autre. (Au passage, la synchronisation des lèvres, pour la lecture sur les lèvres, est traitée dans le RFC 5888.)

La solution choisie est décrite en détail dans la section 5 de notre RFC. Elle consiste en deux attributs SDP, hlang-send et hlang-recv (hlang = human language). Leur valeur est évidemment une étiquette de langue, telles qu'elles sont normalisées dans le RFC 5646. Dans une offre SDP, hlang-send est une liste (pas une langue unique) de langues que l'offreur sait parler, séparées par des espaces, donnée dans l'ordre de préférence décroissante, et hlang-recv une liste de langues qu'elle ou lui comprend. Notez qu'il est de la responsabilité de l'offreur (typiquement celui ou celle qui appelle) de proposer des choix réalistes (le RFC donne le contre-exemple d'un offreur qui demanderait à parler en hongrois et à avoir la réponse en portugais…) D'autre part, notre RFC recommande de bien lire la section 4.1 du RFC 5646, qui indique d'étiqueter intelligement, et notamment de ne pas être trop spécifique : si on est australien et qu'on comprend bien l'anglais, indiquer comme langue en est suffisant, préciser (ce qui serait une étiquette légale) en-AU est inutile et même dangereux si le répondant se dit « je ne sais pas parler avec l'accent australien, tant pis, je raccroche ».

La langue choisie par le répondant est indiquée dans la réponse. hlang-send et hlang-recv sont cette fois des langues uniques. Attention, ce qui est envoi pour l'une des parties est réception pour l'autre : hlang-send dans la réponse est donc un choix parmi les hlang-recv de l'offre. L'offreur (l'appelant) est ainsi prévenu du choix qui a été effectué et peut se préparer à parler la langue indiquée par le hlang-recv du répondant, et à comprendre celle indiquée par le hlang-send.

Voici un exemple simple d'un bloc SDP (on n'en montre qu'une partie), où seul l'anglais est proposé ou accepté (cet exemple peut être une requête ou une réponse) :

m=audio 49170 RTP/AVP 0
a=hlang-send:en
a=hlang-recv:en
    

Le cas où hlang-send et hlang-recv ont la même valeur sera sans doute fréquent. Il avait même été envisagé de permettre un seul attribut (par exemple hlang) dans ce cas courant mais cela avait été écarté, au profit de la solution actuelle, plus générale.

Un exemple un peu plus compliqué où la demande propose trois langues (espagnol, basque et anglais dans l'ordre des préférences décroissantes) :

m=audio 49250 RTP/AVP 20
a=hlang-send:es eu en
a=hlang-recv:es eu en
    

Avec une réponse où l'espagnol est utilisé :

m=audio 49250 RTP/AVP 20
a=hlang-send:es
a=hlang-recv:es     
    

Et si ça rate ? S'il n'y a aucune langue en commun ? Deux choix sont possibles, se résigner à utiliser une langue qu'on n'avait pas choisi, ou bien raccrocher. Le RFC laisse aux deux parties la liberté du choix. En cas de raccrochage, le code d'erreur SIP à utiliser est 488 (Not acceptable here) ou bien 606 (Not acceptable), accompagné d'un en-tête d'avertissement avec le code 308, par exemple :

Warning: 308 code proxy.example.com
         "Incompatible language specification: Requested languages "fr
	 zh" not supported. Supported languages are: "es en".
    

Si la langue indiquée est une langue des signes, elle peut être utilisée dans un canal vidéo, mais évidemment pas dans un canal audio. (Le cas d'un canal texte est laissé à l'imagination des lecteurs. Le cas des sous-titres, ou autres textes affichés dans une vidéo, n'est pas traité par notre RFC.)

Voici un exemple bien plus riche, avec plusieurs médias. La vidéo en langue des signes argentine, le texte en espagnol (ou à la rigueur en portugais), et un canal audio, mêmes préférences que le texte :

m=video 51372 RTP/AVP 31 32
a=hlang-send:aed

m=text 45020 RTP/AVP 103 104
a=hlang-send:es pt

m=audio 49250 RTP/AVP 20
a=hlang-recv:es pt     
    

Voici une réponse possible à cette requête, avec de l'espagnol pour le canal texte et pour la voix. Aucune vidéo n'est proposée, sans doute car aucune n'était disponible dans la langue demandée :

m=video 0 RTP/AVP 31 32

m=text 45020 RTP/AVP 103 104
a=hlang-recv:es

m=audio 49250 RTP/AVP 20
a=hlang-send:es    
    

Notez que ce RFC ne fournit pas de mécanisme pour exprimer des préférences entre les différents canaux (texte et audio, par exempe), uniquement entre langues pour un même canal.

Les deux attributs hlang-recv et hlang-send ont été ajoutés au registre IANA des attributs SDP.

Notons que la section 8 du RFC, sur la protection de la vie privée, rappelle qu'indiquer les préférences linguistiques peut permettre d'apprendre des choses sur l'utilisateur, par exemple sa nationalité. Une section Privacy considerations, quoique non obligatoire, est de plus en plus fréquente dans les RFC.

Enfin, question alternatives, le RFC note aussi (section 4) qu'on aurait pu utiliser l'attribut existant lang, qui existe déjà dans SDP (RFC 4566, section 6). Mais il n'est pas mentionné dans le RFC 3264, ne semble pas utilisé à l'heure actuelle, et ne permet pas de spécifier une langue différente par direction de communication.

À ma connaissance, il n'y a pas encore de mise en œuvre de ce RFC mais comme il est cité dans des documents normatifs, par exemple dans le NENA 08-01 de la North American Emergency Number Association, il est possible qu'elles apparaissent bientôt.


Téléchargez le RFC 8373


L'article seul

RFC 8375: Special-Use Domain 'home.arpa.'

Date de publication du RFC : Mai 2018
Auteur(s) du RFC : P. Pfister (Cisco Systems), T. Lemon (Nominum)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF homenet
Première rédaction de cet article le 18 mai 2018


Ce nouveau RFC a l'air compliqué comme cela, mais en fait il ne fait qu'une chose : remplacer, dans le protocole Homenet/HNCP (Home Networking Control Protocol), le nom de domaine .home par home.arpa.

home.arpa est désormais enregistré dans la liste officielle des noms de domaine spéciaux, ceux qui ne passent pas par les mécanismes habituels d'enregistrement de noms de domaine, et/ou les mécanismes habituels de résolution DNS. (Cette liste a été créée par le RFC 6761, et critiquée par le RFC 8244. home.arpa n'étant pas un TLD, il pose moins de problèmes politiciens.)

Quelle est l'utilité de ce nom home.arpa ? La série de protocoles Homenet (l'architecture de Homenet est décrite dans le RFC 7368) vise à doter la maison de l'utilisateur normal (pas participant à l'IETF) d'un ensemble de réseaux IPv6 qui marchent automatiquement, sans intervention humaine. Parmi les protocoles Homenet, HNCP, normalisé dans le protocole RFC 7788 est le protocole de configuration. Il utilise un suffixe pour les noms de domaines comme nas.SUFFIXE ou printer.SUFFIX. C'est ce home.arpa qui va désormais servir de suffixe.

Mais quel était le problème avec le suffixe .home du RFC 7788 ? D'abord, le RFC 7788 avait commis une grosse erreur, enregistrée sous le numéro 4677 : il ne tenait pas compte des règles du RFC 6761, et réservait ce TLD .home sans suivre les procédures du RFC 6761. Notamment, il ne listait pas les particularités qui font que ce domaine est spécial (pour home.arpa, notre nouveau RFC 8375 le fait dans sa section 5), et il ne demandait pas à l'IANA de le mettre dans le registre des noms de domaine spéciaux. Cela avait des conséquences pratiques comme le fait que ce .home ne pouvait pas marcher à travers un résolveur DNS validant (puisque ce nom n'existait pas du tout dans la racine). Un bon article sur ce choix et sur les problèmes qu'il posait était « Homenet, and the hunt for a name ».

On peut aussi ajouter que le risque de « collision » entre deux noms de domaine était élevé puisque pas mal de réseaux locaux sont nommés sous .home et que ce nom est un de ceux qui « fuitent » souvent vers les serveurs racines (voir par exemple les statistiques du serveur racine L.). On peut consulter à ce sujet les documents de l'ICANN « New gTLD Collision Risk Mitigation » et « New gTLD Collision Occurence Management ». Notons qu'il y avait eu plusieurs candidatures (finalement rejetées en février 2018) pour un .home en cours auprès de l'ICANN. Exit, donc, .home, plus convivial mais trop convoité. Demander à l'ICANN de déléguer un .home pour l'IETF (ce qui aurait été nécessaire pour faire une délégation DNSSEC non signée, cf. RFC 4035, section 4.3) aurait pris dix ou quinze ans.

À la place, voici home.arpa, qui profite du RFC 3172, et du caractère décentralisé du DNS, qui permet de déléguer des noms sous .arpa.

L'utilisation de home.arpa n'est pas limitée à HNCP, tous les protocoles visant le même genre d'usage domestique peuvent s'en servir. Il n'a évidemment qu'une signification locale.

La section 3 décrit le comportement général attendu avec home.arpa. Ce n'est pas un nom de domaine comme les autres. Sa signification est purement locale. printer.home.arpa désignera une machine à un endroit et une autre machine dans une autre maison. Les serveurs DNS globaux ne peuvent pas être utilisés pour résoudre les noms sous home.arpa. Tous les noms se terminant par ce suffixe doivent être traités uniquement par les résolveurs locaux, et jamais transmis à l'extérieur.

Notez que, la plupart du temps, les utilisateurs ne verront pas le suffixe home.arpa, l'interface des applications « Homenet » leur masquera cette partie du nom. Néanmoins, dans certains cas, le nom sera sans doute visible, et il déroutera sans doute certains utilisateurs, soit à cause du suffixe arpa qui n'a pas de signification pour eux, soit parce qu'ils ne sont pas anglophones et qu'ils ne comprennent pas le home. Il n'y a pas de solution miracle à ce problème.

La section 4 est le formulaire d'enregistrement dans le registre des noms spéciaux, suivant les formalités du RFC 6761, section 5. (Ce sont ces formalités qui manquaient au RFC 7788 et qui expliquent l'errata.) Prenons-les dans l'ordre (relisez bien la section 5 du RFC 6761) :

  • Les humains et les applications qu'ils utilisent n'ont pas à faire quelque chose de particulier, ces noms, pour eux, sont des noms comme les autres.
  • Les bibliothèques de résolution de noms (par exemple, sur Mint, la GNU libc) ne devraient pas non plus appliquer un traitement spécial aux noms en home.arpa. Elles devraient passer par le mécanisme de résolution normal. Une exception : si la machine a été configurée pour utiliser un autre résolveur DNS que celui de la maison (un résolveur public, par exemple, qui ne connaîtra pas votre home.arpa ), il peut être nécessaire de mettre une règle particulière pour faire résoudre ces noms par un résolveur local.
  • Les résolveurs locaux (à la maison), eux, doivent traiter ces noms à part, comme étant des « zones locales » à l'image de celles décrites dans le RFC 6303. Bref, le résolveur ne doit pas transmettre ces requêtes aux serveurs publics faisant autorité (il y a une exception pour le cas particulier des enregistrements DS). Ils doivent transmettre ces requêtes aux serveurs locaux qui font autorité pour ces noms (cf. section 7).
  • Les serveurs publics faisant autorité n'ont pas besoin d'un comportement particulier. Par exemple, ceux qui font autorité pour .arpa retournent une délégation normale.

Voici la délégation :


% dig @a.root-servers.net ANY home.arpa

; <<>> DiG 9.10.3-P4-Debian <<>> @a.root-servers.net ANY home.arpa
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48503
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;home.arpa.		IN ANY

;; AUTHORITY SECTION:
home.arpa.		172800 IN NS blackhole-1.iana.org.
home.arpa.		172800 IN NS blackhole-2.iana.org.
home.arpa.		86400 IN NSEC in-addr.arpa. NS RRSIG NSEC
home.arpa.		86400 IN RRSIG NSEC 8 2 86400 (
				20180429000000 20180415230000 56191 arpa.
				K4+fNoY6SXQ+VtHsO5/F0oYrRjZdNSG0MSMaeDSQ78aC
				NHko4uqNAzoQzoM8a2joFeP4wOL6kVQ72UJ5zqd/iZJD
				0ZSh/57lCUVxjYK8sL0dWy/3xr7kbaqi58tNVTLkp8GD
				TfyQf5pW1rtRB/1pGzbmTZkK1jXw4ThG3e9kLHk= )

;; Query time: 24 msec
;; SERVER: 2001:503:ba3e::2:30#53(2001:503:ba3e::2:30)
;; WHEN: Mon Apr 16 09:35:35 CEST 2018
;; MSG SIZE  rcvd: 296

      

La section 5 rassemble les changements dans la norme HNCP (RFC 7788. C'est juste un remplacement de .home par home.arpa.

Quelques petits trucs de sécurité (section 6). D'abord, il ne faut pas s'imaginer que ces noms locaux en home.arpa sont plus sûrs que n'importe quel autre nom. Ce n'est pas parce qu'il y a home dedans qu'on peut leur faire confiance. D'autant plus qu'il y a, par construction, plusieurs home.arpa, et aucun moyen, lorsqu'on se déplace de l'un à l'autre, de les différencier. (Des travaux ont lieu pour concevoir un mécanisme qui pourrait permettre d'avertir l'utilisateur « ce n'est pas le home.arpa que vous pensez » mais ils n'ont pas encore abouti.)

home.arpa n'est pas sécurisé par DNSSEC. Il ne serait pas possible de mettre un enregistrement DS dans .arpa puisqu'un tel enregistrement est un condensat de la clé publique de la zone et que chaque home.arpa qui serait signé aurait sa propre clé. Une solution possible aurait été de ne pas déléguer home.arpa. .arpa étant signé, une telle non-délégation aurait pu être validée par DNSSEC (« denial of existence »). La réponse DNS aurait été (commande tapée avant la délégation de home.arpa) :


% dig A printer.home.arpa
...
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 37887
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 1
...
;; AUTHORITY SECTION:
arpa.			10800 IN SOA a.root-servers.net. nstld.verisign-grs.com. (
				2017112001 ; serial
				1800       ; refresh (30 minutes)
				900        ; retry (15 minutes)
				604800     ; expire (1 week)
				86400      ; minimum (1 day)
				)
arpa.			10800 IN RRSIG SOA 8 1 86400 (
				20171203120000 20171120110000 36264 arpa.
				QqiRv85fb6YO/79ZdtQ8Ke5FmZHF2asjLrNejjcivAAo...
arpa.			10800 IN RRSIG NSEC 8 1 86400 (
				20171203120000 20171120110000 36264 arpa.
				dci8Yr95yQtL9nEBFL3dpdMVTK3Z2cOq+xCujeLsUm+W...
arpa.			10800 IN NSEC as112.arpa. NS SOA RRSIG NSEC DNSKEY
e164.arpa.		10800 IN RRSIG NSEC 8 2 86400 (
				20171203120000 20171120110000 36264 arpa.
				jfJS6QuBEFHWgc4hhtvdfR0Q7FCCgvGNIoc6169lsxz7...
e164.arpa.		10800 IN NSEC in-addr.arpa. NS DS RRSIG NSEC

;; Query time: 187 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Nov 20 20:28:27 CET 2017
;; MSG SIZE  rcvd: 686

Ici, on reçoit un NXDOMAIN (ce domaine n'existe pas), et les enregistrements NSEC qui prouvent que home.arpa n'existe pas non plus (rien entre e164.arpa et in-addr.arpa). Mais cela aurait nécessité un traitement spécial de home.arpa par le résolveur validant (par exemple, sur Unbound, domain-insecure: "home.arpa"). Finalement, le choix fait a été celui d'une délégation non sécurisée (section 7 du RFC), vers les serveurs blackhole-1.iana.org et blackhole-2.iana.org :


% dig NS home.arpa

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

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

;; ANSWER SECTION:
home.arpa.		190 IN NS blackhole-1.iana.org.
home.arpa.		190 IN NS blackhole-2.iana.org.

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Apr 16 09:36:25 CEST 2018
;; MSG SIZE  rcvd: 98

    

Cette délégation a été faite le 15 mars 2018.

Le domaine home.arpa a été ajouté dans le registre des noms de domaine spéciaux ainsi que dans celui des noms servis localement.

En testant avec les sondes RIPE Atlas, on voit que tous les résolveurs ne voient pas la même chose, ce qui est normal, chaque maison pouvant avoir son home.arpa local :

    
% blaeu-resolve -r 1000 -q SOA home.arpa
[prisoner.iana.org. hostmaster.root-servers.org. 1 604800 60 604800 604800] : 548 occurrences 
[prisoner.iana.org. hostmaster.root-servers.org. 1 1800 900 604800 604800] : 11 occurrences 
[prisoner.iana.org. hostmaster.root-servers.org. 1 1800 900 604800 15] : 33 occurrences 
[prisoner.iana.org. hostmaster.root-servers.org. 2002040800 1800 900 604800 60480] : 229 occurrences 
[ERROR: FORMERR] : 1 occurrences 
[ERROR: SERVFAIL] : 132 occurrences 
[] : 4 occurrences 
[prisoner.iana.org. hostmaster.root-servers.org. 1 604800 60 604800 3600] : 11 occurrences 
[prisoner.iana.org. hostmaster.trex.fi. 1 604800 86400 2419200 86400] : 4 occurrences 
[prisoner.iana.org. ops.inx.net.za. 1513082668 10800 3600 604800 3600] : 2 occurrences 
[TIMEOUT(S)] : 19 occurrences 
Test #12177308 done at 2018-04-16T07:38:32Z

On voit sur ce premier test que la grande majorité des sondes voient le vrai SOA (numéro de série 1 ou 2002040800 ; curieusement, les serveurs faisant autorité envoient des numéros différents). Certaines voient un tout autre SOA (par exemple celle où l'adresse du responsable est en Afrique du Sud ou bien en Finlande), et le numéro de série très différent. Ce n'est pas un problème ou un piratage : le principe de home.arpa est que chacun peut avoir le sien.

À l'heure actuelle, toutes les mises en œuvre en logiciel libre que j'ai regardées utilisent encore .home, mais elles semblent souvent non maintenues.


Téléchargez le RFC 8375


L'article seul

RFC 8374: BGPsec Design Choices and Summary of Supporting Discussions

Date de publication du RFC : Avril 2018
Auteur(s) du RFC : K. Sriram (USA NIST)
Pour information
Première rédaction de cet article le 1 mai 2018


Ce RFC est un peu spécial : il ne normalise pas un protocole, ni des procédures internes à l'IETF, et il n'est pas non plus la description d'un problème à résoudre, ou un cahier des charges d'une solution à développer. Non, ce RFC est la documentation a posteriori des choix qui ont été effectués lors du développement de BGPsec, une solution de sécurisation du routage Internet. C'est donc un document utile si vous lisez les RFC sur BGPsec, comme le RFC 8205 et que vous vous demandez « mais pourquoi diable ont-ils choisi cette approche et pas cette autre, qui me semble bien meilleure ? »

Un petit rappel du contexte : le protocole de routage BGP fonctionne en échangeant des informations entre pairs, sur les routes que chaque pair sait joindre. Par défaut, un pair peut raconter n'importe quoi, dire qu'il a une route vers 2001:db8::/32 alors qu'il n'est pas le titulaire de ce préfixe et n'a pas non plus appris cette route d'un de ses pairs. Cela rend donc le routage Internet assez vulnérable. Pour le sécuriser, il existe plusieurs mécanismes qui font que, en pratique, ça ne marche pas trop mal. L'IETF a développé une solution technique, qui a deux couches : une infrastructure à clés publiques, la RPKI, normalisée dans les RFC 6480 et RFC 6481, et une seconde couche, les services qui utilisent la RPKI pour authentifier tel ou tel aspect du routage. Deux de ces services sont normalisés, les ROA (Route Origin Authorization) des RFC 6482 et RFC 6811, qui servent à authentifier l'AS d'origine d'un préfixe, et BGPsec (RFC 8205), qui sert à authentifier le chemin d'AS, la liste des AS empruntés par une annonce de route (cf. section 2.1.1 de notre RFC). Sans BGPsec, les ROA, utilisés seuls, ne peuvent pas arrêter certaines attaques (cf. RFC 7132, qui explique quelles menaces traite BGPsec, et RFC 7353, cahier des charges de BGPsec). Par exemple, si l'AS 64641, malhonnête, veut tromper son pair, l'AS 64642, à propos du préfixe 2001:db8::/32, et que ce préfixe a un ROA n'autorisant que 64643 à être à l'origine, le malhonnête peut fabriquer une annonce avec le chemin d'AS 64641 [éventuellement d'autres AS] 64643 (rappelez-vous que les chemins d'AS se lisent de droite à gauche) et l'envoyer à 64641. Si celui-ci vérifie le ROA, il aura l'impression que tout est normal, l'AS 64643 étant bien indiqué comme l'origine. Et cela marchera même si les annonces de l'AS 64643 ne sont jamais passées par ce chemin ! BGPsec répare ce problème en transportant un chemin d'AS signé, authentifiant toutes les étapes.

Lors du développement de BGPsec, un document avait été rédigé pour documenter tous les choix effectués, mais n'avait pas été publié. C'est désormais chose faite avec ce RFC, qui documente les choix, les justifie, et explique les différences entre ces choix initiaux et le protocole final, modifié après un long développement. Parmi les points importants du cahier des charges (RFC 7353) :

  • Valider la totalité du chemin d'AS (et pas seulement détecter certains problèmes, comme le font les ROA),
  • Déployable de manière incrémentale puisqu'il est évidemment impossible que tous les opérateurs adoptent BGPsec le même jour,
  • Ne pas diffuser davantage d'informations que celles qui sont déjà diffusées. Par exemple, les opérateurs ne souhaitent pas forcément publier la liste de tous leurs accords d'appairage.

Finie, l'introduction, passons tout de suite à certains des choix qui ont été effectués. (Il est évidemment recommandé de lire le RFC 8205 avant, puis de lire notre RFC 8374 après, si on veut tous les choix documentés.) D'abord, à quoi ressemblent les signatures, et les nouveaux messages BGP (section 2 de notre RFC). Les signatures BGPsec signent le préfixe IP, l'AS signataire, et l'AS suivant à qui on va transmettre l'annonce. Le premier point important est qu'on signe l'AS suivant, de manière à créer une chaîne de signatures vérifiables. Autrement, un attaquant pourrait fabriquer un faux chemin à partir de signatures correctes.

Si on utilise le prepending d'AS, la première version de BGPsec prévoyait une signature à chaque fois, mais on peut finalement mettre une seule signature pour la répétition d'AS, avec une variable pCount qui indique leur nombre (RFC 8205, section 3.1), afin de diminuer le nombre de signatures (les signatures peuvent représenter la grande majorité des octets d'une annonce BGP).

Le second point important est que certains attributs ne sont pas pas signés, comme par exemple la préférence locale (RFC 4271, section 5.1.5) ou les communautés (cf. RFC 1997). Ces attributs pourront donc être modifiés par un attaquant à sa guise. Ce n'est pas si grave que ça en a l'air car la plupart de ces attributs n'ont de sens qu'entre deux AS (c'est le cas de la communauté NO_EXPORT) ou sont internes à un AS (la préférence locale). Sur ces courtes distances, on espère de toute façon que la session BGP sera protégée, par exemple par AO (RFC 5925).

La signature est transportée dans un attribut BGP optionnel et non-transitif (qui n'est pas transmis tel quel aux routeurs suivants). Optionnel car, sinon, il ne serait pas possible de déployer BGPsec progressivement. Et non-transitif car un routeur BGPsec n'envoie les signatures que si le routeur suivant lui a dit qu'il savait gérer BGPsec.

Dans le message BGP signé, le routeur qui signe est identifié par sa clé publique, pas par le certificat entier. Cela veut dire qu'on ne peut valider les signatures que si on a accès à une dépôt de la RPKI, avec les certificats.

La section 3 de notre RFC traite le cas des retraits de route : contrairement aux annonces, ils ne sont pas signés. Un AS est toujours libre de retirer une route qu'il a annoncée (BGP n'accepte pas un retrait venant d'un autre pair). Si on a accepté l'annonce, il est logique d'accepter le retrait (en supposant évidemment que la session entre les deux routeurs soit raisonnablement sécurisée).

La section 4, elle, parle des algorithmes de signature. Le RFC 8208 impose ECDSA avec la courbe P-256 (cf. RFC 6090). RSA avait été envisagé mais ECDSA avait l'avantage de signatures bien plus petites (cf. l'étude du NIST sur les conséquences de ce choix).

Une autre décision importante dans cette section est la possibilité d'avoir une clé par routeur et pas par AS (cf. RFC 8207). Cela évite de révoquer un certificat global à l'AS si un seul routeur a été piraté. (Par contre, il me semble que c'est indiscret, permettant de savoir quel routeur de l'AS a relayé l'annonce, une information qu'on n'a pas forcément actuellement.)

Décision plus anecdotique, en revanche, celle comme quoi le nom dans le certificat (Subject) sera la chaîne router suivie du numéro d'AS (cf. RFC 5396) puis de l'identité BGP du routeur. Les détails figurent dans le RFC 8209.

Voyons maintenant les problèmes de performance (section 5). Par exemple, BGPsec ne permet pas de regrouper plusieurs préfixes dans une annonce, comme on peut le faire traditionnellement avec BGP. C'est pour simplifier le protocole, dans des cas où un routeur recevrait une annonce avec plusieurs préfixes et n'en transmettrait que certains. Actuellement, il y a en moyenne quatre préfixes par annonce (cf. l'étude faite par l'auteur du RFC). Si tout le monde adoptait BGPsec, on aurait donc quatre fois plus d'annonces, et il faudra peut-être dans le futur optimiser ce cas.

On l'a vu plus haut, il n'est pas envisageable de déployer BGPsec partout du jour au lendemain. Il faut donc envisager les problèmes de coexistence entre BGPsec et BGP pas sécurisé (section 6 de notre RFC). Dès que deux versions d'un protocole, une sécurisée et une qui ne l'est pas, coexistent, il y a le potentiel d'une attaque par repli, où l'attaquant va essayer de convaindre une des parties de choisir la solution la moins sécurisée. Actuellement, BGPsec ne dispose pas d'une protection contre cette attaque. Ainsi, il n'y a pas de moyen de savoir si un routeur qui envoie des annonces non-signées le fait parce qu'il ne connait pas BGPsec, ou simplement parce qu'un attaquant a modifié le trafic.

La possibilité de faire du BGPsec est négociée à l'établissement de la session BGP. Notez qu'elle est asymétrique. Par exemple, il est raisonnable qu'un routeur de bordure signe ses propres annonces mais accepte tout de la part de ses transitaires, et ne lui demande donc pas d'envoyer les signatures. Pendant un certain temps (probablement plusieurs années), nous aurons donc des ilots BGPsec au milieu d'un océan de routeurs qui font du BGP traditionnel. On peut espérer qu'au fur et à mesure du déploiement, ces ilots se rejoindront et formeront des iles, puis des continents.

La question de permettre des chemins d'AS partiellement signés avait été discutée mais cela avait été rejeté : il faut signer tout le chemin, ou pas du tout. Des signatures partielles auraient aidé au déploiement progressif mais auraient été dangereuses : elle aurait permis aux attaquants de fabriquer des chemins valides en collant des bouts de chemins signés - et donc authentiques - avec des bouts de chemins non-signés et mensongers.

La section 7 de notre RFC est consacrée aux interactions entre BGPsec et les fonctions habituelles de BGP, qui peuvent ne pas bien s'entendre avec la nouvelle sécurité. Par exemple, les très utiles communautés BGP (RFC 1997 et RFC 8092). On a vu plus haut qu'elles n'étaient pas signées du tout et donc pas protégées. La raison est que les auteurs de BGPsec considèrent les communautés comme mal fichues, question sécurité. Certaines sont utilisées pour des décisions effectives par les routeurs, d'autres sont juste pour le déboguage, d'autres encore purement pour information. Certaines sont transitives, d'autres utilisées seulement entre pairs se parlant directement. Et elles sont routinement modifiées en route. Le RFC conseille, pour celles qui ne sont utilisées qu'entre pairs directs, de plutôt sécuriser la session BGP.

Pour les communautés qu'on veut voir transmises transitivement, il avait été envisagé d'utiliser un bit libre pour indiquer que la communauté était transitive et donc devait être incluse dans la signature. Mais la solution n'a pas été retenue. Conseil pratique, dans la situation actuelle : attention à ne pas utiliser des communautés transmises transitivement pour des décisions de routage.

Autre cas pratique d'interaction avec un service très utile, les serveurs de route. Un point d'échange peut fonctionner de trois façons :

  • Appairages directs entre deux membres et, dans ce cas, BGPsec n'a rien de particulier à faire.
  • Utilisation d'un serveur de routes qui ajoute son propre AS dans le chemin (mais, évidemment, ne change pas le NEXT_HOP, c'est un serveur de routes, pas un routeur). Cette méthode est de loin la plus rare des trois. Là aussi, BGPsec n'a rien de particulier à faire.
  • Utilisation d'un serveur de routes qui n'ajoute pas son propre AS dans le chemin. Sur un gros point d'échange, cette méthode permet d'éviter de gérer des dizaines, voire des centaines d'appairages. Pour BGPsec, c'est le cas le plus délicat. Il faut que le serveur de routes mette son AS dans le chemin, pour qu'il puisse être validé, mais en positionnant pCount à 0 (sa valeur normale est 1, ou davantage si on utilise le prepending) pour indiquer qu'il ne faut pas en tenir compte pour les décisions de routage (fondées sur la longueur du chemin), seulement pour la validaton.

Un point de transition intéressant est celui des numéros d'AS de quatre octets, dans le RFC 4893. La technique pour que les AS ayant un tel numéro puisse communiquer avec les vieux routeurs qui ne comprennent pas ces AS est un bricolage utilisant un AS spécial (23456), bricolage incompatible avec BGPsec, qui, d'ailleurs, exige que les AS de quatre octets soient acceptés. En pratique, on peut espérer que les derniers routeurs ne gérant pas les AS de quatre octets auront disparu bien avant que BGPsec soit massivement déployé.

La section 8 du RFC discute de la validation des chemins d'AS signés. Par exemple, le RFC 8205 demande qu'un routeur transmette les annonces ayant des signatures invalides. Pourquoi ? Parce que la RPKI n'est que modérement synchrone : il est parfaitement possible qu'un nouveau certificat ne soit arrivé que sur certains routeurs et que, donc, certains acceptent la signature et d'autres pas. Il ne faut donc pas préjuger de ce que pourront valider les copains.

Une question qui revient souvent avec les techniques de sécurité (pas seulement BGPsec mais aussi des choses comme DNSSEC) est « et si la validation échoue, que doit-on faire des données invalides ? » Vous ne trouverez pas de réponse dans le RFC : c'est une décision locale. Pour BGPsec, chaque routeur, ou plus exactement son administrateur, va décider de ce qu'il faut faire avec les annonces dont le chemin d'AS signé pose un problème. Contrairement à DNSSEC, où la validation peut donner trois résultats (oui, en fait, quatre, mais je simplifie, cf. RFC 4035), « sûr », « non sûr », et « invalide », BGPsec n'a que deux résultats possibles, « valide » et « invalide ». L'état « invalide » regroupe aussi bien les cas où le chemin d'AS n'est pas signé (par exemple parce qu'un routeur non-BGPsec se trouvait sur le trajet) que le cas où une signature ne correspond pas aux données (les deux états « non sûr » et « invalide » de DNSSEC se réduisent donc à un seul ici). Il avait été discuté de faire une division plus fine entre les différents cas d'invalidité mais il avait semblé trop complexe de rendre en compte tous les cas possibles. Notez que « invalide » couvre même le cas où un ROA valide l'origine (un cas particulier des chemins partiellement signés, déjà traités).

Donc, si une annonce est invalide, que doit faire le routeur ? D'abord, la décision d'accepter ou pas une route dépend de plusieurs facteurs, la validation BGPsec n'en étant qu'un seul. Ensuite, il n'est pas évident de traiter tous les cas. D'où la décision de laisser le problème à l'administrateur réseaux.

Ah, et si vous utilisez iBGP, notez que la validation BGPsec ne se fait qu'en bord d'AS. Vous pouvez transporter l'information comme quoi une annonce était valide ou invalide comme vous voulez (une communauté à vous ?), il n'existe pas de mécanisme standard dans iBGP pour cela.

Enfin, la section 9 de notre RFC traite de quelques problèmes d'ordre opérationnel. Mais pensez à lire le RFC 8207 avant. Par exemple, elle estime que BGPsec, contrairement à la validation avec les ROA seuls, nécessitera sans doute du nouveau matériel dans les routeurs, comme un coprocesseur cryptographique, et davantage de RAM. C'est une des raisons pour lesquelles on ne verra certainement pas de déploiement significatif de BGPsec avant des années. Ceci dit, au début, les routeurs BGPsec auront peu de travail supplémentaire, précisément puisqu'il y aura peu d'annonces signées, donc pourront retarder les mises à jour matérielles. D'ici que BGPsec devienne banal, des optimisations comme celles décrites dans cet exposé ou celui-ci, ou encore dans l'article « Design and analysis of optimization algorithms to minimize cryptographic processing in BGP security protocols » aideront peut-être.


Téléchargez le RFC 8374


L'article seul

A Fediverse/Mastodon bot for DNS queries

First publication of this article on 25 April 2018
Last update on of 3 May 2018


I created a bot to answer DNS queries over the fediverse (decentralized social network, best known implementation being Mastodon). What for? Well, mostly for the fun, a bit to learn about Mastodon bots, and a bit because, in these times of censorship, filtering, lying DNS resolvers and so on, offering to the users a way to make DNS requests to the outside can be useful. This article is to document this project.

First, how to use it. Once you have a Fediverse account (for Mastodon, see https://joinmastodon.org/), you write to @DNSresolver@botsin.space. You just tell it the domain name you want to resolve. Here is an example, with the answer: fediverse-dns-bot-1.png

If you want, you can specify the DNS query type after the name (the defaut is A, for IPv4 adresses): fediverse-dns-bot-2.png

The bot replies with the same level of confidentiality as the query. So, if you want the request to be private, use the "direct" mode. Note that the bot itself is very indiscreet: it logs everything, and I read it. So, it will be only privacy against third parties.

And, yes IDN do work. This is 2018, we now know that not everyone on Earth use the latin alphabet: fediverse-dns-bot-3.png

Last, but not least, when the bot sees an IP address, it automatically does a "reverse" query: fediverse-dns-bot-4.png

If you are a command-line fan, you can use the madonctl tool to send the query to the bot:

% madonctl toot "@DNSresolver@botsin.space framapiaf.org"

You can even make a shell function:

# Function definition
dnsfediverse() {
    madonctl toot --visibility direct "@DNSresolver@botsin.space $1"
}

# Function usages
% dnsfediverse www.face-cachee-internet.fr 

% dnsfediverse societegenerale.com\ NS    

There is at least a second public bot using this code, @ResolverCN@mastodon.xyz, which uses a chinese DNS resolver so you can see a (part of) the chinese censorship. To do DNS when normal access is blocked or otherwise unavailable, you have other solutions. You can use DNS looking glasses, public DNS resolver over the Web, the Twitter bot @1111Resolver, email auto-responder resolver@lookup.email

Now, the implementation. (You can get all the files at https://framagit.org/bortzmeyer/mastodon-DNS-bot/.) Mastodon provides a documented API. (Note that it is the client-to-server API, and it is not standard in any way, unlike the ActivityPub protocol used for the server-to-server communication. Not all fediverse programs use this API, for instance GNU Social has a different one.) You can write your own client over the raw API but it is a bit harsh, so I wanted to use an existing library. There were two techniques to write a bot that I considered, madonctl with the shell and Mastodon.py with Python. I choosed the second one because a lof of nice people recommended it, and because madonctl required more text parsing, with the associated risks when you get data from unknown actors.

Mastodon.py has a very good documentation. I first create two files with the credentials to connect to the Mastodon instance of the bot. I choosed the Mastodon instance https://botsin.space because it is dedicated to bots. I created the account DNSresolver. Then, first file to create, DNSresolver_clientcred.secret is to register the application, with this Python code:

Mastodon.create_app(
     'DNSresolverapp',
     api_base_url = 'https://botsin.space/',
     to_file = 'DNSresolver_clientcred.secret'
)

Second file, DNSresolver_usercred.secret, is after you logged in:

mastodon = Mastodon(
    client_id = 'DNSresolver_clientcred.secret',
    api_base_url = 'https://botsin.space/'
)
mastodon.log_in(
    'the-email-address@the-domain',
    'the secret password',
    to_file = 'DNSresolver_usercred.secret'
)
    

Then we can connect to the instance of the bot and listen to incoming requests with the streaming API:

mastodon = Mastodon(
    client_id = 'DNSresolver_clientcred.secret',
    access_token = 'DNSresolver_usercred.secret',
    api_base_url = 'https://botsin.space')
listener = myListener()
mastodon.stream_user(listener)
    

And everything else is event-based. When an incoming request comes in, the program will "immediately" call listener. Use of the streaming API (instead of polling) makes the bot very responsive.

But what is listener? It has to be an instance of the class StreamListener from Mastodon.py, and to provide routines to act when a given event takes place. Here, I'm only interested in notifications (when people mention the bot in a message, a toot):

    
class myListener(StreamListener):
    def on_notification(self, notification):
       if notification['type'] == 'mention':
            # Parse the request, find out domain name and possibly query type     
            # Perform the DNS query
	    # Post the result on the fediverse

The routine on_notification will receive the toot as a dictionary in the parameter notification. The fields of this dictionary are documented.

For the first step, parsing the request, Mastodon unfortunately returns the content of the toot in HTML. We have to extract the text with lxml:

doc = html.document_fromstring(notification['status']['content'])
body = doc.text_content()

We can then get the parameters of the query with a regular expression (remember all the files are at https://framagit.org/bortzmeyer/mastodon-DNS-bot/).

Second thing to do, perform the actual DNS query. We use dnspython which is very simple to use, sending the request to the local resolver (an Unbound) with just one function call:

msg = self.resolver.query(qname, qtype)
for data in msg:
    answers = answers + str(data) + "\n"
  

Finally, we send the reply through the Mastodon API:

id = notification['status']['id']
visibility = notification['status']['visibility']
mastodon.status_post(answers, in_reply_to_id = id, 
                     visibility = visibility) 
  

We retrieve the visibility (public/private/etc) from the original message, and we mention the original identifier of the toot, to let Mastodon keep both query and reply in the same thread.

That's it, you now have a Mastodon bot! Of course, the real code is more complicated. You have to guard against code injection (for instance, using a call to the shell to call dig for DNS resolution would be dangerous if there were semicolons in the domain name), that's why we keep only the text from the HTML. And, of course, because the sender of the original message can be wrong (or malicious), you have to consider many possible failures and guard accordingly. The exception handlers are therefore longer than the "real" code. Remember the Internet is a jungle!

One last problem: when you open a streaming connection to Mastodon, sometimes the network is down, or the server restarted, or closed the connection violently, and you won't be notified. (A bit like a TCP connection with no traffic: you have no way of knowing if it is broken or simply idle, besides sending a message.) The streaming API solves this problem by sending "heartbeats" every fifteen seconds. You need to handle these heartbeats, and do something if they stop arriving. Here, we record the time of the last heartbeat in a file:

def handle_heartbeat(self):
        self.heartbeat_file = open(self.hb_filename, 'w')
        print(time.time(), file=self.heartbeat_file)
        self.heartbeat_file.close()
  

We run the Mastodon listener in a separate process, with Python's multiprocessing module:

proc = multiprocessing.Process(target=driver,
    args=(log, tmp_hb_filename[1],tmp_pid_filename[1]))
proc.start()
  

And we have a timer that checks the timestamps written in the heartbeats file, and kills the listener process if the last heartbeat is too old:

h = open(tmp_hb_filename[1], 'r')
last_heartbeat = float(h.read(128))
if time.time() - last_heartbeat > MAXIMUM_HEARTBEAT_DELAY:
                    log.error("No more heartbeats, kill %s" % proc.pid)
                    proc.terminate()
  

We use multiprocessing and not threading because threads in Python have some annoying limitations. For instance, there is no way to kill them (no equivalent of the terminate() we use. Here is the log file when running with "debug" verbosity. Note the times:

2018-05-02 18:01:25,745 - DEBUG - HEARTBEAT
2018-05-02 18:01:40,746 - DEBUG - HEARTBEAT
2018-05-02 18:01:55,758 - DEBUG - HEARTBEAT
2018-05-02 18:02:10,757 - DEBUG - HEARTBEAT
2018-05-02 18:02:25,770 - DEBUG - HEARTBEAT
2018-05-02 18:02:40,769 - DEBUG - HEARTBEAT
2018-05-02 18:03:38,473 - ERROR - No more heartbeats, kill 28070
2018-05-02 18:03:38,482 - DEBUG - Done, it exited with code -15
2018-05-02 18:03:38,484 - DEBUG - Creating a new process
2018-05-02 18:03:38,659 - INFO - Driver/listener starts, PID 20396
2018-05-02 18:03:53,838 - DEBUG - HEARTBEAT
2018-05-02 18:04:08,837 - DEBUG - HEARTBEAT
  

The second possibility that I considered for writing the bot, but discarded, is the use of madonctl. It is a very powerful command-line tool. You can listen to the stream of toots:

  
% madonctl stream --command "command.sh"

Where command.sh is a program that will parse the message (the toot) and act. A more detailed example is:

%  madonctl stream --notifications-only --notification-types mentions --command "dosomething.sh"

where dosomething.sh has the content:

#!/bin/sh                                                                                                                      

echo "A notification !!!"
echo Args: $*
cat

echo ""

If you write to the account used by madonctl, the script dosomething.sh will display:

Command output: A notification !!!
Args:
- Notification ID: 3907
  Type: mention
  Timestamp: 2018-04-09 13:43:34.746 +0200 CEST
  - Account: (8) @bortzmeyer@mastodon.gougere.fr - S. Bortzmeyer  ✅
  - Status ID: 99829298934602015
    From: bortzmeyer@mastodon.gougere.fr (S. Bortzmeyer  ✅)
    Timestamp: 2018-04-09 13:43:34.704 +0200 CEST
    Contents: @DNSresolver Test 1
Test 2
    URL: https://mastodon.gougere.fr/@bortzmeyer/99829297476510997
  

You then have to parse it. For my DNS bot, it was not ideal, that's why I choosed Mastodon.py.

Other resources about bot implementation on the fediverse:

Thanks a lot to Alarig for the initial server (now down) and the good ideas.


L'article seul

À propos d'une tribune « Contre le numérique à l’école »

Première rédaction de cet article le 8 avril 2018


Dans Libération daté du 5 avril 2018, on a pu lire une tribune d'« un collectif d'enseignants » s'opposant vigoureusement au numérique à l'école. Quels étaient leurs arguments ?

Ils étaient variés. On y trouvait une critique de la pression qui pousse à acheter toujours plus d'équipements numériques, une inquiétude face aux conséquences « psychologiques et cognitives » qu'aurait le numérique, un reproche fait à Facebook de chercher délibérement à rendre ses utilisateurs « accros », un scepticisme vis-à-vis du numérique présenté comme solution magique à tous les problèmes…

On le voit dans cette liste, il y a plein d'arguments auxquels je suis sensible. En effet, les vendeurs comme Apple font tout leur possible pour vendre leurs produits, y compris avec la complicité de l'Éducation Nationale. En effet, Facebook joue un rôle très néfaste de plein de façons. Et, c'est sûr, le numérique ne résout pas tous les problèmes : on n'en finit pas avec l'échec scolaire juste en distribuant des tablettes, comme c'est pourtant souvent affirmé dans les discours ministériels. Mais le problème est que leur liste est vraiment fourre-tout.

Bien sûr, les vendeurs… vendent. Mais ils n'existent pas que dans le numérique ! Dans le domaine du livre, est-ce que Gallimard ou Hachette sont moins des entreprises capitalistes que Microsoft ou Samsung ? Les auteurs dénoncent « une boulimie consumériste » mais elle est une conséquence du capitalisme, pas une spécificité du numérique.

Ensuite, je ne suis pas moi-même un utilisateur de Facebook donc je ne vais pas défendre ce service, mais, ici, quel rapport avec le numérique ? De même que le numérique ne se réduit pas à l'Internet, l'Internet ne se réduit pas au Web, et le Web ne se réduit pas à Facebook. Ces nuances sont trop compliquées ? Pas pour des enseignants, j'espère, qui doivent justement apprendre aux enfants des points délicats et subtils. Commencer par leur expliquer que le Web est bien plus riche que Facebook et offre bien d'autres possibilités serait un bon départ.

Et l'affirmation comme quoi le numérique n'est pas la solution miracle ? Clairement, il ne l'est pas. Mais cela veut-il dire qu'il est complètement inutile, et peut être ignoré complètement ? Une bonne partie du corps enseignant avait déjà suivi ce raisonnement pour le cinéma, la bande dessinée et la télévision, avec le résultat qu'on sait. On retrouve ici une démarche classique des conservateurs : protester contre chaque nouveauté, refuser de considérer son utilisation, puis s'y mettre quand cette nouveauté a été remplacée par une autre. J'ai entendu lors d'une réunion au lycée un enseignant d'économie se plaindre de ce que les enfants ne regardaient pas assez la télévision, « par la faute d'Internet » et ne connaissaient pas assez l'actualité. Quand on sait quelle fut la réaction de ces conservateurs à la télévision, on ne peut qu'être assez étonné de cet amour tardif pour le petit écran.

Plus grave, la tribune publiée dans Libération reprend une légende urbaine, celle comme quoi « les cadres de la Silicon Valley [protègent] leurs propres enfants des écrans, dans et en dehors de l’école ». Cette légende a pourtant été réfutée plusieurs fois (voir par exemple l'article d'Emmanuel Davidenkoff ou bien la chronique de Xavier de la Porte). Mais elle continue à circuler, sans tenir compte des faits. C'est inquiétant pour des enseignants qui ont à former l'esprit critique des enfants, à leur apprendre à se méfier des fake news, à leur montrer l'indispensable questionnement devant les légendes répétées en boucle.

Pourtant, il y aurait des tas de choses à critiquer dans l'Éducation Nationale, à propos du numérique. C'est le cas par exemple du scandaleux accord entre Microsoft et l'Éducation Nationale (accord ancien mais régulièrement reconduit fièrement par le gouvernement), qui sous-traite l'éducation au numérique à une entreprise de logiciels privateurs. Mais les auteurs n'en parlent pas. Je soupçonne que c'est parce qu'ils ne suivent pas tellement ce qui se passe dans le monde du numérique et de l'éducation…

J'aurais bien envoyé ce texte au « collectif d'enseignants » mais je n'ai pas trouvé leur adresse.


L'article seul

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

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