Je suis Charlie

Autres trucs

Accueil

Seulement les RFC

Seulement les fiches de lecture

Mon livre « Cyberstructure »

Ève

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


RFC 8548: Cryptographic Protection of TCP Streams (tcpcrypt)

Date de publication du RFC : Mai 2019
Auteur(s) du RFC : A. Bittau (Google), D. Giffin (Stanford University), M. Handley (University College London), D. Mazieres (Stanford University), Q. Slack (Sourcegraph), E. Smith (Kestrel Institute)
Expérimental
Réalisé dans le cadre du groupe de travail IETF tcpinc
Première rédaction de cet article le 23 mai 2019


Aujourd'hui, il n'est plus acceptable d'avoir des communications non chiffrées. États, entreprises et délinquants surveillent massivement les réseaux publics et toute communication effectuée en clair peut être espionnée, voire modifiée. Il est donc nécessaire de continuer les efforts pour chiffrer ce qui ne l'est pas encore. Ce RFC décrit un mécanisme expérimental pour TCP, nommé tcpcrypt, permettant de chiffrer les communications sans participation de l'application située au-dessus, sans authentification obligatoire. (Mais le projet semble mal en point donc je ne suis pas optimiste quant à son déploiement.)

La section 2 du RFC est le cahier des charges de « tcpcrypt », ce nouveau mécanisme de protection de TCP :

  • Pouvoir être mis en œuvre conjointement à TCP. Cela implique de pouvoir tourner dans le noyau, où on ne peut pas charger des bibliothèques comme OpenSSL. Et certaines piles TCP tournent sur des machines contraintes (Internet des Objets), donc le protocole doit être léger.
  • Ne pas trop augmenter la latence lors de la négociation cryptographique.
  • Tolérer certains intermédiaires qui se permettent de modifier l'en-tête TCP.
  • Ne pas faire de liaison avec l'adresse IP : une session tcpcrypt doit pouvoir reprendre si l'adresse IP a changé.

tcpcrypt est très différent des classiques TLS et SSH. Il est conçu pour ne pas impliquer l'application, qui peut ignorer qu'on chiffre dans les couches inférieures. tcpcrypt est prévu pour être une solution simple, ne nécessitant pas de modifier protocoles ou applications, changeant le moins de chose possible pour être déployable. Il est également intéressant de voir le « non-cahier des charges », ce qui n'est pas obligatoire dans tcpcrypt :

  • Aucune authentification n'est faite par tcpcrypt,
  • En cas de problème, on se replie sur du TCP non sécurisé.

Ces deux points rendent tcpcrypt vulnérable aux attaquants actifs.

Pendant la longue et douloureuse gestation de ce protocole, TLS avait été envisagé comme alternative. Après tout, pourquoi inventer un nouveau protocole de cryptographie, activité longue et délicate (les failles de sécurité sont vite arrivées) ? Il y avait deux propositions sur la table à l'IETF, le futur tcpcrypt, et une solution fondée sur TLS. C'est pour essayer de faire fonctionner les deux solutions que la négociation des paramètres avait été traitée à part (option ENO, RFC 8547). Mais, depuis, la proposition TLS a été de facto abandonnée, en partie parce que la communauté TLS était occupée par le travail sur la version 1.3.

tcpcrypt s'appuie sur l'option TCP ENO (Encryption Negotiation Option) pour la négociation de l'utilisation du chiffrement. Ce RFC 8548 décrit comment chiffrer, une fois les paramètres négociés.

La section 3 décrit le protocole en détail. Je ne vais pas la reprendre ici (la cryptographie n'est pas mon point fort). On est dans le classique, de toute façon, avec cryptographie asymétrique pour se mettre d'accord sur une clé et cryptographie symétrique pour chiffrer ; tcpcrypt utilise trois types d'algorithmes cryptographiques :

  • Un mécanisme de négociation de clé pour, à partir d'une clé publique temporaire, se mettre d'accord sur un secret partagé (qui servira, après traitement, à chiffrer la session),
  • une fonction d'extraction pour tirer de ce secret partagé une clé,
  • une fonction pseudo-aléatoire pour tirer de cette clé les clés de chiffrement symétrique.

La fonction d'extraction et la fonction pseudo-aléatoire sont celles de HKDF (RFC 5869), elle-même fondée sur HMAC (RFC 2104). Une fois qu'on a la clé, on chiffre avec un algorithme de chiffrement intègre.

Comme vous avez vu, les clés publiques utilisées dans le protocole tcpcrypt sont temporaires, jamais écrites sur disque et renouvellées fréquemment. Ce ne sont pas des clés permanentes, indiquant l'identité de la machine comme c'est le cas poour SSH. tcpcrypt n'authentifie pas la machine en face (la section 8 détaille ce point).

La négociation du protocole, pour que les deux parties qui font du TCP ensemble se mettent d'accord pour chiffrer, est faite avec l'option TCP ENO (Encryption Negotiation Option), décrite dans le RFC 8547. La négociation complète peut nécessiter un aller-retour supplémentaire par rapport à du TCP habituel, ce qui augmente la latence d'établissement de connexion. Un mécanisme de reprise des sessions permet de se passer de négociation, si les deux machines ont déjà communiqué, et gardé l'information nécessaire.

Une fois la négociation terminée, et le chiffrement en route, tcpcrypt génère (de manière imprévisible, par exemple en condensant les paramètres de la session avec un secret) un session ID, qui identifie de manière unique cette session tcpcrypt particulière. Ce session ID est mis à la disposition de l'application via une API, qui reste à définir et l'application peut, si elle le souhaite, ajouter son mécanisme d'authentification et lier une authentification réussie au session ID. Dans ce cas, et dans ce cas seulement, tcpcrypt est protégé contre l'Homme du Milieu.

Notez que seule la charge utile TCP est chiffrée, et donc protégée. L'en-tête TCP ne l'est pas (pour pouvoir passer à travers des boitiers intermédiaires qui tripotent cet en-tête, et tcpcrypt ne protège donc pas contre certaines attaques comme les faux paquets RST (terminaison de connexion, les détails figurent en section 8). Toutefois, certains champs de l'en-tête (mais pas RST) sont inclus dans la partie chiffrée (cf. section 4.2). C'est par exemple le cas de FIN, pour éviter qu'une troncation des données passe inaperçue.

L'option TCP ENO (RFC 8547) crée le concept de TEP (TCP Encryption Protocol). Un TEP est un mécanisme cryptographique particulier choisi par les deux machines qui communiquent. Chaque utilisation de l'option ENO doit spécifier son ou ses TEP. Pour tcpcrypt, c'est fait dans la section 7 de notre RFC, et ces TEP sont placés dans un registre IANA. On y trouve, par exemple, TCPCRYPT_ECDHE_Curve25519 (le seul qui soit obligatoire pour toutes les mises en œuvre de tcpcrypt, cf. RFC 7696) qui veut dire « création des clés avec du Diffie-Hellman sur courbes elliptiques avec la courbe Curve25519 ». Pour le chiffrement lui-même, on a vu qu'il ne fallait utiliser que du chiffrement intègre, et le seul algorithme obligatoire pour tcpcrypt est AEAD_AES_128_GCM (« AES en mode GCM »). Les autres sont également dans un registre IANA.

Le but de tcpcrypt est la sécurité donc la section 8, consacrée à l'analyse de la sécurité du protocole, est très détaillée. D'abord, tcpcrypt hérite des propriétés de sécurité de l'option ENO (RFC 8547). Ainsi, il ne protège pas contre un attaquant actif, qui peut s'insérer dans le réseau, intercepter les paquets, les modifier, etc. Un tel attaquant peut retirer l'option ENO des paquets et il n'y a alors plus grand'chose à faire (à part peut-être épingler la connaissance du fait qu'une machine donnée parlait tcpcrypt la dernière fois qu'on a échangé, et qu'il est bizarre qu'elle ne le fasse plus ?) Si l'application a son propre mécanisme d'autentification, situé au-dessus de tcpcrypt, et qui lie l'authentification au session ID, alors, on est protégé contre les attaques actives. Sinon, seul l'attaquant passif (qui ne fait qu'observer) est bloqué par tcpcrypt. Une analyse plus détaillée figure dans l'article fondateur du projet tcpcrypt, « The case for ubiquitous transport-level encryption », par Bittau, A., Hamburg, M., Handley, M., Mazieres, D., et D. Boneh.. tcpcrypt fait donc du chiffrement opportuniste (RFC 7435).

tcpcrypt ne protège pas la plus grande partie des en-têtes TCP. Donc une attaque active comme l'injection de faux RST (RFC 793, et aussi RFC 5961) reste possible.

Comme la plupart des techniques cryptographiques, tcpcrypt dépend fortement de la qualité du générateur de nombres pseudo-aléatoires utilisé. C'est d'autant plus crucial qu'un des cas d'usage prévus pour tcpcrypt est les objets contraints, disposant de ressources matérielles insuffisantes. Bref, il faut relire le RFC 4086 quand on met en œuvre tcpcrypt. Et ne pas envoyer l'option ENO avant d'être sûr que le générateur a acquis assez d'entropie.

On a dit que tcpcrypt ne protégeait pas les « métadonnées » de la connexion TCP. Ainsi, les keepalives (RFC 1122) ne sont pas cryptographiquement vérifiables. Une solution alternative est le mécanisme de renouvellement des clés de tcpcrypt, décrit dans la section 3.9 de notre RFC.

Ce RFC 8548 est marqué comme « Expérimental ». On n'a en effet que peu de recul sur l'utilisation massive de tcpcrypt. La section 9 liste les points qui vont devoir être surveillés pendant cette phase expérimentale : que deux machines puissent toujours se connecter, même en présence de boitiers intermédiaires bogués et agressifs (tcpcrypt va certainement gêner la DPI, c'est son but, et cela peut offenser certains boitiers noirs), et que l'implémentation dans le noyau ne soulève pas de problèmes insurmontables (comme le chiffrement change la taille des données, le mécanisme de gestion des tampons va devoir s'adapter et, dans le noyau, la gestion de la mémoire n'est pas de la tarte). C'est d'autant plus important qu'il semble qu'après l'intérêt initial, l'élan en faveur de ce nouveau protocole se soit sérieusement refroidi (pas de commit depuis des années dans le dépôt initial).

Et les mises en œuvre de tcpcrypt (et de l'option ENO, qui lui est nécessaire) ? Outre celle de référence citée plus haut, qui est en espace utilisateur, et qui met en œuvre ENO et tcpcrypt, il y a plusieurs projets, donc aucun ne semble prêt pour la production :

Il y avait un site « officiel » pour le projet, http://tcpcrypt.org/ mais qui semble désormais cassé.


Téléchargez le RFC 8548


L'article seul

RFC 8548: Cryptographic Protection of TCP Streams (tcpcrypt)

Date de publication du RFC : Mai 2019
Auteur(s) du RFC : A. Bittau (Google), D. Giffin (Stanford University), M. Handley (University College London), D. Mazieres (Stanford University), Q. Slack (Sourcegraph), E. Smith (Kestrel Institute)
Expérimental
Réalisé dans le cadre du groupe de travail IETF tcpinc
Première rédaction de cet article le 23 mai 2019


Aujourd'hui, il n'est plus acceptable d'avoir des communications non chiffrées. États, entreprises et délinquants surveillent massivement les réseaux publics et toute communication effectuée en clair peut être espionnée, voire modifiée. Il est donc nécessaire de continuer les efforts pour chiffrer ce qui ne l'est pas encore. Ce RFC décrit un mécanisme expérimental pour TCP, nommé tcpcrypt, permettant de chiffrer les communications sans participation de l'application située au-dessus, sans authentification obligatoire. (Mais le projet semble mal en point donc je ne suis pas optimiste quant à son déploiement.)

La section 2 du RFC est le cahier des charges de « tcpcrypt », ce nouveau mécanisme de protection de TCP :

  • Pouvoir être mis en œuvre conjointement à TCP. Cela implique de pouvoir tourner dans le noyau, où on ne peut pas charger des bibliothèques comme OpenSSL. Et certaines piles TCP tournent sur des machines contraintes (Internet des Objets), donc le protocole doit être léger.
  • Ne pas trop augmenter la latence lors de la négociation cryptographique.
  • Tolérer certains intermédiaires qui se permettent de modifier l'en-tête TCP.
  • Ne pas faire de liaison avec l'adresse IP : une session tcpcrypt doit pouvoir reprendre si l'adresse IP a changé.

tcpcrypt est très différent des classiques TLS et SSH. Il est conçu pour ne pas impliquer l'application, qui peut ignorer qu'on chiffre dans les couches inférieures. tcpcrypt est prévu pour être une solution simple, ne nécessitant pas de modifier protocoles ou applications, changeant le moins de chose possible pour être déployable. Il est également intéressant de voir le « non-cahier des charges », ce qui n'est pas obligatoire dans tcpcrypt :

  • Aucune authentification n'est faite par tcpcrypt,
  • En cas de problème, on se replie sur du TCP non sécurisé.

Ces deux points rendent tcpcrypt vulnérable aux attaquants actifs.

Pendant la longue et douloureuse gestation de ce protocole, TLS avait été envisagé comme alternative. Après tout, pourquoi inventer un nouveau protocole de cryptographie, activité longue et délicate (les failles de sécurité sont vite arrivées) ? Il y avait deux propositions sur la table à l'IETF, le futur tcpcrypt, et une solution fondée sur TLS. C'est pour essayer de faire fonctionner les deux solutions que la négociation des paramètres avait été traitée à part (option ENO, RFC 8547). Mais, depuis, la proposition TLS a été de facto abandonnée, en partie parce que la communauté TLS était occupée par le travail sur la version 1.3.

tcpcrypt s'appuie sur l'option TCP ENO (Encryption Negotiation Option) pour la négociation de l'utilisation du chiffrement. Ce RFC 8548 décrit comment chiffrer, une fois les paramètres négociés.

La section 3 décrit le protocole en détail. Je ne vais pas la reprendre ici (la cryptographie n'est pas mon point fort). On est dans le classique, de toute façon, avec cryptographie asymétrique pour se mettre d'accord sur une clé et cryptographie symétrique pour chiffrer ; tcpcrypt utilise trois types d'algorithmes cryptographiques :

  • Un mécanisme de négociation de clé pour, à partir d'une clé publique temporaire, se mettre d'accord sur un secret partagé (qui servira, après traitement, à chiffrer la session),
  • une fonction d'extraction pour tirer de ce secret partagé une clé,
  • une fonction pseudo-aléatoire pour tirer de cette clé les clés de chiffrement symétrique.

La fonction d'extraction et la fonction pseudo-aléatoire sont celles de HKDF (RFC 5869), elle-même fondée sur HMAC (RFC 2104). Une fois qu'on a la clé, on chiffre avec un algorithme de chiffrement intègre.

Comme vous avez vu, les clés publiques utilisées dans le protocole tcpcrypt sont temporaires, jamais écrites sur disque et renouvellées fréquemment. Ce ne sont pas des clés permanentes, indiquant l'identité de la machine comme c'est le cas poour SSH. tcpcrypt n'authentifie pas la machine en face (la section 8 détaille ce point).

La négociation du protocole, pour que les deux parties qui font du TCP ensemble se mettent d'accord pour chiffrer, est faite avec l'option TCP ENO (Encryption Negotiation Option), décrite dans le RFC 8547. La négociation complète peut nécessiter un aller-retour supplémentaire par rapport à du TCP habituel, ce qui augmente la latence d'établissement de connexion. Un mécanisme de reprise des sessions permet de se passer de négociation, si les deux machines ont déjà communiqué, et gardé l'information nécessaire.

Une fois la négociation terminée, et le chiffrement en route, tcpcrypt génère (de manière imprévisible, par exemple en condensant les paramètres de la session avec un secret) un session ID, qui identifie de manière unique cette session tcpcrypt particulière. Ce session ID est mis à la disposition de l'application via une API, qui reste à définir et l'application peut, si elle le souhaite, ajouter son mécanisme d'authentification et lier une authentification réussie au session ID. Dans ce cas, et dans ce cas seulement, tcpcrypt est protégé contre l'Homme du Milieu.

Notez que seule la charge utile TCP est chiffrée, et donc protégée. L'en-tête TCP ne l'est pas (pour pouvoir passer à travers des boitiers intermédiaires qui tripotent cet en-tête, et tcpcrypt ne protège donc pas contre certaines attaques comme les faux paquets RST (terminaison de connexion, les détails figurent en section 8). Toutefois, certains champs de l'en-tête (mais pas RST) sont inclus dans la partie chiffrée (cf. section 4.2). C'est par exemple le cas de FIN, pour éviter qu'une troncation des données passe inaperçue.

L'option TCP ENO (RFC 8547) crée le concept de TEP (TCP Encryption Protocol). Un TEP est un mécanisme cryptographique particulier choisi par les deux machines qui communiquent. Chaque utilisation de l'option ENO doit spécifier son ou ses TEP. Pour tcpcrypt, c'est fait dans la section 7 de notre RFC, et ces TEP sont placés dans un registre IANA. On y trouve, par exemple, TCPCRYPT_ECDHE_Curve25519 (le seul qui soit obligatoire pour toutes les mises en œuvre de tcpcrypt, cf. RFC 7696) qui veut dire « création des clés avec du Diffie-Hellman sur courbes elliptiques avec la courbe Curve25519 ». Pour le chiffrement lui-même, on a vu qu'il ne fallait utiliser que du chiffrement intègre, et le seul algorithme obligatoire pour tcpcrypt est AEAD_AES_128_GCM (« AES en mode GCM »). Les autres sont également dans un registre IANA.

Le but de tcpcrypt est la sécurité donc la section 8, consacrée à l'analyse de la sécurité du protocole, est très détaillée. D'abord, tcpcrypt hérite des propriétés de sécurité de l'option ENO (RFC 8547). Ainsi, il ne protège pas contre un attaquant actif, qui peut s'insérer dans le réseau, intercepter les paquets, les modifier, etc. Un tel attaquant peut retirer l'option ENO des paquets et il n'y a alors plus grand'chose à faire (à part peut-être épingler la connaissance du fait qu'une machine donnée parlait tcpcrypt la dernière fois qu'on a échangé, et qu'il est bizarre qu'elle ne le fasse plus ?) Si l'application a son propre mécanisme d'autentification, situé au-dessus de tcpcrypt, et qui lie l'authentification au session ID, alors, on est protégé contre les attaques actives. Sinon, seul l'attaquant passif (qui ne fait qu'observer) est bloqué par tcpcrypt. Une analyse plus détaillée figure dans l'article fondateur du projet tcpcrypt, « The case for ubiquitous transport-level encryption », par Bittau, A., Hamburg, M., Handley, M., Mazieres, D., et D. Boneh.. tcpcrypt fait donc du chiffrement opportuniste (RFC 7435).

tcpcrypt ne protège pas la plus grande partie des en-têtes TCP. Donc une attaque active comme l'injection de faux RST (RFC 793, et aussi RFC 5961) reste possible.

Comme la plupart des techniques cryptographiques, tcpcrypt dépend fortement de la qualité du générateur de nombres pseudo-aléatoires utilisé. C'est d'autant plus crucial qu'un des cas d'usage prévus pour tcpcrypt est les objets contraints, disposant de ressources matérielles insuffisantes. Bref, il faut relire le RFC 4086 quand on met en œuvre tcpcrypt. Et ne pas envoyer l'option ENO avant d'être sûr que le générateur a acquis assez d'entropie.

On a dit que tcpcrypt ne protégeait pas les « métadonnées » de la connexion TCP. Ainsi, les keepalives (RFC 1122) ne sont pas cryptographiquement vérifiables. Une solution alternative est le mécanisme de renouvellement des clés de tcpcrypt, décrit dans la section 3.9 de notre RFC.

Ce RFC 8548 est marqué comme « Expérimental ». On n'a en effet que peu de recul sur l'utilisation massive de tcpcrypt. La section 9 liste les points qui vont devoir être surveillés pendant cette phase expérimentale : que deux machines puissent toujours se connecter, même en présence de boitiers intermédiaires bogués et agressifs (tcpcrypt va certainement gêner la DPI, c'est son but, et cela peut offenser certains boitiers noirs), et que l'implémentation dans le noyau ne soulève pas de problèmes insurmontables (comme le chiffrement change la taille des données, le mécanisme de gestion des tampons va devoir s'adapter et, dans le noyau, la gestion de la mémoire n'est pas de la tarte). C'est d'autant plus important qu'il semble qu'après l'intérêt initial, l'élan en faveur de ce nouveau protocole se soit sérieusement refroidi (pas de commit depuis des années dans le dépôt initial).

Et les mises en œuvre de tcpcrypt (et de l'option ENO, qui lui est nécessaire) ? Outre celle de référence citée plus haut, qui est en espace utilisateur, et qui met en œuvre ENO et tcpcrypt, il y a plusieurs projets, donc aucun ne semble prêt pour la production :

Il y avait un site « officiel » pour le projet, http://tcpcrypt.org/ mais qui semble désormais cassé.


Téléchargez le RFC 8548


L'article seul

RFC 8547: TCP-ENO: Encryption Negotiation Option

Date de publication du RFC : Mai 2019
Auteur(s) du RFC : A. Bittau (Google), D. Giffin (Stanford University), M. Handley (University College London), D. Mazieres (Stanford University), E. Smith (Kestrel Institute)
Expérimental
Réalisé dans le cadre du groupe de travail IETF tcpinc
Première rédaction de cet article le 23 mai 2019


Ce RFC, tout juste sorti des presses, décrit une extension de TCP nommée ENO, pour Encryption Negotiation Option. Elle permet d'indiquer qu'on souhaite chiffrer la communication avec son partenaire TCP, et de négocier les options. Elle sert au protocole tcpcrypt, décrit, lui, dans le RFC 8548.

Malgré le caractère massif de la surveillance exercée sur les communications Internet, il y a encore des connexions TCP dont le contenu n'est pas chiffré. Cela peut être parce que le protocole applicatif ne fournit pas de moyen (genre une commande STARTTLS) pour indiquer le passage en mode chiffré, ou simplement parce que les applications ne sont plus guère maintenues et que personne n'a envie de faire le travail d'utilisation, par exemple, de TLS. Pensons à whois (RFC 3912), par exemple. La nouvelle option ENO va permettre de chiffrer ces protocoles et ces applications, en agissant uniquement dans la couche transport, au niveau TCP.

Le but de cette nouvelle option TCP est de permettre aux deux pairs TCP de se mettre d'accord sur le fait d'utiliser le chiffrement, et quel chiffrement. Ensuite, tcpcrypt (RFC 8548) ou un autre protocole utilisera cet accord pour chiffrer la communication. En cas de désaccord, on se rabattra sur du TCP « normal », en clair.

Le gros de la spécification est dans la section 4 du RFC. ENO est une option TCP (cf. RFC 793, section 3.1). Elle porte le numéro 69 (qui avait déjà été utilisé par des protocoles analogues mais qui étaient restés encore plus expérimentaux) et figure dans le registre des options TCP. (0x454E a été gardé pour des expériences, cf. RFC 6994.) Le fait d'envoyer cette option indique qu'on veut du chiffrement. Chaque possibilité de chiffrement, les TEP (TCP Encryption Protocol) est dans une sous-option de l'option ENO (section 4.1 pour les détails de format). Si la négociation a été un succès, un TEP est choisi. Les TEP sont décrits dans d'autres RFC (par exemple, le RFC 8548, sur tcpcrypt, en décrit quatre). Les TEP sont enregistrés à l'IANA.

À noter que TCP est symétrique : il n'y a pas de « client » ou de « serveur », les deux pairs peuvent entamer la connexion simultanément (BGP, par exemple, utilise beaucoup cette possibilité). ENO, par contre, voit une asymétrie : les deux machines qui communiquent sont nommées A et B et ont des rôles différents.

A priori, c'est A qui enverra un SYN (message de demande d'établissement de connexion). Ce SYN inclura l'option ENO, et ce sera de même pour les trois messages de la triple poignée de mains TCP. La section 6 du RFC donne quelques exemples. Ainsi :

  • A > B : SYN avec ENO (X, Y) - TEP X et Y,
  • A < B : SYN+ACK avec ENO (Y)
  • A > B : ACK avec ENO vide

Cet échange mènera à un chiffrement fait avec le TEP Y, le seul que A et B avaient en commun. Par contre, si B est un vieux TCP qui ne connait pas ENO :

  • A > B : SYN avec ENO (X, Y) - TEP X et Y,
  • A < B : SYN+ACK sans ENO
  • A > B : ACK sans ENO

Ne voyant pas de ENO dans le SYN+ACK, A renonce au chiffrement. La connexion TCP ne sera pas protégée.

Et les TEP (TCP Encryption Protocol), qu'est-ce qu'ils doivent définir ? La section 5 détaille les exigences pour ces protocols. Notamment :

  • Ils doivent chiffrer (évidemment) avec un algorithme de chiffrement intègre (cf. RFC 5116),
  • définir un session ID, un identificateur de session unique et imprévisible (pour les applications qui souhaiteraient faire leur authentification et la lier à une session particulière),
  • ne pas accepter d'algorithmes de chiffrement trop faibles, ou, bien sûr, nuls (cela parait drôle mais certaines protocoles autorisaient explicitement un chiffrement sans effet),
  • fournir de la confidentialité persistante.

Si, à ce stade, vous vous posez des questions sur les choix faits par les concepteurs d'ENO, et que vous vous demandez pourquoi diable ont-ils décidé ceci ou cela, il est temps de lire la section 8, qui explique certains choix de conception. D'abord, une décision importante était qu'en cas de problème lors de la négociation, la connexion devait se replier sur du TCP classique, non chiffré, et surtout ne pas échouer. En effet, si un problème de négociation empêchait la connexion de s'établir, personne n'essayerait d'utiliser ENO. Donc, si on n'a pas d'option ENO dans les paquets qu'on reçoit, on n'insiste pas, on repasse en TCP classique. Et ceci, que les options n'aient jamais été mises, ou bien qu'elles aient été retirées par un intermédiaire trop zélé. On trouve de tout dans ces machines intermédiaires, y compris les comportements les plus délirants. Le RFC note ainsi que certains répartiteurs de charge renvoient à l'expéditeur les options TCP inconnues. L'émetteur pourrait alors croire à tort que son correspondant accepte ENO même quand ce n'est pas vrai. Un bit nommé b, mis à 0 par la machine A et à 1 par la machine B, permet de détecter ce problème, et de ne pas tenter de chiffrer avec un correspondant qui ne sait pas faire.

Cette asymétrie (une machine met le bit b à 1 mais pas l'autre) est un peu ennuyeuse, car TCP est normalement symétrique (deux machines peuvent participer à une ouverture simultanée de connexion, cf. RFC 793, section 3.4). Mais aucune meilleure solution n'a été trouvée, d'autant plus qu'une machine ne sait qu'il y a eu ouverture simultanée qu'après avoir envoyé son SYN (et si le message SYN de l'autre machine est perdu, elle ne saura jamais qu'une ouverture simultanée a été tentée).

Les protocoles utilisant ENO, comme tcpcrypt, sont conçus pour fonctionner sans la participation de l'application. Mais si celle-ci le souhaite, elle peut s'informer de l'état de sécurisation de la connexion TCP, par exemple pour débrayer un chiffrement au niveau applicatif, qui n'est plus nécessaire. Le bit a dans l'option ENO sert à cela. Mis à 1 par une application, il sert à informer l'application en face qu'on peut tenir compte du chiffrement, par exemple pour activer des services qui ont besoin d'une connexion sécurisée. (Notez qu'il n'existe pas d'API standard pour lire et modifier le bit a, ce qui limite les possibilités.)

La section 7 de notre RFC explique quelques développements futurs qui pourraient avoir lieu si des améliorations futures à TCP se répandent. Ainsi, si de nouvelles API, plus perfectionnées que celles du RFC 3493, permettent à TCP de connaitre non seulement l'adresse IP de la machine où on veut se connecter mais également son nom, on pourrait imaginer une authentification fondée sur le nom, par exemple avec DANE (RFC 6394). On pourrait aussi imaginer qu'ENO permette de sélectionner et de démarrer TLS même sans que l'application soit au courant.

Dans l'Internet très ossifié d'aujourd'hui, il est difficile de déployer quelque chose de nouveau, comme l'option ENO (d'où le statut expérimental de ce RFC.) On risque toujours de tomber sur un intermédiaire qui se croit autorisé à modifier ou jeter des paquets dont la tête ne lui revient pas. La section 9 du RFC analyse deux risques :

  • Le rique de repasser en TCP classique, non chiffré, par exemple si un intermédiaire supprime l'option ENO,
  • le risque de ne pas pouvoir se connecter du tout, par exemple si un intermédiaire jette les paquets contenant l'option ENO.

Le premier risque n'est pas trop sérieux, ENO était prévu pour du déploiement incrémental, de toute façon (on ne peut pas espérer que toutes les machines adoptent ENO en même temps.) Le deuxième est plus grave et, s'il s'avère trop commun, il faudra des heuristiques du genre « si pas de réponse en N millisecones, réessayer sans ENO ».

Outre ces risques, il est toujours possible, lorsqu'on touche à un protocole aussi crucial que TCP, que d'autres choses aillent mal, et il est donc nécessaire d'expérimenter. Il y a aussi des inconnues du genre « les applications vont-elles tirer profit d'ENO ? » (ce qui n'est pas nécessaire mais pourrait être utile).

La section 10 du RFC étudie les questions de sécurité soulevées par ENO. Comme ENO vise à permettre, entre autres, le chiffrement opportuniste (on chiffre si on peut, sinon on passe en clair, et on n'impose pas d'authentification, cf. RFC 7435), il faut être bien conscient des limites de ce modèle. Le chiffrement opportuniste protège bien contre un surveillant purement passif, mais pas contre un attaquant actif qui pourrait, par exemple, supprimer toutes les options ENO des paquets TCP, ou bien se mettre en position de terminaison TCP, avant de relayer vers le vrai destinataire, agissant ainsi en homme du milieu. Il ne faudrait donc pas prétendre à l'utilisateur que sa connexion est sûre.

Une solution est l'authentification, et c'est bien à cela que sert le session ID. Si l'application peut authentifier, elle doit lier cette authentification au session ID, pour être bien sûr qu'un attaquant ne va pas profiter d'une authentification réussie dans une session pour abuser d'une autre. Par exemple, si l'authentification est faite par une méthode analogue à celle du RFC 7616, le session ID peut être ajouté aux éléments qui seront condensés. Et si la méthode d'authentification ressemble à SCRAM (RFC 5802), le session ID peut être utilisé comme channel binding.

ENO n'est pas lié à un algorithme cryptographique particulier, en application du principe d'agilité (RFC 7696). Mais cela implique qu'un algorithme faible peut affaiblir la sécurité de tout le système. Les mises en œuvre d'ENO doivent donc faire attention à ne pas accepter des algorithmes cryprographiques faibles.

Pour les mises en œuvre d'ENO, voir la fin de mon article sur le RFC 8548 ; pour l'instant, ce sont les mêmes que celles de tcpcrypt.


Téléchargez le RFC 8547


L'article seul

RFC 8589: The 'leaptofrogans' URI Scheme

Date de publication du RFC : Mai 2019
Auteur(s) du RFC : A. Tamas (OP3FT), B. Phister (OP3FT), J-E. Rodriguez (OP3FT)
Pour information
Première rédaction de cet article le 23 mai 2019


Ce nouveau RFC documente un nouveau plan d'URI, leaptofrogans, qui permettra de construire des URI pour le système Frogans, comme par exemple leaptofrogans:example*test.

Le système Frogans est un système, conçu il y a vingt ans, de publication de contenu sur l'Internet. Il ne semble pas avoir jamais décollé et je suis sceptique quant à ses chances. Mais l'enregistrement d'un plan d'URI (le plan est la première partie d'un URI, avant le deux-points, cf. RFC 3986) ne signifie pas approbation ou encouragement, il indique juste que les formes ont bien été respectées.

Le système Frogans (section 1 du RFC) contient plusieurs composants, un langage de description des « sites Frogans », des adresses Frogans qu'on reconnait à l'astérisque (comme par exemple example*test), un logiciel non-libre, le Frogans Player, un registre des adresses, une organisation qui pilote la technologie, etc.

Pourquoi un nouveau plan d'URI ? (Section 2.) L'idée est de permettre au navigateur, quand il voit un lien du genre <a href="leaptofrogans:example*test">Contenu intéressant</a> de lancer le Frogans Player lorsque ce lien est sélectionné par l'utilisateur.

Le nom un peu long du nouveau plan, leaptofrogans, a été choisi pour éviter des confusions avec les adresses Frogans, qui commencent souvent par frogans avant l'astérisque (section 3 du RFC pour les détails.)

Quelques détails de syntaxe maintenant (section 4). Les adresses Frogans peuvent utiliser tout Unicode. Il faut donc utiliser le nouveau plan leaptofrogans dans des IRI (RFC 3987) ou bien encoder avec les pour-cent. Ainsi, l'adresse Frogans 网络名*站名 sera l'IRI leaptofrogans:网络名*站名 ou bien l'URI leaptofrogans:%E7%BD%91%E7%BB%9C%E5%90%8D*%E7%AB%99%E5%90%8D.

Les procédures du RFC 7595 ayant été suivies, le plan leaptofrogans est désormais dans le registre IANA (enregistrement permanent, un enregistrement temporaire avait été envisagé à un moment).


Téléchargez le RFC 8589


L'article seul

RFC 8594: The Sunset HTTP Header Field

Date de publication du RFC : Mai 2019
Auteur(s) du RFC : E. Wilde
Pour information
Première rédaction de cet article le 22 mai 2019


Un nouvel en-tête HTTP (et un nouveau type de lien) fait son apparition avec ce RFC : Sunset: sert à indiquer la date où la ressource Web cessera probablement d'être servie. Le but est, lorsque le webmestre sait à l'avance qu'il retirera une ressource, de prévenir les utilisateurs.

Normalement, bien sûr, cela ne devrait pas arriver. Les URL doivent être stables. Mais dans certains cas, il peut y avoir une raison légitime de retirer une ressource qui avait été publiée sur le Web. Et, si on le sait à l'avance, c'est plus gentil si on prévient les utilisateurs qui accèdent à cette ressource. Donc, en pratique, ce nouvel en-tête servira peu mais il sera utile dans des cas précis. Par exemple (ce sont les cas cités par le RFC, personnellement, je ne les trouve pas tous pertinents) :

  • Certaines ressources sont par nature temporaires. Par exemple, une page correspondant à une commande en cours sur un site Web de commerce en ligne. (À mon avis, il vaudrait mieux qu'elle soit permanente, pour pouvoir accéder à des informations même une fois la commande exécutée.)
  • Lorsqu'une migration vers de nouveaux URL est envisagée dans le futur.
  • Lorsque la loi ou un réglement quelconque l'exige. Par exemple, le RGPD, comme les lois de protection des données personnelles qui l'ont précédé, exige la suppression de ces données lorsque la raison pour laquelle elles avaient été collectées n'est plus d'actualité. Si on les détruit au bout d'un mois, on peut annoncer cette suppression à l'avance.
  • Si la ressource fait partie d'une API, il est possible que l'API soit remplacée par une nouvelle version et que la date de retrait de l'ancienne soit connue à l'avance, permettant d'informer les utilisateurs. (Notez qu'une API comprend en général plusieurs ressources, donc plusieurs URL. L'en-tête Sunset: ne permet pas de traiter ce cas, cf. section 5 du RFC, mais le type de lien sunset permet d'indiquer une page Web documentant l'API et ses changements.)

Pour ces usages, ce RFC introduit (section 3) donc l'en-tête HTTP Sunset: (coucher de soleil). Il contient une seule valeur, la date et l'heure de la suppression, au format HTTP classique de la section 7.1.1.1 du RFC 7231. Par exemple, pour indiquer qu'une ressource disparait à la fin de cette année (celle de parution du RFC) :

Sunset: Tue, 31 Dec 2019 23:59:59 GMT
    

Et c'est tout. L'en-tête ne donne aucune information sur ce qui arrivera après (réponse 404, 410, redirection 3xx vers une autre ressource…) Cet en-tête figure désormais dans le registre IANA des en-têtes.

Notre RFC introduit, en plus de l'en-tête HTTP, un type de lien (cf. RFC 8288), sunset, qui peut être mis dans d'autres contextes que celui des en-têtes HTTP, par exemple dans du HTML (section 6 de notre RFC). Il permet d'indiquer des détails sur la future suppression de la ressource, à la page Web indiquée. Ainsi, en HTML, cela donnerait :

      
<link rel="sunset" href="https://example.org/why-sunset-and-when">

    

Ce type de lien figure dans le registre IANA de ces types.

Le RFC ne précise pas ce que des applications comme les navigateurs doivent faire exactement avec cette information. C'est un choix des auteurs des applications. Ils peuvent choisir, par exemple, d'alerter l'utilisateur. Notez que la date indiquée n'est qu'une indication. Le serveur Web reste libre de garder la ressource plus longtemps, ou au contraire de la supprimer avant.

Quelques logiciels utilisent ou génèrent l'information sur le coucher de soleil :


Téléchargez le RFC 8594


L'article seul

Financement du logiciel de coordination d'actions Mobilizon

Première rédaction de cet article le 21 mai 2019


Une campagne de financement est en cours pour le logiciel Mobilizon. Ce logiciel doit permettre de créer des services Internet de coordination d'actions. Que l'action soit une manifestation, un pique-nique, une soutenance de thèse, une réunion ou bien un goûter d'anniversaire, Mobilizon permettra de la préparer et de le faire connaître.

Aujourd'hui, ce genre de service est rendu par des grosses sociétés privées, à but lucratif, capteuses de données personnelles, et qui censurent à leur guise les événements qui ne leur plaisent pas. Il est paradoxal d'organiser, par exemple, une réunion sur le thème de la défense de la vie privée, en utilisant un service qui la viole régulièrement ! Trop d'associations, de syndicats ou de partis politiques dépendent exclusivement de gros silos de données pour des actions citoyennes.

Mobilizon sera développé par l'association Framasoft, qui a déjà piloté des projets similaires comme PeerTube. Mobilizon sera évidemment un logiciel libre. Mais Framasoft a besoin d'argent pour cela, d'où cet appel au financement. Le projet Mobilizon prévoit trois paliers :

  • 20 000 € pour les fonctions de base. Ce montant a déjà été atteint.
  • 35 000 € pour la fédération. Ce point est très important. Il ne s'agit pas de créer un nouveau service centralisé, il en existe déjà trop ! Au contraire, Mobilizon permettra à chaque personne, à chaque organisation, de créer son propre service avec le logiciel Mobilizon. Mais avoir un service isolé est d'une utilité limitée : ce qui est important est de pouvoir se connecter au reste du fédivers, des autres services décentralisés. C'est actuellement là qu'il faut aider le projet et le financer.
  • 50 000 € pour la prochaine étape, avec par exemple des service de communication et de travail en groupe.

Loin des salons commerciaux où des startups plus ou moins bidons font assaut de services dangereux pour la démocratie ou pour la vie privée, Mobilizon vise à changer le monde en mieux. Son financement dépend de vous, il n'y a pas de business model pour de tels projets, c'est à vous de jouer.


L'article seul

Sur l'Internet, citoyen ou simple consommateur ?

Première rédaction de cet article le 9 mai 2019


Si vous regardez la télévision, ou écoutez les discours officiels, vous avez l'impression que l'Internet sert uniquement au commerce en ligne, et à nourrir Facebook et YouTube en leur laissant le plus de données personnelles possibles. Le débat politique au sujet de l'Internet est très pauvre, réduit à des discussions sur la répartition exacte du pouvoir entre ces GAFA et le gouvernement. Le citoyen et la citoyenne sont complètement absents. GAFA et gouvernement, derrière les polémiques qu'ils mettent en scène, sont en fait d'accord : l'internaute doit la fermer et consommer.

Il est donc urgent de re-politiser l'Internet. Un système technique par lequel passe l'essentiel de nos échanges ne doit pas être géré sans les citoyennes et citoyens. D'où le débat le 5 juin prochain, à la Fonderie de l'Image à Bagnolet, « Réinventer la citoyenneté à l'heure d'internet ». (Ce débat rassemble notamment des auteurs publiés chez C&F Éditions.) Venez nombreuses et nombreux ! (Si vous utilisez OpenAgenda, les informations sont par ici.) cfeditions-5juin2019.jpg

On parlera aussi d'alternatives. Car un autre point sur lesquels gouvernement et GAFA sont d'accord, c'est qu'il n'y a pas d'alternative : on discute de Facebook comme si aucun autre moyen de communication n'existait. De nombreuses entreprises, et plus encore d'associations ne communiquent publiquement que via Facebook. C'est cette position dominante qu'il faut remettre en cause. Quand on a une place aussi importante que Facebook, on en abuse forcément (pour la plus grande joie du gouvernement, qui ne demande pas mieux que de sous-traiter la censure à Facebook). Il est donc urgent de développer et de populariser des alternatives, comme les réseaux sociaux décentralisés mais aussi, par exemple, la syndication.

Les sites Web des autres intervenants :


L'article seul

RFC 8415: Dynamic Host Configuration Protocol for IPv6 (DHCPv6)

Date de publication du RFC : Novembre 2018
Auteur(s) du RFC : T. Mrugalski, M. Siodelski (ISC), B. Volz, A. Yourtchenko (Cisco), M. Richardson (SSW), S. Jiang (Huawei), T. Lemon (Nibbhaya Consulting), T. Winters (UNH-IOL)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dhc
Première rédaction de cet article le 3 mai 2019


IPv6 dispose de trois mécanismes principaux pour l'allocation d'une adresse IP à une machine. L'allocation statique, « à la main », le système d'« autoconfiguration » SLAAC du RFC 4862 et DHCP. DHCP pour IPv6 avait été normalisé pour la première fois dans le RFC 3315, que notre RFC met à jour. Le protocole n'a guère changé mais le texte du RFC a été sérieusement revu (DHCP est un protocole compliqué).

DHCP permet à une machine (qui n'est pas forcément un ordinateur) d'obtenir une adresse IP (ainsi que plusieurs autres informations de configuration) à partir d'un serveur DHCP du réseau local. C'est donc une configuration « avec état », du moins dans son mode d'utilisation le plus connu. (Notre RFC documente également un mode sans état.) DHCP nécessite un serveur, par opposition à l'autoconfiguration du RFC 4862 qui ne dépend pas d'un serveur (cette autoconfiguration sans état peut être utilisée à la place de, ou bien en plus de DHCP). Deux utilisations typiques de DHCP sont le SoHo où le routeur ADSL est également serveur DHCP pour les trois PC connectés et le réseau local d'entreprise où deux ou trois machines Unix distribuent adresses IP et informations de configuration à des centaines de machines.

Le principe de base de DHCP (IPv4 ou IPv6) est simple : la nouvelle machine annonce à la cantonade qu'elle cherche une adresse IP, le serveur lui répond, l'adresse est allouée pour une certaine durée, le bail, la machine cliente devra renouveler le bail de temps en temps.

L'administrateur d'un réseau IPv6 se pose souvent la question « DHCP ou SLAAC » ? Notez que les deux peuvent coexister, ne serait-ce que parce que certaines possibilités n'existent que pour un seul des deux protocoles. Ainsi, DHCP seul ne peut indiquer l'adresse du routeur par défaut. Pour le reste, c'est une question de goût.

Le DHCP spécifié par notre RFC ne fonctionne que pour IPv6, les RFC 2131 et RFC 2132 traitant d'IPv4. Les deux protocoles restent donc complètement séparés, le RFC 4477 donnant quelques idées sur leur coexistence. Il a parfois été question de produire une description unique de DHCPv4 et DHCPv6, ajoutant ensuite les spécificités de chacun, mais le projet n'a pas eu de suite (section 1.2 de ce RFC), les deux protocoles étant trop différents. De même, l'idée de permettre l'envoi d'informations spécifiques à IPv4 (par exemple l'adresse IPv4, proposée dans le RFC 3315, section 1) au-dessus de DHCPv6 a été abandonnée (mais voyez quand même le RFC 7341).

DHCP fonctionne par diffusion restreinte. Un client DHCP, c'est-à-dire une machine qui veut obtenir une adresse, diffuse (DHCP fonctionne au-dessus d'UDP, RFC 768, le port source est 546, le port de destination, où le serveur écoute, est 547) sa demande à l'adresse multicast locale au lien ff02::1:2. Le serveur se reconnait et lui répond. S'il n'y a pas de réponse, c'est, comme dans le DNS, c'est au client de réémettre (section 15). L'adresse IP source du client est également une adresse locale au lien.

(Notez qu'une autre adresse de diffusion restreinte est réservée, ff05::1:3 ; elle inclut également tous les serveurs DHCP mais, contrairement à la précédente, elle exclut les relais, qui transmettent les requêtes DHCP d'un réseau local à un autre.)

Le serveur choisit sur quels critères il alloue les adresses IP. Il peut les distribuer de manière statique (une même machine a toujours la même adresse IP) ou bien les prendre dans un pool d'adresses et chaque client aura donc une adresse « dynamique ». Le fichier de configuration du serveur DHCP ISC ci-dessous montre un mélange des deux approches.

Il faut bien noter (et notre RFC le fait dans sa section 22) que DHCP n'offre aucune sécurité. Comme il est conçu pour servir des machines non configurées, sur lesquelles on ne souhaite pas intervenir, authentifier la communication est difficile. Un serveur DHCP pirate, ou, tout simplement, un serveur DHCP accidentellement activé, peuvent donc être très gênants. Les approches de sécurité suggérées dans le RFC 3315 se sont avérées peu pratiques et plusieurs ont été retirées dans notre nouveau RFC.

Outre l'adresse IP, DHCP peut indiquer des options comme les adresses des serveurs DNS à utiliser (RFC 3646).

Notre version IPv6 de DHCP est assez différente de la version IPv4 (et le RFC est plus de trois fois plus long). Par exemple, l'échange « normal » entre client et serveur prend quatre paquets IP (section 5) et non pas deux. (Il y a aussi un échange simplifié à deux paquets, cf. section 5.1.) L'encodage des messages est très différent, et il y a des différences internes comme l'IA (Identity Association) de la section 12. Il y a aussi des différences visibles à l'utilisateur comme le concept de DUID (DHCP Unique IDentifier), section 11, qui remplace les anciens client identifier et server identifier de DHCP v4. Les différences sont telles que le RFC précise que leur intégration avec DHCP pour IPv4 n'est pas envisagée.

À l'heure actuelle, il existe plusieurs mises en œuvre de DHCPv6, Dibbler (client et serveur, mais qui n'est plus maintenu), celle de l'Institut Leibniz à Dresde (serveur seulement), celles de l'ISC, l'ancien DHCP et le nouveau, nommé Kea (dans les deux cas, serveur seulement) et dhcpcd (client seulement). Pour celles et ceux qui utilisent une Freebox comme serveur DHCP, il semble qu'elle ait DHCPv6 depuis 2018 (je n'ai pas testé). Il parait que la Livebox le fait également. Je n'ai pas non plus essayé pour la Turris Omnia mais cela devrait marcher puisqu'elle utilise le serveur odhcpd, qui sait faire du DHCPv6. Et il y a bien sûr des implémentations non-libres dans des équipements comme les Cisco. Notez que ces mises en œuvre de DHCPv6 n'ont pas forcément déjà intégré les modifications de notre RFC 8415.

Voici un exemple d'utilisation de Dibbler, qui nous affiche les quatre messages (Solicit - Advertise - Request - Reply) :


% sudo dibbler-client run 
...
2019.05.02 11:35:15 Client Notice    Unable to open DUID file (client-duid), generating new DUID.
2019.05.02 11:35:15 Client Notice    DUID creation: Generating 14-bytes long link-local+time (duid-llt) DUID.
2019.05.02 11:35:15 Client Info      My DUID is 00:01:00:01:24:5d:76:53:00:1e:8c:76:29:b6.
...
2019.05.02 12:12:38 Client Info      Creating SOLICIT message with 1 IA(s), no TA and 0 PD(s) on eth1/2 interface.
2019.05.02 12:12:38 Client Info      Received ADVERTISE on eth1/2,trans-id=0x614722, 4 opts: 1 2 3 23
2019.05.02 12:12:39 Client Info      Processing msg (SOLICIT,transID=0x614722,opts: 1 3 8 6)
2019.05.02 12:12:39 Client Info      Creating REQUEST. Backup server list contains 1 server(s).
2019.05.02 12:12:39 Client Info      Received REPLY on eth1/2,trans-id=0x634881, 4 opts: 1 2 3 23
2019.05.02 12:12:39 Client Notice    Address fde8:9fa9:1aba:0:fafa::2/128 added to eth1/2 interface.
2019.05.02 12:12:39 Client Notice    Setting up DNS server 2001:db8:2::dead:beef on interface eth1/2.

   

Le serveur en face était un Kea ainsi configuré :

"subnet6": [
        {
           "interface": "eth0", 
           "subnet": "fde8:9fa9:1aba:0::/64",
           "pools": [ { "pool": "fde8:9fa9:1aba:0:fafa::/80" } ],
...

Pour que Kea puisse écouter sur le port DHCP, il a aussi fallu :

%  sudo setcap 'cap_net_bind_service=+ep' /usr/sbin/kea-dhcp6

(Sinon, c'est le Permission denied.) Si vous voulez, le pcap de l'échange est disponible (capture faite avec tcpdump -w /tmp/dhcpv6.pcap udp and port 546 or port 547). tcpdump voit le trafic ainsi :

12:17:44.531859 IP6 fe80::21e:8cff:fe76:29b6.546 > ff02::1:2.547: dhcp6 solicit
12:17:44.555202 IP6 fe80::d40b:5ff:fee8:a36b.547 > fe80::21e:8cff:fe76:29b6.546: dhcp6 advertise
12:17:45.559247 IP6 fe80::21e:8cff:fe76:29b6.546 > ff02::1:2.547: dhcp6 request
12:17:45.567875 IP6 fe80::d40b:5ff:fee8:a36b.547 > fe80::21e:8cff:fe76:29b6.546: dhcp6 reply
   

On voit bien les quatre messages (Solicit - Advertise - Request - Reply). Avec l'option -vvv, tcpdump est plus bavard et montre qu'il analyse bien DHCPv6 :

12:17:44.531859 IP6 (flowlabel 0x90d05, hlim 1, next-header UDP (17) payload length: 58) fe80::21e:8cff:fe76:29b6.546 > ff02::1:2.547: [udp sum ok] dhcp6 solicit (xid=aecc66 (client-ID hwaddr/time type 1 time 610104915 001e8c7629b6) (IA_NA IAID:1 T1:4294967295 T2:4294967295) (elapsed-time 0) (option-request DNS-server))
12:17:44.555202 IP6 (flowlabel 0xa6d2a, hlim 64, next-header UDP (17) payload length: 128) fe80::d40b:5ff:fee8:a36b.547 > fe80::21e:8cff:fe76:29b6.546: [udp sum ok] dhcp6 advertise (xid=aecc66 (client-ID hwaddr/time type 1 time 610104915 001e8c7629b6) (server-ID hwaddr/time type 1 time 610105065 d60b05e8a36b) (IA_NA IAID:1 T1:1000 T2:2000 (IA_ADDR fde8:9fa9:1aba:0:fafa::4 pltime:3000 vltime:4000)) (DNS-server 2001:db8:2::dead:beef 2001:db8:2::cafe:babe))
12:17:45.559247 IP6 (flowlabel 0x90d05, hlim 1, next-header UDP (17) payload length: 104) fe80::21e:8cff:fe76:29b6.546 > ff02::1:2.547: [udp sum ok] dhcp6 request (xid=dc7ba (client-ID hwaddr/time type 1 time 610104915 001e8c7629b6) (IA_NA IAID:1 T1:4294967295 T2:4294967295 (IA_ADDR fde8:9fa9:1aba:0:fafa::4 pltime:3000 vltime:4000)) (option-request DNS-server) (server-ID hwaddr/time type 1 time 610105065 d60b05e8a36b) (elapsed-time 0))
12:17:45.567875 IP6 (flowlabel 0xa6d2a, hlim 64, next-header UDP (17) payload length: 128) fe80::d40b:5ff:fee8:a36b.547 > fe80::21e:8cff:fe76:29b6.546: [udp sum ok] dhcp6 reply (xid=dc7ba (client-ID hwaddr/time type 1 time 610104915 001e8c7629b6) (server-ID hwaddr/time type 1 time 610105065 d60b05e8a36b) (IA_NA IAID:1 T1:1000 T2:2000 (IA_ADDR fde8:9fa9:1aba:0:fafa::4 pltime:3000 vltime:4000)) (DNS-server 2001:db8:2::dead:beef 2001:db8:2::cafe:babe))
   

Mais si vous préférez tshark, l'analyse de cet échange est également disponible.

Quant au fichier de configuration du traditionnel serveur ISC (celui avant Kea), il ressemble beaucoup à ce qu'il est en v4 :

subnet6 2001:db8:dead:babe::/64 {
     range6      2001:db8:dead:babe::100 2001:db8:dead:babe::FFF; 
     # On peut aussi utiliser préfixe/longueur au lieu d'indiquer les
     # adresses de début et de fin de la plage
}

On doit lancer le serveur avec l'option -6 (le même démon ne peut pas servir le v4 et le v6 en même temps, les deux protocoles étant trop différents) :

# dhcpd -6 -d -f 
Internet Systems Consortium DHCP Server 4.0.0
...
Listening on Socket/eth0/2001:db8:dead:babe::/64
Sending on   Socket/eth0/2001:db8:dead:babe::/64

[Puis arrive une requête]

Solicit message from fe80::219:b9ff:fee4:25f9 port 546, transaction ID 0x4BB14F00
Picking pool address 2001:db8:dead:babe::fbb
Sending Advertise to fe80::219:b9ff:fee4:25f9 port 546
Request message from fe80::219:b9ff:fee4:25f9 port 546, transaction ID 0x46B10900
Sending Reply to fe80::219:b9ff:fee4:25f9 port 546

(Le concept de transaction ID est décrit sections 8 et 16.1.) La requête est émise depuis une adresse lien-local (ici fe80::219:b9ff:fee4:25f9) pas depuis une adresse « tout zéro » comme en IPv4 (section 17 du RFC). Vu avec tcpdump, la requête est :

15:07:43.455918 IP6 fe80::219:b9ff:fee4:25f9.546 > ff02::1:2.547: dhcp6 solicit
15:07:43.456098 IP6 fe80::219:b9ff:fee4:2987.547 > fe80::219:b9ff:fee4:25f9.546: dhcp6 advertise
15:07:44.512946 IP6 fe80::219:b9ff:fee4:25f9.546 > ff02::1:2.547: dhcp6 request
15:07:44.513233 IP6 fe80::219:b9ff:fee4:2987.547 > fe80::219:b9ff:fee4:25f9.546: dhcp6 reply

On note que l'échange a été celui à quatre messages (Solicit - Advertise - Request - Reply), décrit section 5.2 (et la liste des types possibles est en section 7.3). Le serveur n'a pas répondu directement avec un Reply, parce que le client n'a pas inclus l'option Rapid Commit (section 21.14). Cette option n'est pas actuellement gérée par le client DHCP utilisé (l'option dhcp6.rapid-commit existe mais la documentation précise qu'elle est ignorée). Dans l'échange à quatre message,s, le client demande à tous (Solicit), un(s) serveur(s) DHCP répond(ent) (Advertise), le client envoie alors sa requête au serveur choisi (Request), le serveur donne (ou pas) son accord (Reply).

L'échange à deux messages (Solicit- Reply) est, lui, spécifié dans la section 5.1. Il s'utilise si le client n'a pas besoin d'une adresse IP, juste d'autres informations de configuration comme l'adresse du serveur NTP, comme décrit dans le RFC 4075. Même si le client demande une adresse IP, il est possible d'utiliser l'échange à deux messages, via la procédure rapide avec l'option Rapid Commit.

Actuellement, l'attribution d'adresses statiques à une machine, en la reconnaissant, par exemple, à son adresse MAC est plus délicate avec le serveur de l'ISC (merci à Shane Kerr pour son aide). Il faut trouver le client identifier (section 21.2 du RFC, deux méthodes possibles pour le trouver sont expliquées plus loin) et le mettre dans dhcpd.conf :

host lilith {
  host-identifier option dhcp6.client-id 0:1:0:1:47:96:21:f7:0:19:b9:e4:25:f9;
  fixed-address6 2001:db8:dead:babe::2;
}

et cette adresse IP fixe est donnée au client.

Pour trouver le client identifier, une méthode spécifique au client DHCP de l'ISC est de regarder dans le fichier des baux du client (typiquement /var/db/dhclient6.leases) :

...
  option dhcp6.client-id 0:1:0:1:47:96:21:f7:0:19:b9:e4:25:f9;

Il suffit alors de le copier-coller.

Une autre méthode, plus complexe, mais qui marche avec tous les clients DHCP est de lancer tcpdump en mode bavard :

# tcpdump -n -vvv ip6 and udp and port 547
12:24:15.084006 IP6 (hlim 64, next-header UDP (17) payload length: 60) fe80::219:b9ff:fee4:25f9.546 > ff02::1:2.547: dhcp6 solicit (xid=4323ac (client ID hwaddr/time type 1 time 1201021431 0019b9e425f9) (option request DNS DNS name)[|dhcp6ext])

Tout client ou serveur DHCP v6 a un DUID (DHCP Unique Identifier, décrit en section 11). Le DUID est opaque et ne devrait pas être analysé par la machine qui le reçoit. La seule opération admise est de tester si deux DUID sont égaux (indiquant qu'en face, c'est la même machine). Il existe plusieurs façons de générer un DUID (dans l'exemple plus haut, Dibbler avait choisi la méthode duid-llt, adresse locale et heure) et de nouvelles pourront apparaitre dans le futur. Par exempe, un DUID peut être fabriqué à partir d'un UUID (RFC 6355).

Le client identifier de l'exemple avec le serveur de l'ISC ci-dessus, le DUID, a été fabriqué, comme pour Dibbler, en concaténant le type de DUID (ici, 1, Link-layer Address Plus Time, section 11.2 du RFC), le type de matériel (1 pour Ethernet), le temps (ici 1201021431, notons que ce client DHCP violait le RFC en comptant les secondes à partir de 1970 et pas de 2000) et l'adresse MAC, ce qui redonne le même résultat au prix de quelques calculs avec bc.

Mais l'utilisation exclusive du DUID, au détriment de l'adresse MAC, n'est pas une obligation du RFC (le RFC, section 11, dit juste « DHCP servers use DUIDs to identify clients for the selection of configuration parameters », ce qui n'interdit pas d'autres méthodes), juste un choix des développeurs de l'ISC. Le serveur de Dresde, dhcpy6d, permet d'utiliser les adresses MAC, comme on le fait traditionnellement en IPv4. En combinaison avec des commutateurs qui filtrent sur l'adresse MAC, cela peut améliorer la sécurité.

La section 6 de notre RFC décrit les différentes façons d'utiliser DHCPv6. On peut se servir de DHCPv6 en mode sans état (section 6.1), lorsqu'on veut juste des informations de configuration, ou avec état (section 6.2, qui décrit la façon historique d'utiliser DHCP), lorsqu'on veut réserver une ressource (typiquement l'adresse IP) et qu'il faut alors que le serveur enregistre (et pas juste dans sa mémoire, car il peut redémarrer) ce qui a été réservé. On peut aussi faire quelque chose qui n'a pas d'équivalent en IPv4, se faire déléguer un préfixe d'adresses IP entier (section 6.3). Un client DHCP qui reçoit un préfixe, mettons, /60, peut ensuite redéléguer des bouts, par exemple ici des /64. (Le RFC 7084 est une utile lecture au sujet des routeurs installés chez M. Toutlemonde.)

Le format détaillé des messages est dans la section 8. Le début des messages est toujours le même, un type d'un octet (la liste des types est en section 7.3) suivi d'un identificateur de transaction de trois octets. Le reste est variable, dépendant du type de message.

On a déjà parlé du concept de DUID plus haut, donc sautons la section 11 du RFC, qui parle du DUID, et allons directement à la section 12, qui parle d'IA (Identity Association). Une IA est composée d'un identifiant numérique, l'IAID (IA IDentifier) et d'un ensemble d'adresses et de préfixes. Le but du concept d'IA est de permettre de gérer collectivement un groupe de ressources (adresses et préfixes). Pour beaucoup de clients, le concept n'est pas nécessaire, on n'a qu'une IA, d'identificateur égal à zéro. Pour les clients plus compliqués, on a plusieurs IA, et les messages DHCP (par exemple d'abandon d'un bail) indiquent l'IA concernée.

Comme pour DHCPv4, une bonne partie des informations est transportée dans des options, décrites dans la section 21. Certaines options sont dans ce RFC, d'autres pourront apparaitre dans des RFC ultérieurs. Toutes les options commencent par deux champs communs, le code identifiant l'option (deux octets), et la longueur de l'option. Ces champs sont suivis par des données, spécifiques à l'option. Ainsi, l'option Client Identifier a le code 1, et les données sont un DUID (cf. section 11). Autre exemple, l'option Vendor Class (code 16) permet d'indiquer le fournisseur du logiciel client (notez qu'elle pose des problèmes de sécurité, cf. RFC 7824, et section 23 de notre RFC). Notez qu'il peut y avoir des options dans les options, ainsi, l'adresse IP (code 5) est toujours dans les données d'une option IA (les IA sont décrites en section 12).

Puisqu'on a parlé de sécurité, la section 22 du RFC détaille les questions de sécurité liées à DHCP. Le fond du problème est qu'il y a une profonde incompatibilité entre le désir d'une autoconfiguration simple des clients (le but principal de DHCP) et la sécurité. DHCP n'a pas de chiffrement et tout le monde peut donc écouter les échanges de messages, voire les modifier. Et, de toute façon, le serveur n'est pas authentifié, donc le client ne sait jamais s'il parle au serveur légitime. Il est trivial pour un méchant de configurer un serveur DHCP « pirate » et de répondre à la place du vrai, indiquant par exemple un serveur DNS que le pirate contrôle. Les RFC 7610 et RFC 7513 décrivent des solutions possibles à ce problème.

Des attaques par déni de service sont également possibles, par exemple via un client méchant qui demande des ressources (comme des adresses IP) en quantité. Un serveur prudent peut donc limiter la quantité de ressources accessible à un client.

Nouveauté de ce RFC (elle était quasiment absente du RFC 3315), les questions de vie privée. La section 23 rappelle que DHCP est très indiscret. Le RFC 7824 décrit les risques que DHCP fait courir à la vie privée du client (et le RFC 7844 des solutions possibles).

Les registres IANA ne changent pas par rapport à l'ancien RFC. Les différents paramètres sont en ligne.

L'annexe A de notre RFC décrit les changements depuis le vieil RFC 3315. Ils sont nombreux mais en général pas cruciaux. On notera :

  • beaucoup de changements dans le texte, pour clarifier certains points, ou mieux expliquer des questions complexes,
  • une section 6 sur les questions d'utilisation de DHCP, toute neuve,
  • l'option Option Request devient obligatoire dans certains messages,
  • une nouvelle section sur la vie privée,
  • les références à IPsec (jamais déployé) ont été retirées,
  • le protocole d'« authentification retardée » (jamais déployé, et probablement pas déployable) a été retiré (cf. section 25),
  • la possibilité pour le client d'indiquer la durée d'un bail souhaitée a été retirée,
  • plusieurs RFC séparés ont été fusionnés avec ce nouveau RFC, comme la délégation de préfixe, initialement séparée dans le RFC 3633 (l'implémenteur de DHCPv6 aura donc désormais une vue unifiée du protocole, et moins de RFC à lire),
  • le mode « sans état », qui était dans le RFC 3736, a été intégré,
  • le mécanisme de ralentissement des clients lorsque le serveur ne répond pas (ex-RFC 7083), le relayage des messages de type inconnu (remplaçant donc le RFC 7283), et le fonctionnement des différents modes (au revoir, le RFC 7550) sont également intégrés,
  • une bonne douzaine de bogues relevées dans le RFC 3315 et ses RFC proches, ont été corrigées.

Téléchargez le RFC 8415


L'article seul

RFC 8546: The Wire Image of a Network Protocol

Date de publication du RFC : Avril 2019
Auteur(s) du RFC : B. Trammell, M. Kuehlewind (ETH Zurich)
Pour information
Première rédaction de cet article le 29 avril 2019


Ce nouveau RFC de l'IAB décrit le très important concept de vue depuis le réseau (wire image), une abstraction servant à modéliser ce que voit, sur le réseau, une entité qui ne participe pas à un protocole, mais peut en observer les effets. Cela peut être un routeur, un boitier de surveillance, etc. Le concept n'était pas nécessaire autrefois, où tout le trafic était en clair. Maintenant qu'une grande partie est (heureusement) chiffrée, il est important d'étudier ce qui reste visible à ces entités extérieures.

Un protocole de communication, c'est un ensemble de règles que les participants doivent respecter, le format des messages, qui doit envoyer quoi et comment y répondre, etc. Par exemple, si on prend le protocole HTTP, il y a au moins deux participants, le client et le serveur, parfois davantage s'il y a des relais. Ces participants (par exemple le navigateur Web et le serveur HTTP) connaissent le protocole, et le suivent. (Du moins on peut l'espérer.) Mais, en plus des participants, d'autres entités peuvent observer le trafic. Cela va des couches basses de la machine (TCP, IP, Ethernet) aux équipements intermédiaires. Même si le routeur ne connait pas HTTP, et n'en a pas besoin pour faire son travail, il voit passer les bits et peut techniquement les décoder, en suivant le RFC. C'est ainsi que des applications comme Wireshark peuvent nous afficher une compréhension d'un dialogue auxquelles elles ne participent pas.

Cette fuite d'informations vers d'autres entités n'est pas explicite dans la spécification d'un protocole. Autrefois, avec le trafic en clair, elle allait de soi (« bien sûr que le routeur voit tout passer ! »). Aujourd'hui, avec le chiffrement, il faut se pencher sur la question « qu'est-ce que ces entités voient et comprennent du trafic ? » C'est la vue depuis le réseau qui compte, pas la spécification du protocole, qui ne mentionne pas les fuites implicites.

Prenons l'exemple de TLS (RFC 8446). TLS chiffre le contenu de la connexion TCP. Mais il reste des informations visibles : les couches inférieures (un observateur tiers voit le protocole TCP en action, les retransmissions, le RTT, etc), les informations sur la taille (TLS ne fait pas de remplissage, par défaut, ce qui permet, par exemple, d'identifier la page Web regardée), la dynamique des paquets (délai entre requête et réponse, par exemple). Tout ceci représente la vue depuis le réseau.

Le RFC prend un autre exemple, le protocole QUIC. Cette fois, la mécanique du protocole de transport est largement cachée par le chiffrement. QUIC a donc une « vue depuis le réseau » réduite. C'est le premier protocole IETF qui essaie délibérement de réduire cette vue, de diminuer le « rayonnement informationnel ». Cela a d'ailleurs entrainé de chaudes discussions, comme celles autour du spin bit, un seul bit d'information laissé délibérement en clair pour informer les couches extérieures sur le RTT. En effet, diminuer la taille de la vue depuis le réseau protège la vie privée mais donne moins d'informations aux opérateurs réseau (c'est bien le but) et ceux-ci peuvent être frustrés de cette décision. Le conflit dans ce domaine, entre sécurité et visibilité, ne va pas cesser de si tôt.

Après cette introduction, la section 2 du RFC décrit formellement cette notion de « vue depuis le réseau ». La vue depuis le réseau (wire image) est ce que voit une entité qui ne participe pas aux protocoles en question. C'est la suite des paquets transmis, y compris les métadonnées (comme l'heure de passage du paquet).

La section 3 de notre RFC discute ensuite en détail les propriétés de cette vue. D'abord, elle ne se réduit pas aux « bits non chiffrés ». On l'a vu, elle inclut les métadonnées comme la taille des paquets ou l'intervalle entre paquets. Ces métadonnées peuvent révéler bien des choses sur le trafic. Si vous utilisez OpenVPN pour chiffrer, et que vous faites ensuite par dessus du SSH ou du DNS, ces deux protocoles présentent une vue très différente, même si tout est chiffré. Mais un protocole chiffré, contrairement aux protocoles en clair (où la vue est maximale) peut être conçu pour changer volontairement la vue qu'il offre (la section 4 approfondira cette idée).

La cryptographie peut aussi servir à garantir l'intégrité de la vue (empêcher les modifications), même si on ne chiffre pas. En revanche, toutes les parties de la vue qui n'utilisent pas la cryptographie peuvent être non seulement observées mais encore changées par des intermédiaires. Ainsi, un FAI sans scrupules peut changer les en-têtes TCP pour ralentir certains types de trafic. (Beaucoup de FAI ne respectent pas le principe de neutralité.)

Notez que la vue depuis le réseau dépend aussi de l'observateur. S'il ne capture qu'un seul paquet, il aura une vue réduite. S'il observe plusieurs paquets, il a accès à des informations supplémentaires, et pas seulement celles contenues dans ces paquets, mais également celles liées à l'intervalle entre paquets. De même, si l'observateur ne voit que les paquets d'un seul protocole, il aura une vue limitée de ce qui se passe alors que, s'il peut croiser plusieurs protocoles, sa vue s'élargit. Un exemple typique est celui du DNS : très majoritairement non chiffré, contrairement à la plupart des protocoles applicatifs, et indispensable à la très grande majorité des transactions Internet, il contribue beaucoup à la vue depuis le réseau (RFC 7626). Si vous voyez une requête DNS pour imap.example.net juste avant un soudain trafic, il est facile de suspecter que le protocole utilisé était IMAP. Élargissons encore la perspective : outre le trafic observé, le surveillant peut disposer d'autres informations (le résultat d'une reconnaissance faite avec nmap, par exemple), et cela augmente encore les possibilités d'analyse de la vue dont il dispose.

Puisqu'on parle de vue (image), le RFC note également que le terme n'est pas uniquement une métaphore, et qu'on pourrait utiliser les techniques de reconnaissance d'images pour analyser ces vues.

Notez que, du point de vue de l'IETF, l'Internet commence à la couche 3. Les couches 1 et 2 contribuent également à la vue depuis le réseau, mais sont plus difficiles à protéger, puisqu'elles n'opèrent pas de bout en bout.

Pour un protocole, il est difficile de réduire la vue qu'il offre au réseau. On ne peut pas rendre les paquets plus petits, ni diminuer l'intervalle entre deux paquets. Une des solutions est d'envoyer volontairement des informations fausses, pour « noyer » les vraies. (Voir le livre de Finn Brunton et Helen Nissenbaum, « Obfuscation », chez C&F Éditions.) On ne peut pas réduire les paquets, mais on peut les remplir, par exemple. Ou bien on peut ajouter de faux paquets pour brouiller les pistes. Mais il n'y a pas de miracle, ces méthodes diminueront la capacité utile du réseau, ou ralentiront les communications. (Par exemple, utiliser le Web via Tor est bien plus lent.) Bref, ces méthodes ne sont vraiment acceptables que pour des applications qui ne sont pas trop exigeantes en performance.

J'ai dit plus haut qu'on pouvait assurer l'intégrité de certains champs du protocole, sans les chiffrer. Cela permet d'avoir des informations fiables, non modifiables, mais visibles, ce qui peut être utile pour certains équipements intermédiaires. Notez que cette protection a ses limites : on ne peut protéger que des bits, pas des données implicites comme l'écart entre deux paquets. Et la protection est forcément par paquet puisque, dans un réseau à commutation de paquets, comme l'Internet, on ne peut pas garantir l'arrivée de tous les paquets, ou leur ordre.

Enfin, la dernière section de notre RFC, la section 4, explore les moyens par lesquels un protocole peut tromper un éventuel surveillant, en modifiant la vue qu'il offre au réseau. Une fois qu'on a ce concept de vue depuis le réseau, on peut bâtir des choses utiles sur ce concept. Par exemple, il aide à comprendre des questions d'ossification (la difficulté à déployer de nouveaux services ou protocoles, et qui rend, par exemple, nécessaire de faire passer même le DNS sur HTTPS, comme spécifié dans le RFC 8484). En effet, tout ce qui est visible sera regardé, tout ce qui n'est pas protégé sera modifié. Les boitiers intermédiaires, ou plutôt les entreprises et les États qui les conçoivent et les déploient, n'ont aucun scrupule et ne connaissent aucune restriction. Cela veut dire que si un protocole laisse une information visible, celle-ci sera utilisée par les boitiers intermédiaires et donc il sera difficile de changer sa sémantique par la suite, même si toutes les machines terminales sont d'accord.

Prenons l'exemple de TCP (normalisé dans le RFC 793). TCP envoie un certain nombre de signaux involontaires et implicites. Par exemple, l'observation des numéros de séquence permet de mesurer le RTT. Et TCP permet également de modifier ces signaux. Comme l'explique le RFC 8558, des équipements sont vendus aujourd'hui avec des fonctions de surveillance et tripotage des en-têtes TCP. Le RFC fournit deux exemples d'utilisation de cette surveillance :

  • Déterminer la joignabilité et le consentement. Si on voit des réponses respectant le protocole TCP (notamment les numéros de séquences, cf. RFC 6528), cela indique que les deux machines sont d'accord pour communiquer, l'une n'est pas en train d'attaquer l'autre. Cette conclusion peut être utilisée par un pare-feu.
  • Mesurer la latence et le taux de pertes de paquets. Cela peut se faire, comme indiqué plus haut, en regardant les numéros de séquence dans les paquets et les accusés de réception, et ou en regardant ECN (RFC 3168) et l'estampillage (RFC 7323).

Dans le cas de TCP, cette exposition d'information est « involontaire ». Le but de TCP n'est pas que tout le monde sur le trajet puisse regarder, et encore moins modifier, ces informations. Mais c'est quand même ce qui arrive. Avec un protocole qui réduit consciemment la vue, comme QUIC, ne serait-ce pas une bonne idée que de donner un peu à manger aux équipements intermédiaires, afin qu'ils puissent optimiser leurs décisions ? Ce fut tout le débat dans le groupe de travail QUIC à l'IETF sur le spin bit, un bit uniquement conçu pour agrandir un peu la vue dont disposent les équipements du réseau, mais qui était un peu en conflit avec le principe d'en dire le moins possible, et ossifiait un peu le protocole (une fois QUIC déployé avec le spin bit, on ne peut plus le supprimer, sous peine de mettre en colère les middleboxes.)

Les informations accessibles dans la vue sont en pratique difficiles à changer puisque les boitiers intermédiaires vont s'habituer à compter dessus. Au moins, on pourrait les rendre explicites plutôt qu'implicites, et documenter clairement ces invariants, ces informations présentes dans la vue et que les concepteurs du protocole s'engagent à garder dans les évolutions futures. Typiquement, les invariants sont des données stables, et simples. Pour un protocole qui a la notion de version, et de négociation de version, cette négociation a intérêt à être déclarée invariante. Mais attention : une fois qu'on a figé certaines parties de la vue, en les déclarant invariantes, il ne faut pas s'imaginer que les équipements du réseau vont s'arrêter là : ils vont sans doute utiliser d'autres parties de la vue pour prendre leur décision, et ces autres parties risquent donc de devenir des invariants de fait. Le RFC recommande donc que toutes les parties qui ne sont pas explicitement listées comme invariantes soient chiffrées, pas pour la confidentialité, mais pour éviter qu'elles ne deviennent invariantes du fait de leur utilisation par les intermédiaires.

Enfin, le RFC rappelle que les équipements intermédiaires ne peuvent pas savoir ce que les deux parties qui communiquent ont décidé entre elles, et que la véracité de la vue depuis le réseau n'est jamais garantie.


Téléchargez le RFC 8546


L'article seul

Le logiciel Pleroma, pour communiquer sur le fédivers

Première rédaction de cet article le 25 avril 2019


Le fédivers (ou fediverse, ou fédiverse) est l'ensemble des systèmes qui communiquent sur l'Internet en échangeant des messages typiquement assez courts, dans une logique de microblogging et en utilisant en général le format Activity Streams et le protocole ActivityPub. Il existe plusieurs logiciels qui permettent de participer au fédivers, et cet article va parler de mon expérience avec Pleroma.

Mon résumé du fédivers vous a semble bien vague et bien nébuleux ? C'est normal, le fédivers n'a pas de définition claire. Beaucoup de gens le confondent avec Mastodon, qui n'est qu'un des logiciels permettant l'accès au fédivers. En pratique, il est vrai que « fédivers » est souvent défini de facto comme « tout ce qui peut échanger des messages avec Mastodon ».

Mon compte fédivers personnel, bortzmeyer@mastodon.gougere.fr, est sur Mastodon, mais je pense qu'il est crucial d'avoir un pluralisme des logiciels utilisés. Ce serait triste de fuir les rézosocios centralisés des GAFA pour retrouver un monopole dans le logiciel, même si c'est un logiciel libre. Et puis, je voulais gérer ma propre instance (ma propre machine connectée au fédivers) et l'installation et l'administration de Mastodon me faisaient un peu peur, surtout sur une petite machine. Donc, je me suis tourné vers le logiciel des pauvres, Pleroma.

Un mot d'abord pour les utilisateurs, et je parlerai des administrateurs système par la suite. Si vous voulez expérimenter avec Pleroma, il existe au moins une instance publique en France, https://pleroma.fr/. Vous pouvez y créer un compte et essayer ainsi le logiciel (j'ai d'ailleurs un compte sur cette instance, bortzmeyer@pleroma.fr, même si je m'en sers peu). Contrairement à Mastodon, où le serveur et l'interface utilisateur sont développés et distribués ensemble, Pleroma sépare les deux. L'interface Web par défaut est nommée Pleroma FE (FE pour FrontEnd) mais on peut en utiliser d'autres, y compris celle de Mastodon (Mastodon FE). Bon, mais on ne va parler que de Pleroma FE.

Contrairement à Mastodon FE, tous les pouètes sont sur une seule colonne, et on utilise les liens à gauche pour afficher les messages des gens qu'on suit, les messages qui nous mentionnent, les messages privées, ou bien tous les messages connus de l'instance.

Pour écrire, on tape son pouète (son message, son toot) dans la fenêtre (celle avec le choix par défaut « Just landed in L.A. », je n'ai pas encore trouvé comment le modifier…) Le protocole ActivityPub laisse plein de choses non décidées. Ainsi, il n'y a aucune règle sur la taille maximale des pouètes. Mastodon la limite à 500 caractères par défaut (mais l'administrateurice de l'instance peut le changer), Pleroma à 5 000 (également modifiable). Le format des pouètes n'est pas non plus normalisé et Pleroma permet (ce n'est pas activé par défaut) de mettre du gras, de l'italique… (Les utilisateurices les plus avancé·e·s noteront qu'on peut utiliser la syntaxe Markdown.)

Comme Pleroma est par défaut mono-colonne et Mastodon par défaut multi-colonnes, cela déroute parfois ceux et celles qui viennent de Mastodon. Quelques petites particularités de l'interface Pleroma FE, listées par lord :

  • Sur n’importe quel pouète qui est une réponse, tu peux survoler une petite flèche en haut à gauche pour voir le pouète d’origine, sans ouvrir tout le fil.
  • En parlant de fil, le petit signe plus permet d’afficher tout le fil tout en restant là où tu en étais dans ta colonne (ça insère le contenu à partir du pouète que tu as cliqué, donc une fois refermé, tu es de nouveau là où tu étais, tu ne perds pas le fil).
  • Tu peux cliquer sur l'avatar d'un utilisateur pour faire apparaitre son profil sans changer la vue, juste en insérant le profil au milieu de la colonne.
  • Tu peux mettre des signes distinctifs sur les gens ; par exemple, sur telle personne tu peux rajouter un liseré de la couleur que tu veux pour faire ressortir ses pouètes plus distinctement. (Je me suis amusé à mettre un fond rouge vif pour tous les gens de gauche que je suis.)

Autre avantage de Pleroma par rapport à Mastodon : le moteur de recherche cherche dans tous les messages, pas seulement ceux marqués avec un croisillon.

Outre l'instance https://pleroma.fr/ citée plus haut, il existe d'autres instances du fédivers utilisant Pleroma. La mienne est https://pleroma.bortzmeyer.fr/. Elle n'est pas ouverte au public, mais si vous demandez gentiment, je peux vous y ouvrir un compte. Les CGU de cette instance sont simples : « l'administrateur [moi] fait ce qu'il veut ». Bon, c'est pareil sur toutes les autres instances mais, ici, c'est moins hypocrite. (Sur une instance qui accepte les créations de compte à tou·te·s, pensez à éditer priv/static/static/terms-of-service.html pour indiquer les CGU.)

Le reste de cet article concerne plutôt les administrateurs système, qui voudraient installer une instance Pleroma sur leur machine.

Un des principaux intérêts de Pleroma par rapport à Mastodon est d'être très léger et de pouvoir tourner sur une petite machine. Dans mon cas, il s'agit d'un container LXC sur une Turris Omnia. J'ai installé Alpine Linux dessus, car c'est un système peu consommateur de ressources (et qui n'a pas systemd, chic).

Comme je n'ai qu'une seule adresse IPv4, le container n'a pas d'adresse IPv4 publique. Donc, j'ai configuré un relais sur la machine ayant l'adresse publique. Ce relais utilise nginx et sa configuration est celle proposée par Pleroma (installation/pleroma.nginx dans les sources de Pleroma), notamment :

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    ssl_certificate           /var/lib/dehydrated/certs/pleroma.bortzmeyer.fr/fullchain.pem;
    ssl_certificate_key       /var/lib/dehydrated/certs/pleroma.bortzmeyer.fr/privkey.pem;
    server_name pleroma.bortzmeyer.fr;

    # Redirection vers le container qui porte Pleroma
    location / {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;

        proxy_pass http://LE.CONTAINER.À.MOI:4000;
    }
    

Cela a également l'avantage de permettre de gérer les certificats Let's Encrypt (ce que Pleroma ne sait pas faire seul).

Pleroma n'est apparemment distribué que sous forme de code source. Il faut donc le compiler. Il est écrit en Elixir et il faut donc un compilateur Elixir. Sur Alpine, Elixir est dans la section community et pas main. Il faut donc ajouter l'URL de community à /etc/apk/repositories.

Ensuite, on télécharge les sources. Le site de référence est https://git.pleroma.social/pleroma/pleroma. Le téléchargement se fait via le répertoire des versions livrées, ou bien avec git si on veut la version de développement :

% git clone https://git.pleroma.social/pleroma/pleroma.git
    

Une fois que c'est fait, il n'y a plus qu'à suivre les instructions d'installation. (Si vous vous demandez ce qu'est ce mix, c'est l'outil de gestion de tâches d'Elixir. Un peu comme make pour C. Il permet entre autres d'installer les dépendances.)

% mix deps.get  
% mix pleroma.instance gen
What domain will your instance use? (e.g pleroma.soykaf.com) [] pleroma.bortzmeyer.fr
What is the name of your instance? (e.g. Pleroma/Soykaf) [] Unsafe
What is your admin email address? [] stephane+pleroma@bortzmeyer.org
What is the hostname of your database? [localhost] 
What is the name of your database? [pleroma_dev] pleroma
What is the user used to connect to your database? [pleroma] 
What is the password used to connect to your database? [autogenerated] 
Writing config to config/generated_config.exs. You should rename it to config/prod.secret.exs or config/dev.secret.exs.
Writing config/setup_db.psql.

To get started:
1. Verify the contents of the generated files.
2. Run `sudo -u postgres psql -f 'config/setup_db.psql'`.
3. Run `mv 'config/generated_config.exs' 'config/prod.secret.exs'`.
    

Arrivé à ce stade, le SGBD PostgreSQL doit être installé, et tourner, pour faire la suite.

% sudo -u postgres psql -f 'config/setup_db.psql'   
% mv 'config/generated_config.exs' 'config/prod.secret.exs'
    

On peut alors lancer à la main Pleroma (le code va être compilé par mix) :

%  mix phx.server    
    

Et voilà, votre Pleroma tourne. Essayez de vous connecter sur https://MON-INSTANCE/ pour voir. Si vous n'avez pas fait proprement la redirection depuis le relais nginx, vous aurez des choses bizarres, comme des boutons présents mais inactifs.

Pour activer Pleroma sur Alpine, avec OpenRC, on copie le script proposé, installation/init.d/pleroma en /etc/init.d/pleroma et on peut alors indiquer que Pleroma doit être lancé au démarrage, avec rc-update add pleroma. Si on veut le démarrer tout de suite, c'est rc-service pleroma start.

Notez que Pleroma permet de gérer facilement deux environnements, de production et de développement, en fonction de la variable d'environnement MIX_ENV. Comme je n'ai qu'un environnement, j'ai défini MIX_ENV=prod dans mes fichiers de configuration. Sinon, il faut y penser à chaque commande :

%    MIX_ENV=prod mix phx.server

Bon, Pleroma est maintenant démarré. Créons un·e utilisateurice :

    
% mix  pleroma.user new dominique dominique@example.net --password toto
   

Et voilà, dominique peut désormais se connecter via l'interface Web, et envoyer des pensées profondes et des photos de chats à tout le fédivers.

Vous trouvez que le mot de passe mis est vraiment trop nul ? Laissons l'utilisateur le changer mais on peut aussi l'aider :

% mix pleroma.user reset_password dominique

Où ai-je trouvé ces commandes ? Sur l'ancien Wiki, plus mis à jour mais qui contient des informations que je ne trouve pas facilement dans la documentation officielle (Haelwenn me signale que toutes ces tâches mix sont documentées ici.) On peut aussi voir toutes les commandes avec mix help pleroma.user. Et il y a bien sûr l'examen du source (lib/mix/tasks/pleroma/user.ex)… Maintenant qu'ielle a un bon mot de passe, dominique peut être nommé·e administrateurice :

% mix pleroma.user set dominique --admin    
   

Ou bien « modérateurice » (censeur) :

% mix pleroma.user set dominique --moderator
   

Il n'y a pas à ma connaissance de page Web pour l'administrateur d'une instance Pleroma. On édite le fichier de configuration (config/prod.secret.exs, qui est du code Elixir) et on tape des commandes comme celles ci-dessus. Pour changer l'apparence de son instance Pleroma, par exemple, on regarde cette page. Pour contrôler le robots.txt, voir cette documentation.

Qu'est-ce que Pleroma va afficher lors de son exécution ? Cela se règle également dans le fichier de configuration :

config :logger, level: :info

(Si vous êtes curieu·x·se, le deux-points indique un atome Elixir.) Et si on veut tous les détails (vraiment tous) :

config :logger, level: :debug

Un des intérêts d'avoir sa propre instance, est qu'on peut fouiller dans la base de données, au-delà de ce que permet l'interface, ce qui est pratique, les capacités de recherche de pouètes de Mastodon et de Pleroma étant limitées. On se connecte à la base PostgreSQL :

% sudo -u postgres psql pleroma

Et on peut alors taper les requêtes SQL de son choix (ici, émetteur et date d'arrivée du message) :

pleroma=# SELECT actor,inserted_at FROM activities ORDER BY inserted_at DESC LIMIT 10;
                    actor                     |     inserted_at     
----------------------------------------------+---------------------
 https://hostux.social/users/R1Rail           | 2019-04-24 15:30:38
 https://hostux.social/users/R1Rail           | 2019-04-24 15:27:30
 https://mastodon.gougere.fr/users/bortzmeyer | 2019-04-24 15:25:02
 https://hostux.social/users/R1Rail           | 2019-04-24 15:25:02
 https://mastodon.social/users/jpmens         | 2019-04-24 15:21:20
 https://mamot.fr/users/Shaft                 | 2019-04-24 15:21:20
 https://mastodon.host/users/federationbot    | 2019-04-24 15:18:58
 https://mastodon.host/users/federationbot    | 2019-04-24 15:18:12
 https://misskey.io/users/7rmk7gm7ew          | 2019-04-24 15:18:12
 https://mastodon.host/users/federationbot    | 2019-04-24 15:16:55
(10 rows)

Et si on veut tout le contenu en Activity Streams du pouète (ici, je restreins aux messages que j'ai envoyé) :


pleroma=# SELECT actor,inserted_at,data FROM activities WHERE actor LIKE 'https://pleroma.bortzmeyer.fr/%' ORDER BY inserted_at DESC LIMIT 10;
https://pleroma.bortzmeyer.fr/users/stephane | 2019-04-24 10:04:20 | {"cc": ["https://pleroma.bortzmeyer.fr/users/stephane/followers"], "id": "https://pleroma.bortzmeyer.fr/activities/586a3505-d8e7-4bb9-958c-18c18e098e7b", "to": ["https://www.w3.org/ns/activitystreams#Public"], "type": "Create", "actor": "https://pleroma.bortzmeyer.fr/users/stephane", "object": {"cc": ["https://pleroma.bortzmeyer.fr/users/stephane/followers"], "id": "https://pleroma.bortzmeyer.fr/objects/207538f4-759d-4126-bb0a-a41fcbe60129", "to": ["https://www.w3.org/ns/activitystreams#Public"], "tag": ["pleroma"], "type": "Note", "actor": "https://pleroma.bortzmeyer.fr/users/stephane", "emoji": {}, "content": "As an user, I can change the <a class='hashtag' data-tag='pleroma' href='https://pleroma.bortzmeyer.fr/tag/pleroma' rel='tag'>#Pleroma</a> theme at will but changing the default theme in the secret.exs file seems ignored. Why?", "context": "https://pleroma.bortzmeyer.fr/contexts/cd07dc37-4472-4e87-b157-90df23a00e8e", "summary": null, "published": "2019-04-24T10:04:20.185186Z", "attachment": [], "context_id": 174350, "announcements": ["https://mastodon.gougere.fr/users/bortzmeyer", "https://pleroma.fr/users/bortzmeyer"], "announcement_count": 2}, "context": "https://pleroma.bortzmeyer.fr/contexts/cd07dc37-4472-4e87-b157-90df23a00e8e", "published": "2019-04-24T10:04:20.177118Z", "context_id": 174350, "directMessage": false}
...

  

La colonne data contient l'objet Activity Streams, en JSON.

J'ai indiqué que j'utilisais la version de développement de Pleroma. Il y a donc du nouveau régulièrement et, après un git pull, il faut penser à charger les dépendances nouvelles :

% mix deps.get
   

Si le schéma de la base de données a changé, il faut également mettre à jour votre base :

% mix ecto.migrate
  

(Ecto est l'interface aux bases de données utilisé par Elixir.) Si vous avez un doute sur la nécessité de ces deux commandes, n'hésitez pas, lancez-les, elles sont inoffensives si le travail est déjà fait. Vous trouverez davantage d'informations sur la mise à jour de votre instance sur l'ancien Wiki (pas encore trouvé où c'était sur la documentation officielle).

Et si je veux hacker Pleroma sérieusement ? Voyez ce bon article, avec explications sur Elixir, Ecto, Mix, etc. Au passage, vous devrez apprendre Elixir donc les documentations sont là (dont une pour débutants.) Si vous trouvez des bogues dans Pleroma, signalez-les.

Enfin, pour finir, quelques articles et informations sur l'administration d'un serveur Pleroma :


L'article seul

RFC 8552: Scoped Interpretation of DNS Resource Records through "Underscored" Naming of Attribute Leaves

Date de publication du RFC : Mars 2019
Auteur(s) du RFC : D. Crocker (Brandenburg InternetWorking)
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 19 avril 2019


Une convention répandue pour les noms de domaine est de préfixer les services par un tiret bas, par exemple _xmpp-client._tcp.jabber.ietf.org. Cette pratique n'avait jamais été documentée mais c'est désormais fait. Et il existe désormais un registre IANA des noms ainsi préfixés.

Bien sûr, on peut mettre des ressources sous n'importe quel nom. Le DNS n'impose aucune restriction pour cela, et vous pouvez décider que le service X sera sous le nom $X%.example.com (si vous ne me croyez pas, relisez le RFC 1035 et RFC 2181). Mais les humains aiment les conventions, par exemple pour les machines, comme www comme préfixe d'un serveur Web (préfixe d'ailleurs contesté, souvent pour de mauvaises raisons) ou mail pour un serveur de messagerie. Ce ne sont que des conventions, le DNS s'en moque, et on peut mettre un serveur Web en mail.example.com si on veut, cela ne perturbera que les humains. D'autant plus qu'on peut utiliser n'importe quel type de données avec n'importe quel nom (par exemple un enregistrement MX pour www.example.org).

La convention du tiret bas initial est répandue, notamment parce qu'elle évite toute confusion avec les noms de machines, qui ne peuvent pas comporter ce caractère (RFC 952). Elle est donc très commune en pratique. Cette convention permet de restreindre explicitement une partie de l'arbre des noms de domaine pour certains usages. Comme ce RFC ne fait que documenter une convention, il ne nécessite aucun changement dans les logiciels.

Une alternative au tiret bas serait d'utiliser un type de données spécifique. Quant aux types « généralistes » comme TXT, ils ont l'inconvénient qu'on récupère, lors de la résolution DNS, des informations inutiles, par exemple les TXT des autres services. Bref, vous créez un nouveau service, mettons X, vous avez le choix, pour le cas du domaine parent example.org, entre :

  • Un nouveau type d'enregistrements DNS, nommons-le par exemple TYPEX (en pratique, c'est long et compliqué, et sans déploiement garanti, cf. RFC 5507),
  • Un type d'enregistrement générique comme TXT cité plus haut ou bien le URI du RFC 7553, menant à des ensembles d'enregistrements (RRset) potentiellement assez gros, problème détaillé en section 1.2,
  • Une convention de nommage comme x.example.org,
  • Une convention de nommage avec un tiret bas (_x.example.org), l'objet de ce RFC 8552.

Un exemple d'un service réel utilisant la convention avec le tiret bas est DKIM (RFC 6376), avec le préfixe _domainkey :


% dig +short TXT mail._domainkey.gendarmerie.interieur.gouv.fr
"v=DKIM1; k=rsa; t=y; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgwhYZeeZgM94IofX9uaGAwQ+tynFX7rYs/igs+d1afqrrjMaoKay11IMprRyqhcVurQtJwGfk7PAWrXRjB+KpdySH9lqzvbisB3GrSs1Sf4uWscAbZ+saapw3/QD8YFyfYbsXunc6HROJQuQHM+U56OOcoiAiPHpfepAmuQyFQIDAQAB"
      
    

Comme beaucoup de choses, la convention « tiret bas » s'entend mal avec les jokers du DNS. D'abord, on ne peut pas utiliser les jokers entre le préfixe et le reste du nom (_x.*.example.net ne marche pas), ensuite, un joker couvre également les noms avec tiret bas donc *.example.net va répondre positivement pour _x.example.net même si on ne le voulait pas.

La section 1.5 de notre RFC détaille l'histoire de la convention « tiret bas au début ». Beaucoup de services utilisaient cette convention mais sans coordination, et sans qu'il existe une liste complète. Du fait de l'existence de plusieurs choix possibles (énumérés plus haut), ce RFC n'a pas obtenu de consensus immédiatement et les débats ont été longs et compliqués.

La section 2 du RFC explique comment remplir le nouveau registre des noms à tiret bas. On ne met dans ce registre que le nom le plus proche de la racine du DNS. Si un service mène à des noms comme _foo._bar.example.org, seul le _bar sera mis dans le registre. C'est particulièrement important pour le cas des enregistrements SRV qui ont souvent deux niveaux de noms préfixés (par exemple _sip._tcp.cisco.com). Seul le nom le plus proche de la racine, ici _tcp, est enregistré (ici, _sip est quand même enregistré car il peut en théorie être utilisé sans le _tcp mais il me semble que c'est rare en pratique).

Les règles pour les noms plus spécifiques sous le _bar (ou _tcp) sont spécifiées lors de la description du service en question. Par exemple, pour DKIM, le RFC 6376 précise que que sous le nom _domainkey, on trouve un sélecteur dont l'identificateur apparait dans le courrier signé. Donc, pour un message envoyé avec s=mail et d=gendarmerie.interieur.gouv.fr, on cherche les informations DKIM en mail._domainkey.gendarmerie.interieur.gouv.fr.

Le formulaire pour demander l'enregistrement d'un nouveau nom préfixé par un tiret bas figure en section 3 du RFC. Il faut indiquer le type de données DNS (un enregistrement n'est valable que pour un certain type, donc la clé du registre est un couple {type, nom}), le nom et la référence du document décrivant le service en question. Le registre est décrit en section 4 du RFC. L'ajout dans ce registre se fait selon la politique « examen par un expert » (RFC 8126, section 4.5). La section 5 de notre RFC donne quelques indications à l'IANA sur cet examen.

Un ensemble d'entrées à ajouter pour initialiser ce nouveau registre est indiqué. On y trouve par exemple {TXT, _domainkey} pour DKIM, {TLSA, _tcp} pour DANE (RFC 6698), {TXT, _acme-challenge} pour ACME (RFC 8555), etc. Deux cas particuliers : le nom _example est réservé pour tous les types d'enregistrement, lorsqu'on a besoin de donner un exemple, sans spécifier un cas réel, et le nom _ta, qui sert au mécanisme de signalement des clés DNSSEC du RFC 8145, désigne en fait tous les noms commençant par _ta.


Téléchargez le RFC 8552


L'article seul

RFC 8553: DNS Attrleaf Changes: Fixing Specifications That Use Underscored Node Names

Date de publication du RFC : Mars 2019
Auteur(s) du RFC : D. Crocker (Brandenburg InternetWorking)
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 19 avril 2019


Autrefois, de nombreux services et protocoles Internet avaient « réservé » de manière informelle, et sans enregistrement de cette réservation, des noms préfixés par un tiret bas, comme _submission._tcp.example.net (cf. RFC 6186 pour cet exemple). Comme le RFC 8552 a mis fin à cette activité en créant un registre officiel des noms préfixés, il fallait réviser les normes existantes pour s'aligner sur les nouvelles règles. C'est le but de ce RFC 8553 qui modifie pas moins de trente-trois RFC !

Dans le nouveau registre, les entrées sont indexées par un couple {type d'enregistrement DNS, nom}. Par exemple, {TXT, _dmarc} pour DMARC (RFC 7489).

Les enregistrements SRV (RFC 2782) et URI (RFC 7553) posent un problème supplémentaire puisqu'ils utilisent un autre registre de noms, celui des noms de protocoles et services (dit aussi registre des numéros de ports) décrit dans le RFC 6335.

La section 2 du RFC décrit les usages actuels des noms préfixés par le tiret bas. Les enregistrements de type TXT, par exemple, sont utilisés dans sept RFC différents, comme le RFC 5518. Et les SRV dans bien davantage.

Enfin la section 3 du RFC contient le texte des changements qui est fait aux différentes spécifications utilisant les noms préfixés. (Il s'agit essentiellement de faire référence au nouveau registre du RFC 8552, il n'y a pas de changement technique.)


Téléchargez le RFC 8553


L'article seul

Cours HTTP au CNAM

Première rédaction de cet article le 18 avril 2019
Dernière mise à jour le 19 avril 2019


Le 17 avril 2019, c'était la première édition de mon cours HTTP de deux heures au CNAM. Pour l'anecdote, c'était dans le bâtiment où il y avait eu l'un des premiers serveurs HTTP publics en France.

Voici les supports de l'exposé :

Tout a été filmé et la vidéo est disponible :

J'ai fait aussi un cours BGP et un cours DNS au même endroit.

Merci à Éric Gressier pour l'idée et l'organisation, et aux élèves pour avoir posé plein de questions pas toujours faciles.


L'article seul

RFC 8574: cite-as: A Link Relation to Convey a Preferred URI for Referencing

Date de publication du RFC : Avril 2019
Auteur(s) du RFC : H. Van de Sompel (Data Archiving and Networked Services), M. Nelson (Old Dominion University), G. Bilder (Crossref), J. Kunze (California Digital Library), S. Warner (Cornell University)
Pour information
Première rédaction de cet article le 18 avril 2019


Ce RFC décrit un nouveau type de liens hypertexte permettant d'indiquer l'URI sous lequel on préfère qu'une ressource soit accédée, à des fins de documentation ou de citation précise.

Imaginons que vous accédez à un article scientifique en ligne. Vous utilisez un URI qui identifie cet article. Vous voulez ensuite citer cet article dans un de vos textes. Allez-vous utiliser l'URI d'accès ? Ce n'est pas forcément le meilleur, par exemple ce n'est pas forcément le plus stable sur le long terme. Le lien « cite avec » permet au serveur d'indiquer l'URI le plus pertinent pour une citation.

Ce RFC s'appuie sur la formalisation du concept de lien faite dans le RFC 8288. « Contexte » et « cible » sont donc utilisés comme dans cette norme, le contexte d'un lien étant le point de départ et la cible l'arrivée. En prime, notre RFC 8574 définit deux termes, l'URI d'accès, celui utilisé pour accéder à une ressource (par exemple une page Web) et l'URI de référence, celui qu'il faudrait utiliser pour la citation.

La section 3 du RFC décrit quelques scénarios d'usage. Le premier est celui des identificateurs stables. Normalement, lorsque le ou la webmestre est compétent(e) et sérieux(se), les URI sont stables, comme précisé dans l'article « Cool URIs don't change. Mais, en pratique, beaucoup de webmestres sont incompétents ou paresseux. Cela a mené à des « solutions » fondées sur la redirection, où il apparait une différence entre URI d'accès et URI de référence. C'est le cas avec des techniques comme les DOI (« use the Crossref DOI URL as the permanent [reference] link »), PURL ou ARK. Dans les trois cas, au lieu de gérer proprement les URI, on utilise un redirecteur supposé plus stable (alors que rien ne le garantit) et on souhaite utiliser comme URI de référence l'URI du redirecteur (donnant ainsi des pouvoirs démesurés à des organisations privées comme l'IDF, qui matraque régulièrement l'importance de n'utiliser que leurs identificateurs).

Un autre scénario d'usage est celui des ressources versionnées. C'est par exemple le cas de Wikipédia. La page Wikipédia sur l'incendie de Notre-Dame de Paris change souvent en ce moment. Comme toutes les pages Wikipédia, chaque version à un identificateur, et on peut se référer à une version particulier. Si https://fr.wikipedia.org/wiki/Incendie_de_Notre-Dame_de_Paris renvoie à la dernière version, sans cesse en mouvement, https://fr.wikipedia.org/w/index.php?title=Incendie_de_Notre-Dame_de_Paris&oldid=158468007 renvoie uniquement à la toute première version, très sommaire et https://fr.wikipedia.org/w/index.php?title=Incendie_de_Notre-Dame_de_Paris&oldid=158478416 à une version intermédiaire déjà très fournie.

Souvent, quand on veut citer un article de Wikipédia, on veut se mettre à l'abri de changements ultérieurs, pas forcément positifs, et on souhaite donc citer exactement une version particulière. On clique donc sur « Lien permanent » (ou bien « Voir l'historique » puis sur la date la plus récente) pour avoir l'URI de la version qu'on vient de regarder. (Notez aussi le très utile lien « Citer cette page ».)

Troisième cas d'usage cité, celui des identifiants sur les réseaux sociaux. M. Toutlemonde a typiquement plusieurs pages le décrivant sur ces réseaux (dans mon cas, https://mastodon.gougere.fr/@bortzmeyer, https://twitter.com/bortzmeyer, https://www.linkedin.com/in/sbortzmeyer/, https://seenthis.net/people/stephane et sans doute bien d'autres que j'ai oubliés, et ceux que j'ai eu la flemme de faire, comme FOAF). Or, on pourrait souhaiter décider qu'un de ces profils est meilleur que les autres, par exemple parce qu'il est plus directement contrôlé par l'utilisateur, ou mieux maintenu. Il serait alors intéressant d'indiquer lors de l'accès à chacun des autres profils quel est le profil de référence. (Le RFC est très irréaliste là-dessus : je vois mal un réseau « social » capitaliste permettre à ses utilisateurs de dire « allez plutôt voir là ».)

Enfin, un dernier cas d'usage est celui d'une publication composée de plusieurs ressources (par exemple un livre où chaque chapitre est accessible séparement, avec son propre URI). On souhaite alors que l'accès à chaque ressource indique, à des fins de citation, l'URI de référence (par exemple la page d'accueil).

La section 4 du RFC présente la solution : un nouveau type de lien, cite-as, qui permet de dire quel est l'URI de référence. (Le RFC recommande d'ailleurs que cet URI soit un URL : le but est d'accéder à la ressource !) Il est évidemment recommandé qu'il n'y ait qu'un seul lien de type cite-as. Ce lien n'interdit pas d'utiliser d'autres URI, il indique seulement quel est l'URI que le webmestre souhaite voir utilisé dans les références webographiques. cite-as est désormais dans le registre IANA des types de liens.

La section 6 du RFC donne des exemples concrets, puisque les liens peuvent se représenter de plusieurs façons. Par exemple, l'article de PLOS One auquel vous accédez en https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0171057 pourrait contenir, en HTML, le lien avec l'attribut rel="cite-as" :


 <link rel="cite-as"
           href="https://doi.org/10.1371/journal.pone.0171057" />    

Cela indiquerait que les auteurs préfèrent être cités par le DOI (une mauvaise idée, mais c'est une autre histoire).

Autre exemple de syntaxe concrète pour les liens, imaginé pour arXiv, pour des articles avec versionnement, un lien dans un en-tête HTTP pour https://arxiv.org/abs/1711.03787, qui pourrait indiquer qu'on est en train de regarder la première version, « v1 » (il existe une « v2 », essayez) :


HTTP/1.1 200 OK
Date: Sun, 24 Dec 2017 16:12:43 GMT
Content-Type: text/html; charset=utf-8
Link: <https://arxiv.org/abs/1711.03787v1> ; rel="cite-as"

    

Comme arXiv garde les différentes versions successives d'un article, cela permettrait de récupérer la version actuelle tout en sachant comment la référencer.

Revenons au HTML pour l'exemple des profils sur les réseaux sociaux, imaginons un utilisateur, John Doe, qui place dans le code HTML de sa page personnelle un lien vers son profil FOAF en Turtle :


<html>
    <head>
    ...
     <link rel="cite-as" href="http://johndoe.example.com/foaf"
           type="text/turtle"/>
    ...
    </head>
    <body>
...

    

Et un dernier exemple, celui d'une publication composée de plusieurs ressources. Ici, l'exemple est Dryad une base de données scientifiques qui permet l'accès à des fichiers individuels, mais où chaque jeu de données a un identificateur canonique. En HTTP, cela donnerait, lorsqu'on accès à https://datadryad.org/bitstream/handle/10255/dryad.98509/PIPFIL_M_BCI.csv (un fichier CSV qui fait partie de cette base de données) :


HTTP/1.1 200 OK
Date: Tue, 12 Jun 2018 19:19:22 GMT
Last-Modified: Wed, 17 Feb 2016 18:37:02 GMT
Content-Type: text/csv;charset=ISO-8859-1
Link: <https://doi.org/10.5061/dryad.5d23f> ; rel="cite-as"

    

Le fichier CSV est membre d'un jeu de données plus général, dont l'URI de référence est https://doi.org/10.5061/dryad.5d23f.

Ainsi, dans un monde idéal, un logiciel qui reçoit un lien cite-as pourrait :

  • Lorsqu'il garde un signet, utiliser l'URI de référence,
  • Identifier plusieurs URI d'accès comme ayant le même URI de référence, par exemple à des fins de comptage,
  • Indexer les ressources par plusieurs URI.

D'autres solutions avaient été proposées pour résoudre ce problème de l'URI de référence. La section 5 de notre RFC les énumère. Il y avait notamment cinq autres types de liens qui auraient peut-être pu convenir, alternate, duplicate, related, bookmark et canonical.

Les trois premiers sont vite éliminés. alternate (RFC 4287) décrit une autre représentation de la même ressource (par exemple la même vidéo mais encodée différemment). duplicate (RFC 6249) désigne une reproduction identique (et cela ne traite donc pas, par exemple, le cas d'une publication composée de plusieurs ressources). Quant à related (RFC 4287), sa sémantique est bien trop vague. Un article des auteurs du RFC décrit en détail les choix de conceptions et explique bien le problème. (Je trouve cet article un peu gâché par les affirmations sans preuves comme quoi les DOI seraient « permanents ». Si le registre disparait ou fait n'importe quoi, il y aura le même problème avec les DOI qu'avec n'importe quelle autre famille d'identificateurs.)

Le cas de bookmark (normalisé par le W3C) est plus compliqué. Il est certainement proche en sémantique de cite-as mais ne peut pas être présent dans les en-têtes HTTP ou dans la tête d'une page HTML, ce qui en réduit beaucoup l'intérêt. Le cas compliqué de bookmark est décrit dans un autre article des auteurs du RFC.

Enfin, le cas de canonical (RFC 6596). Ce dernier semble trop restreint d'usage pour les utilisations prévues pour cite-as. Et il n'a pas vraiment la même sémantique. Par exemple, pour les ressources versionnées, canonical indique la plus récente, exactement le contraire de ce qu'on voudrait avec cite-as. Et c'est bien ainsi que l'utilise Wikipédia. Si je récupère https://fr.wikipedia.org/w/index.php?title=Incendie_de_Notre-Dame_de_Paris&oldid=158478416 :

      
<link rel="canonical" href="https://fr.wikipedia.org/wiki/Incendie_de_Notre-Dame_de_Paris"/>

    

On voit que canonical renvoie à la dernière version. Le cas de canonical fait lui aussi l'objet d'un article détaillé.

Je n'ai pas mis de tels liens sur ce blog, ne voyant pas de cas où ils seraient utiles.


Téléchargez le RFC 8574


L'article seul

RFC 8509: A Root Key Trust Anchor Sentinel for DNSSEC

Date de publication du RFC : Décembre 2018
Auteur(s) du RFC : G. Huston, J. Damas (APNIC), W. Kumari (Google)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 14 avril 2019


On le sait, le premier changement de la clé DNSSEC de la racine du DNS s'est déroulé sans encombre le 11 octobre dernier. Ce qu'on sait moins est que ce changement a été précédé par des nombreuses études, pour tenter de prévoir les conséquences du changement. Une question importante était « combien de résolveurs DNS n'ont pas vu la nouvelle clé, depuis sa publication, et vont donc avoir des problèmes lorsque l'ancienne sera retirée du service ? » Ce RFC décrit une des techniques qui avaient été développées pour répondre à cette question, technique qui continuera à être utile pour les discussions actuellement en cours sur une éventuelle systématisation et accélération du rythme des changements de clé.

La question de cette systématisation a fait par exemple l'objet d'un débat à la dernière réunion IETF à Prague le 28 mars. L'idée est de voir si on peut passer de changements exceptionnels et rares à des changements réguliers, banalisés. Pour cela, il faut avoir une idée de ce que voient les résolveurs DNS, du moins ceux qui valident avec DNSSEC, technique dont l'importance avait encore été démontré par les attaques récentes. Mais comment savoir ce que voient les résolveurs et, notamment, quelle(s) clé(s) de départ de la validation (trust anchor) ils utilisent ? La solution de la sentinelle, spécifiée dans ce nouveau RFC, peut permettre de répondre à cette question. L'idée est que les résolveurs reconnaitront une requête « spéciale », dont le nom commence par root-key-sentinel-is-ta ou root-key-sentinel-not-ta, nom qui sera suivi de l'identificateur (key tag) de la clé (ta = Trust Anchor). La réponse du résolveur dépendra de s'il utilise cette clé ou pas comme point de départ de la validation. Un logiciel client pourra alors tester si son résolveur a bien vu le changement de clé en cours, et est prêt. L'utilisateur peut alors savoir si ce résolveur fonctionnera lors du changement. (Cela peut aussi aider l'administrateurice système mais celui-ci ou celle-là a d'autres moyens pour cela, comme d'afficher le fichier contenant la clé de départ de la validation. Par contre, les sentinelles sont utiles pour les chercheurs qui récoltent des données automatiquement, comme dans l'article de Huston cité à la fin.)

Ce mécanisme de sentinelle est complémentaire de celui du RFC 8145, où le résolveur signale aux serveurs faisant autorité les clés qu'il utilise comme trust anchor. Ici, la cible est le client du résolveur, pas les serveurs faisant autorité que contacte le résolveur. Ce mécanisme de sentinelle permet à tout utilisateur de savoir facilement la(es) clé(s) utilisée(s) par son résolveur DNS.

Petit rappel sur DNSSEC d'abord : comme le DNS, DNSSEC est arborescent. La validation part en général de la racine, et, via les enregistrements DS, arrive à la zone qu'on veut valider. Les clés sont identifiées par un key tag qui est un condensat de la clé. La clé qui est le point de départ de la validation se nomme le trust anchor et est stockée par le résolveur. Si on a la mauvaise clé, la validation échouera. Le trust anchor est géré manuellement par l'administrateur système en charge du résolveur, ou peut être mise à jour automatiquement en suivant la technique du RFC 5011. Aujourd'hui, le résolveur sur la machine où j'écris cet article utilise la trust anchor ayant le key tag 20326, la clé publique de la racine IANA (le résolveur est un Unbound) :

% cat /var/lib/unbound/root.key
...
.	172800	IN	DNSKEY	257 3 8 AwEAAaz/tAm8y...1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b} ;;state=2 [  VALID  ] ;;count=0 ;;lastchange=1502474052 ;;Fri Aug 11 19:54:12 2017
    

Le id = 20326 indique l'identificateur de la clé.

La section 2 expose le cœur du RFC. Un résolveur DNS, validant avec DNSSEC (RFC 4035) et qui met en œuvre ce RFC, doit reconnaitre comme spéciaux, les noms de domaine demandés qui commencent par root-key-sentinel-is-ta-NNNNN et root-key-sentinel-not-ta-NNNNN où NNNNN est un identificateur de clé. Voyons un exemple avec un domaine de test, dans lequel on trouve root-key-sentinel-is-ta-20326.ksk-test.net et root-key-sentinel-not-ta-20326.ksk-test.net (20326 est l'identificateur de la clé actuelle de la racine). Tout résolveur validant qui utilise la clé 20326 va pouvoir récupérer et valider le premier nom :

      
% dig root-key-sentinel-is-ta-20326.ksk-test.net  
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23817
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 3, ADDITIONAL: 1
...
;; ANSWER SECTION:
root-key-sentinel-is-ta-20326.ksk-test.net. 30 IN A 204.194.23.4

    

Ici, le ad dans les résultats indique que l'enregistrement a bien été validé (AD = Authentic Data). Si le résolveur ne valide pas, on n'aura évidemment pas le ad. Maintenant, que se passe-t-il avec le second nom, celui avec not-ta ? Un résolveur validant, mais qui ne met pas en œuvre notre RFC 8509 va récupérer et valider ce nom :


% dig root-key-sentinel-not-ta-20326.ksk-test.net  
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20409
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 3, ADDITIONAL: 1
...
;; ANSWER SECTION:
root-key-sentinel-not-ta-20326.ksk-test.net. 30	IN A 204.194.23.4

    

En revanche, un résolveur validant et qui met en œuvre ce RFC 8509 (ici, le résolveur Knot) va renvoyer un SERVFAIL (Server Failure) :


% dig root-key-sentinel-not-ta-20326.ksk-test.net     
...
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 37396
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0

    

On voit comment on peut utiliser le mécanisme de ce RFC pour découvrir si le résolveur utilise ou pas la clé 20326. Avec un client DNS, et une zone qui dispose des sentinelles :

  • Si on peut récupérer root-key-sentinel-not-ta-20326 et qu'on n'a pas le bit ad, c'est que le résolveur ne valide pas,
  • Si on peut récupérer et valider root-key-sentinel-not-ta-20326, c'est que le résolveur valide mais ne gère pas le mécanisme des sentinelles,
  • Si on peut récupérer et valider root-key-sentinel-is-ta-20326 mais pas root-key-sentinel-not-ta-20326, c'est que le résolveur valide, gère le mécanisme des sentinelles, et utilise la clé 20326,
  • Si on peut récupérer et valider root-key-sentinel-not-ta-20326 mais pas root-key-sentinel-is-ta-20326, c'est que le résolveur valide, gère le mécanisme des sentinelles, et n'utilise pas la clé 20326. (Ce test ne marche pas forcément, cela dépend de comment est configurée la zone de test.)

L'annexe A de notre RFC détaille comment on utilise le mécanisme de sentinelle.

La beauté du mécanisme est qu'on peut l'utiliser sans client DNS, depuis une page Web. On crée une page qui essaie de charger trois images, une depuis invalid.ZONEDETEST (enregistrement mal signé), une depuis root-key-sentinel-is-ta-20326.ZONEDETEST et une depuis root-key-sentinel-not-ta-20326.ZONEDETEST. On teste en JavaScript si les images se sont chargées :

  • Si toutes se chargent, c'est que le résolveur de l'utilisateur ne valide pas,
  • Si la première (invalid) ne charge pas mais que les deux autres chargent, c'est que le résolveur ne connait pas les sentinelles, on ne peut donc pas savoir quelle(s) clé(s) il utilise,
  • Si la première (invalid) et la troisième (root-key-sentinel-not-ta-20326) ne se chargent pas mais que la deuxième (root-key-sentinel-is-ta-20326) se charge, c'est que le résolveur connait les sentinelles, et utilise la clé 20326.
  • Si la première (invalid) et la deuxième (root-key-sentinel-is-ta-20326) ne se chargent pas mais que la troisième (root-key-sentinel-not-ta-20326) se charge, c'est que le résolveur connait les sentinelles, et n'utilise pas la clé 20326. Soit il utilise une autre racine que celle de l'ICANN, soit il a gardé une ancienne clé et aura des problèmes lors du remplacement.

Notez que le choix des préfixes avait été chaudement discuté à l'IETF. À un moment, l'accord s'était fait sur les préfixes _is-ta ou _not-ta, le tiret bas convenant bien à ces noms spéciaux. Mais comme ce même tiret bas rendait ces noms ilégaux comme noms de machine, cela rendait difficile certains tests. Autant _is-ta ou _not-ta étaient utilisables depuis dig, autant on ne pouvait pas les tester depuis un navigateur Web, ce qui rendait difficile des tests semi-automatisés, sous formes d'images qu'on charge depuis une page Web et de JavaScript qui regarde le résultat. D'où les noms root-key-sentinel-is-ta-NNNNN et root-key-sentinel-not-ta-NNNNN. Ce n'est pas une solution satisfaisante que de prendre des noms ne commençant pas par un tiret bas, mais cela a semblé le meilleur compromis possible. Le nom est suffisamment long et alambiqué pour que le risque de collisions avec des noms existants soit faible.

Donc, que doit faire le résolveur quand on l'interroge pour un nom commençant par root-key-sentinel-is-ta-NNNNN ou root-key-sentinel-not-ta-NNNNN ? Si et seulement si le type demandé est A (adresse IPv4) ou AAAA (adresse IPv6), et que la réponse est validée par DNSSEC, le résolveur doit extraire le key tag du nom et déterminer s'il corresponde à une des clés de départ de la validation pour la racine. (Les serveurs faisant autorité, eux, n'ont rien de particulier à faire, ils fonctionnent comme aujourd'hui.)

  • Si le nom demandé commençait par root-key-sentinel-is-ta et que l'identificateur de clé est celui d'une trust anchor, alors on renvoie la réponse originale,
  • Si le nom demandé commençait par root-key-sentinel-is-ta et que l'identificateur n'est pas celui d'une trust anchor, alors on renvoie le code d'erreur SERVFAIL,
  • Si le nom demandé commençait par root-key-sentinel-not-ta et que l'identificateur est celui d'une trust anchor, alors on renvoie le code d'erreur SERVFAIL,
  • Si le nom demandé commençait par root-key-sentinel-not-ta et que l'identificateur n'est pas celui d'une trust anchor, alors on renvoie la réponse originale.

Le principe est simple, les sections suivantes du RFC décrivent comment déduire l'état du résolveur avec ce principe. Par exemple, la section 3 décrit le cas où on n'a qu'un seul résolveur. Si on veut connaitre la situation de la clé 12345, et que les noms nécessaires sont présents dans le domaine xxx.example, que le domaine broken.xxx.example est cassé, question DNSSEC, on cherche à résoudre les noms root-key-sentinel-is-ta-12345.xxx.example, root-key-sentinel-not-ta-12345.xxx.example et broken.xxx.example. Pour un résolveur validant, et qui met en œuvre ce RFC, seule la première requête doit réussir. Si la troisième réussit, c'est que le résolveur ne valide pas. Si la deuxième réussit, c'est qu'il ne connait pas le système sentinelle de notre RFC 8509. (L'algorithme détaillé est dans la section 3 du RFC, il y a quelques cas vicieux.)

Notez bien qu'on n'a pas besoin d'un client DNS complet pour ces tests. Une résolution de nom en adresse normale suffit, ce qui permet de faire ces tests depuis un navigateur Web, ce qui était bien le but. Par exemple en HTML :


     <ul>
      <li><a href="http://invalid.ksk-test.net/invalid.gif">"http://invalid.ksk-test.net/invalid.gif"</a></li>
      <li><a href="http://root-key-sentinel-is-ta-20326.ksk-test.net/is-ta.gif">"http://root-key-sentinel-is-ta-20326.ksk-test.net/is-ta.gif"</a></li>
      <li><a href="http://root-key-sentinel-not-ta-20326.ksk-test.net/not-ta.gif">"http://root-key-sentinel-not-ta-20326.ksk-test.net/not-ta.gif"</a></li>
    </ul>

    

Et avec du JavaScript pour vérifier :

      
      if (img_invalid.height === 0) {invalid = false;}
      if (img_is_ta.height === 0) {is_ta = false;}
      if (img_not_ta.height === 0) {not_ta = false;}

      switch (true) {
        case invalid === true:
          result="You are not DNSSEC validating, and so will be fine!";
        break;
        case (is_ta === true && not_ta === true):
          result="You are using a legacy resolver (or updated resolvers, with some new and some old), we cannot determine your fate!";
        break;
        case (not_ta === true):
           result="WARNING!: Your resolvers do not have the new KSK. Your Internet access will break!"; 
        break;
        case (is_ta === true):
           result="Congratulations, you have the new key. You will be fine.";
        break;
      }

    

Si la machine a plusieurs résolveurs, le cas est plus compliqué par la bibliothèque qui fait la résolution de noms va typiquement passer au résolveur suivant en cas de réponse SERVFAIL. La section 4 décrit un algorithme qui peut permettre, sinon de déterminer la situation exacte de chacun des résolveurs utilisés, en tout cas de voir si une clé donnée a des chances de fonctionner avec cet ensemble de résolveurs.

Quels résolveurs ont ce mécanisme de sentinelle ? BIND l'a depuis la version 9.13, Knot a également ce mécanisme, activé par défaut, depuis la version 2.0 (et le documente), Unbound en dispose depuis la version 1.7.1 et c'est activé par défaut. Parmi les résolveurs DNS publics, Cloudflare et Quad9 ne gèrent apparemment pas les sentinelles de notre RFC 8509.

Côté client, on peut tester son résolveur avec dig, ou bien avec les services Web http://www.ksk-test.net, http://test.kskroll.dnssec.lab.nic.cl/, http://sentinel.research.icann.org/ (le code derrière ce service est publié, notez que les résultats sont présentés en utilisant des codes spécifiques au RFC, pas très lisibles) ou http://www.bellis.me.uk/sentinel/ (utilise le domaine ksk-test.net). La lecture des sources de ces pages est recommandée.

On peut aussi regarder, parmi les sondes RIPE Atlas, lesquelles utilisent un résolveur avec sentinelles :

    
% blaeu-resolve --displayvalidation --type A --requested 100 --dnssec root-key-sentinel-not-ta-20326.ksk-test.net                                             
[ (Authentic Data flag)  204.194.23.4] : 30 occurrences 
[ERROR: SERVFAIL] : 12 occurrences 
[ (Authentic Data flag)   (TRUNCATED - EDNS buffer size was 4096 )  204.194.23.4] : 1 occurrences 
[204.194.23.4] : 52 occurrences 
Test #20692175 done at 2019-04-13T16:32:15Z

% blaeu-resolve --displayvalidation --type A --requested 100 --old_measurement 20692175 --dnssec root-key-sentinel-is-ta-20326.ksk-test.net                                                   
[ERROR: SERVFAIL] : 1 occurrences 
[ (Authentic Data flag)  204.194.23.4] : 38 occurrences 
[204.194.23.4] : 56 occurrences 
Test #20692176 done at 2019-04-13T16:33:56Z

Le premier test montre 52 sondes utilisant un résolveur non validant, 30 utilisant un validant sans sentinelles (ou bien utilisant une autre clé que 20326), et 12 utilisant un résolveur validant avec sentinelles. Le second test, effectué avec exactement les mêmes sondes, montre que les sondes utilisant un résolveur validant à sentinelles utilisent bien la clé 20326 (sauf une, qui a un SERVFAIL). Notez que les nombres ne coïncident pas parfaitement (30 + 12 - 1 ne fait pas 38), sans doute parce que certaines sondes ont plusieurs résolveurs DNS configurés, ce qui complique sérieusement l'analyse.

Un exemple d'utilisation de ce mécanisme de sentinelles figure dans cet article de Geoff Huston et cet autre.


Téléchargez le RFC 8509


L'article seul

RFC 8544: Organization Extension for the Extensible Provisioning Protocol (EPP)

Date de publication du RFC : Avril 2019
Auteur(s) du RFC : L. Zhou (CNNIC), N. Kong (Consultant), J. Wei, J. Yao (CNNIC), J. Gould (Verisign)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 12 avril 2019


Le RFC 8543 étendait le format utilisé par le protocole d'avitaillement EPP, afin d'ajouter le concept d'« organisation », une entreprise, association ou agence qui joue un rôle dans la création et gestion d'objets enregistrés, notamment les noms de domaine. Ce RFC 8544 ajoute une extension au protocole EPP pour affecter ces organisations à un domaine, contact ou autre objet enregistré.

Prenons l'exemple le plus connu (quoique EPP puisse servir à d'autres), celui de l'industrie des noms de domaine. Souvent, le registre reçoit des demandes d'un BE, via le protocole EPP. Mais d'autres organisations peuvent jouer un rôle, en plus du BE. Il y a par exemple l'hébergeur DNS (qui n'est pas forcément le BE) ou bien un revendeur du BE, ou bien un « anonymisateur » qui, pour protéger la vie privée des participants, est placé entre le participant et le monde extérieur. Ces différents acteurs (cf. RFC 8499, section 9, pour la terminologie) sont décrits par les nouvelles classes d'objets du RFC 8543. Notre RFC 8544 permet d'utiliser ces classes. Une fois les objets « organisation » créés au registre, on peut les attacher à un nom de domaine ou à un contact, par exemple pour dire « ce nom de domaine a été acheté via le revendeur X ».

L'espace de noms XML est urn:ietf:params:xml:ns:epp:orgext-1.0 (et est enregistré dans le registre IANA). L'extension à EPP est notée dans le registre des extensions EPP. Dans les exemples qui suivent, l'espace de noms est abrégé orgext. Les organisations ont un identificateur (le <org:id> du RFC 8543), et cet identificateur sera un attribut <orgext:id> des objets comme par exemple le domaine. Pour chaque rôle (revendeur, hébergeur DNS, etc, cf. RFC 8543, section 7.3), le domaine a au plus un attribut identifiant une organisation.

La section 4 du RFC décrit les ajouts aux commandes et réponses EPP. Par exemple, pour <info>, la commande ne change pas mais la réponse a désormais en plus des attributs <orgext:id>. Voici un exemple :


<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
  <response>
   <result code="1000">
      <msg lang="en-US">Command completed successfully</msg>
    </result>
    <resData>
      <domain:infData
        xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
        <domain:name>example.com</domain:name>
        ...
    </resData>
    <extension>
      <orgext:infData
        xmlns:orgext="urn:ietf:params:xml:ns:epp:orgext-1.0">
        <orgext:id role="reseller">reseller1523</orgext:id>
        <orgext:id role="privacyproxy">proxy2935</orgext:id>
      </orgext:infData>
    </extension>
    <trID>
      <clTRID>ngcl-IvJjzMZc</clTRID>
      <svTRID>test142AWQONJZ</svTRID>
    </trID>
  </response>
</epp>

    

Ici, le domaine a été avitaillé via le revendeur « reseller1523 » et est protégé par l'« anonymisateur » « proxy2935 ».

Bien sûr, la commande EPP <create> est également modifiée, pour pourvoir créer un domaine avec les organisations associées. Ici, le revendeur « reseller1523 » crée un domaine :


<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
  <command>
    <create>
      <domain:create
        xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
        <domain:name>example.com</domain:name>
        <domain:period unit="y">3</domain:period>
        ...
    </create>
    <extension>
      <orgext:create
        xmlns:orgext="urn:ietf:params:xml:ns:epp:orgext-1.0">
        <orgext:id role="reseller">reseller1523</orgext:id>
      </orgext:create>
    </extension>
  </command>
</epp>

De le même façon, on peut mettre à jour les organisations associées à un objet, avec <update>. Ici, on ajoute un « anonymiseur » :

      
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
  <command>
    <update>
      <domain:update
        xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
        <domain:name>example.com</domain:name>
      </domain:update>
    </update>
    <extension>
      <orgext:update
        xmlns:orgext="urn:ietf:params:xml:ns:epp:orgext-1.0">
        <orgext:add>
            <orgext:id role="privacyproxy">proxy2935</orgext:id>
        </orgext:add>
      </orgext:update>
    </extension>
  </command>
</epp>

    

Et ici on retire le revendeur (pas besoin d'indiquer son identificateur, rappelez-vous qu'il ne peut y avoir qu'une seule organisation par rôle) :


<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
  <command>
    <update>
      <domain:update
        xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
        <domain:name>example.com</domain:name>
      </domain:update>
    </update>
    <extension>
      <orgext:update
        xmlns:orgext="urn:ietf:params:xml:ns:epp:orgext-1.0">
        <orgext:rem>
          <orgext:id role="reseller"/>
        </orgext:rem>
      </orgext:update>
    </extension>
  </command>
</epp>

La syntaxe complète (au format XML Schema) figure dans la section 5 du RFC.

Question mise en œuvre, cette extension est dans le SDK de Verisign, accessible avec leurs autres logiciels pour registres. CNNIC a également inclus cette extension, dans leur code interne.


Téléchargez le RFC 8544


L'article seul

RFC 8543: Extensible Provisioning Protocol (EPP) Organization Mapping

Date de publication du RFC : Mars 2019
Auteur(s) du RFC : L. Zhou (CNNIC), N. Kong (Consultant), G. Zhou, J. Yao (CNNIC), J. Gould (Verisign)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 12 avril 2019


L'industrie des noms de domaine est d'une grande complexité. Les utilisateurs s'y perdent facilement entre registres, bureaux d'enregistrement, hébergeurs DNS, revendeurs divers, sociétés qui développent des sites Web, prête-noms pour protéger la vie privée, etc. Cette complexité fait qu'il est difficile de savoir qui est responsable de quoi. Dans le contexte d'EPP, protocole d'avitaillement de noms de domaine (création, modification, suppression de noms), il n'y avait jusqu'à présent pas de moyen de décrire ces acteurs. Par exemple, l'ajout d'un enregistrement DS dépend d'actions de l'hébergeur DNS, qui n'est pas forcément le BE. Mais ces hébergeurs DNS n'étaient pas définis et connus. Désormais, avec ce nouveau RFC, on peut utiliser EPP pour l'avitaillement d'objets « organisation ».

EPP (RFC 5730) est le protocole standard d'avitaillement de noms de domaine, permettant à un client (en général le BE) de créer des objets dans un registre, en parlant au serveur EPP. EPP permettait déjà des objets de type « contact » RFC 5733, identifiant les personnes ou les organisations qui assuraient certaines fonctions pour un nom de domaine. Par exemple, le contact technique était la personne ou l'organisation à contacter en cas de problème technique avec le nom de domaine.

Désormais, avec notre nouveau RFC 8543, une nouvelle catégorie (mapping) d'objets est créée, les organisations. On peut ainsi utiliser EPP pour enregistrer l'hébergeur DNS d'un domaine (qui peut être le titulaire du domaine, mais ce n'est pas toujours le cas, ou qui peut être le BE, mais ce n'est pas systématique). Ce nouveau RFC décrit donc une extension à EPP, qui figure désormais dans le registre des extensions (cf. RFC 7451).

EPP utilise XML et tout ici va donc être spécifié en XML, avec un nouvel espace de noms XML, urn:ietf:params:xml:ns:epp:org-1.0, abrégé en org dans le RFC (mais rappelez-vous que le vrai identificateur d'un espace de noms XML est l'URI, pas l'abréviation). Le nouvel espace de noms est désormais dans le registre des espaces de noms.

La section 3 de notre RFC décrit les attributs d'une organisation (notez que le vocabulaire est trompeur : ils s'appellent attributs mais ne sont pas des attributs XML). Mais commençons par un exemple, décrivant le BE nommé « Example Registrar Inc. » :


<org:infData
    xmlns:org="urn:ietf:params:xml:ns:epp:org-1.0">
    <org:id>registrar1362</org:id>
    <org:roid>registrar1362-REP</org:roid>
    <org:role>
      <org:type>registrar</org:type>
      <org:status>ok</org:status>
      <org:status>linked</org:status>
      <org:roleID>1362</org:roleID>
    </org:role>
    <org:status>ok</org:status>
    <org:postalInfo type="int">
      <org:name>Example Registrar Inc.</org:name>
      <org:addr>
        <org:street>123 Example Dr.</org:street>
        <org:city>Dulles</org:city>
        <org:sp>VA</org:sp>
        <org:cc>US</org:cc>
      </org:addr>
    </org:postalInfo>
    <org:voice x="1234">+1.7035555555</org:voice>
    <org:email>contact@organization.example</org:email>
    <org:url>https://organization.example</org:url>
    <org:contact type="admin">sh8013</org:contact>
    <org:contact type="billing">sh8013</org:contact>
    <org:contact type="custom"
       typeName="legal">sh8013</org:contact>
    <org:crID>ClientX</org:crID>
    <org:crDate>1999-04-03T22:00:00.0Z</org:crDate>
    <org:upID>ClientX</org:upID>
    <org:upDate>1999-12-03T09:00:00.0Z</org:upDate>
</org:infData>

Voyons maintenant quelques-uns des attributs possibles.

Une organisation a un identificateur, indiqué par l'élément XML <org:id>, attribué par le registre (c'est registrar1362 dans l'exemple). Il a aussi un ou plusieurs rôles, dans l'élement XML <org:role>. Un même acteur peut avoir plusieurs rôles (par exemple il est fréquent que les BE soient également hébergeurs DNS). Le rôle inclut un type, qui peut valoir :

  • registrar : BE, comme dans le cas ci-dessus,
  • reseller : revendeur, par exemple l'organisation à laquelle le titulaire du nom de domaine achète un domaine n'est pas toujours un « vrai » BE, ce peut être un revendeur,
  • privacyproxy : un prête-nom qui, en se mettant devant l'utilisateur, permet de protéger sa vie privée,
  • et enfin dns-operator, l'hébergeur DNS.

D'autres types pourront apparaitre dans le futur, ils sont indiqués dans un registre IANA, des nouveaux types seront ajoutés en suivant la procédure « Examen par un expert » du RFC 8126.

Notez qu'au début du travail à l'IETF sur cette extension, seul le cas des revendeurs était prévu. Après des discussions sur l'importance relative des différents acteurs, il a été décidé de prévoir d'autres types que les seuls revendeurs.

Il y a aussi dans l'objet un ou plusieurs état(s), <org:status>, qui peut valoir notamment :

  • ok, l'état normal, celui du BE dans l'exemple ci-dessus,
  • terminated, quand l'organisation va être retirée de la base et ne peut plus être utilisée (c'est le cas d'un BE qui n'est plus accrédité),
  • linked, qui indique que cette organisation est liée à d'autres objets, et ne doit donc pas être supprimée.

Il existe également un attribut <org:parent>, qui indique une relation avec une autre organisation. Par exemple, un revendeur aura une relation <org:parent> vers le BE dont il est revendeur. (Dans l'exemple plus haut, il n'y a pas de <org:parent>.)

La section 4 du RFC présente ensuite les commandes EPP qui peuvent être appliquées à ces objets « organisation ». <check> permet au client EPP de savoir s'il pourra créer un objet, <info> lui donnera les moyens de s'informer sur une oranisation (l'exemple en XML ci-dessus était le résultat d'une commande EPP <info>) et bien sûr une commande <create> et une <delete>. Voici <create> en action, pour créer un objet de rôle « revendeur » (notez que, cette fois, il a un parent) :


<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
  <command>                                                                                                           
    <create>                                                                                                          
      <org:create                                                                                                     
        xmlns:org="urn:ietf:params:xml:ns:epp:org-1.0">                                                               
        <org:id>res1523</org:id>                                                                                      
        <org:role>                                                                                                    
          <org:type>reseller</org:type>                                                                               
        </org:role>                                                                                                   
        <org:parentId>1523res</org:parentId>                                                                          
        <org:postalInfo type="int">                                                                                   
          <org:name>Example Organization Inc.</org:name>                                                              
          <org:addr>                                                                                                  
            <org:street>123 Example Dr.</org:street>                                                                  
            <org:city>Dulles</org:city>                                                                               
            <org:sp>VA</org:sp>                                                                                       
            <org:cc>US</org:cc>                                                                                       
          </org:addr>                                                                                                 
        </org:postalInfo>                                                                                             
        <org:voice x="1234">+1.7035555555</org:voice>                                                                 
        <org:email>contact@organization.example</org:email>                                                           
        <org:url>https://organization.example</org:url>                                                               
        <org:contact type="admin">sh8013</org:contact>                                                                
        <org:contact type="billing">sh8013</org:contact>                                                              
      </org:create>                                                                                                   
    </create>                                                                                                         
  </command>                                                                                                          
</epp>                                                                                                                

  

Le schéma complet, en syntaxe XML Schema, figure dans la section 5 du RFC.

Question mise en œuvre de cette extension EPP, Verisign l'a ajouté dans son SDK, disponible en ligne. CNNIC a une implémentation, mais non publique.


Téléchargez le RFC 8543


L'article seul

RFC 8555: Automatic Certificate Management Environment (ACME)

Date de publication du RFC : Mars 2019
Auteur(s) du RFC : R. Barnes (Cisco), J. Hoffman-Andrews (EFF), D. McCarney (Let's Encrypt), J. Kasten (University of Michigan)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF acme
Première rédaction de cet article le 11 avril 2019


Une grande partie de la sécurité du Web, et d'ailleurs de plein d'autres chose sur l'Internet, repose sur des certificats où une Autorité de Certification (AC) garantit que vous êtes bien ce que vous prétendez être. Traditionnellement, l'émission d'un certificat se faisait selon un processus manuel, lent et coûteux, à part dans quelques AC automatisées et gratuites comme CAcert. Mais il n'existait pas de mécanisme standard pour cette automatisation. (Et CAcert n'a pas d'API, même non-standard.) Un tel mécanisme standard existe désormais, avec le protocole ACME, normalisé dans ce RFC. Son utilisateur le plus connu est l'AC Let's Encrypt.

Pour comprendre ACME, il faut d'abord revenir aux utilisations des certificats. La norme technique pour les certificats utilisés sur l'Internet se nomme PKIX et est normalisée dans le RFC 5280. PKIX est un profil (une restriction d'une norme beaucoup plus large - et bien trop large, comme le sont souvent les normes des organismes comme l'UIT ou l'ISO) de X.509. Un certificat PKIX comprend, entre autres :

  • Une clé cryptographique publique, le titulaire du certificat étant supposé conserver avec soin et précaution la clé privée correspondante,
  • Le nom du titulaire du certificat (X.509 l'appelle le sujet),
  • Une signature de l'émetteur du certificat (l'AC).
  • Des métadonnées dont notamment la date d'expiration du certificat, qui sert à garantir qu'en cas de copie de la clé privée, le copieur ne pourra pas profiter du certificat éternellement.

On note que le certificat est public. N'importe qui peut récupérer le certificat de, par exemple, un site Web. Voici un exemple avec OpenSSL et www.labanquepostale.fr pour un certificat de type EV :

% openssl s_client -connect www.labanquepostale.fr:443 -showcerts | openssl x509 -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            0d:8f:ec:dd:d8:7b:83:b8:a0:1e:eb:c2:a0:2c:10:9b
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert SHA2 Extended Validation Server CA
        Validity
            Not Before: Sep  5 00:00:00 2018 GMT
            Not After : Sep  4 12:00:00 2020 GMT
        Subject: businessCategory = Private Organization, jurisdictionC = FR, serialNumber = 421 100 645, C = FR, L = PARIS, O = LA BANQUE POSTALE SA, OU = DISFE, CN = www.labanquepostale.fr
...
    

et un avec GnuTLS pour un certificat DV (Domain Validation), mamot.fr :

% gnutls-cli mamot.fr
 - subject `CN=mamot.fr', issuer `CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US', serial 0x035516154ab9120c186e9211d0da6296af62, RSA key 2048 bits, signed using RSA-SHA256, activated `2019-01-13 23:00:32 UTC', expires `2019-04-13 23:00:32 UTC', key-ID `sha256:ef62c4aae2a9a99c00c33f2bbac9c40b980c70400a056e2a8042885e501ce283'
...

D'ailleurs, des services comme Certificate Transparency (RFC 6962), accessible entre autres en https://crt.sh/, donnent accès facilement à tous les certificats émis par les AC participantes.

Du fait que seul le titulaire connait la clé privée, la capacité à signer des messages vérifiables avec la clé publique permet d'authentifier le partenaire avec lequel on communique. Grâce à la signature de l'AC, quiconque fait confiance à cette AC particulière peut être sûr que le certificat appartient bien au titulaire. Dans l'exemple avec OpenSSL, le certificat de la Banque Postale était signé par DigiCert, si on fait confiance à DigiCert, on sait qu'on est bien connecté à la Banque Postale.

Qui sont les AC ? Ce sont la plupart du temps des entreprises commerciales qui sont payées par les titulaires de certificat, et elles sont censées vérifier la sincérité de leurs clients. Cette vérification peut être manuelle, partiellement ou totalement automatique. Normalement, les certificats de type EV (Extended Validation), comme celui de la Banque Postale plus haut, font l'objet d'une vérification manuelle. Cela permet de vérifier l'identité officielle (celle gérée par l'État) du titulaire. Les certificats DV (Domain Validation), comme celui de mamot.fr, eux, peuvent être validés automatiquement, ils assurent uniquement le fait que le titulaire contrôle bien le nom de domaine utilisé comme sujet. (Pour avoir tous les horribles détails, y compris les certificats OV - Organization Validated - dont je n'ai pas parlé, on peut consulter les « Baseline Requirements for the Issuance and Management of Publicly-Trusted Certificates » du CA/Browser Forum.) Ainsi, pour CAcert, on doit prouver le contrôle du domaine en répondant aux courriers envoyés aux adresses publiques de contact pour le domaine.

Les certificats peuvent servir à de nombreux protocoles de sécurité mais le plus connu est TLS (normalisé dans le RFC 8446). Comme il n'est pas le seul protocole pour lequel on a des certificats, il est erroné de parler de « certificats TLS » ou, pire encore, de « certificats SSL ». TLS est un protocole client/serveur, où le client authentifie le serveur mais où le serveur authentifie rarement le client. Il est à la base de la sécurisation d'un grand nombre de services sur l'Internet, à commencer par le Web avec HTTPS (RFC 2818). L'authentification du serveur par le client est faite en vérifiant (attention, je vais simplifier) :

  • Que le partenaire avec qui on parle a la clé privée (il peut signer des messages) correspondant au certificat présenté,
  • Que le certificat n'a pas expiré,
  • Que le certificat est signé par une AC connue du client (la clé publique de l'AC est dans le magasin du client),
  • Que le nom indiqué par le client correspond à un des noms disponibles dans le certificat. Dans le cas du Web, c'est le nom de domaine dans l'URL choisi (RFC 6125).

Une fois cette authentification faite, TLS assure l'intégrité et la confidentialité de la communication.

Attention, on parle bien d'authentification, pas de confiance. Malgré ce que vous pourrez lire dans les « La sécurité pour les nuls », le fameux « cadenas vert » ne signifie pas du tout que vous pouvez faire vos achats en ligne en toute sécurité. Il indique seulement que le partenaire a bien le nom que vous avez demandé, et qu'un tiers ne pourra pas écouter ou modifier la conversation. Il n'indique pas que le partenaire soit digne de confiance ; l'AC ne peut pas vérifier cela ! Ainsi, dans l'exemple plus haut, TLS et l'authentification par certificat garantissent bien qu'on se connecte au serveur HTTPS de la Maison-Blanche, www.whitehouse.gov, mais pas que Trump dise la vérité !

J'ai parlé du magasin où se trouvent les clés des AC à qui on fait confiance. Qui décide du contenu de ce magasin ? C'est une question complexe, il n'y a pas une liste d'AC faisant autorité. La plupart des systèmes d'exploitation ont une liste à eux, créée en suivant des critères plus ou moins opaques. Les applications (comme le navigateur Web) utilisent ce magasin global du système ou, parfois, ont leur propre magasin, ce qui aggrave encore la confusion. Les utilisateurs peuvent (c'est plus ou moins facile) ajouter des AC ou bien en retirer.

Et comment obtient-on un certificat ? Typiquement, on crée d'abord une demande de certificat (CSR pour Certificate Signing Request, cf. RFC 2986). Avec OpenSSL, cela peut se faire avec :

% openssl req  -new -nodes -newkey rsa:2048 -keyout server.key -out server.csr
    

On se connecte ensuite au site Web de l'AC choisie, et on lui soumet le CSR. Ici, un exemple avec CAcert : cacert-csr.png

L'AC doit alors faire des vérifications, plus ou moins rigoureuses. Par exemple, l'AC fait une requête whois, note l'adresse de courrier du domaine, envoie un message contenant un défi et le client de l'AC doit y répondre pour prouver qu'il a bien accès à cette adresse et contrôle donc bien le domaine. L'AC crée ensuite le certificat qu'elle vous renvoie. Il faut alors l'installer sur le serveur (HTTPS, par exemple). L'opération est complexe, et beaucoup d'utilisateurs débutants cafouillent.

C'est ce processus non-standard et compliqué que le protocole ACME vise à normaliser et à automatiser. Ce RFC a une longue histoire mais est déjà déployé en production par au moins une AC.

Le principe d'ACME est simple : l'AC fait tourner un serveur ACME, qui attend les requêtes des clients. Le client ACME (un logiciel comme dehydrated ou certbot) génère la CSR, se connecte au serveur, et demande un certificat signé pour un nom donné. Le serveur va alors renvoyer un défi qui va permettre au client de prouver qu'il contrôle bien le nom de domaine demandé. Il existe plusieurs types de défis, mais le plus simple est un nom de fichier que le serveur ACME choisit, demandant au client ACME de mettre un fichier de ce nom sur son site Web. Si le nom de fichier était Vyzs0Oqkfa4gn4skMwszORg6vJaO73dvMLN0uX38TDw, le serveur ACME va devenir client HTTP et chercher à récupérer http://DOMAIN/.well-known/acme-challenge/Vyzs0Oqkfa4gn4skMwszORg6vJaO73dvMLN0uX38TDw. S'il y réussit, il considère que le client ACME contrôle bien le nom de domaine, et il signe alors le certificat, qui est renvoyé au client lorsque celui-ci soumet la CSR.

Le modèle idéal d'utilisation d'ACME est présenté dans la section 2. (En pratique, il n'est pas vraiment réalisé, notamment parce qu'il n'existe pratiquement qu'une seule AC utilisant ACME, Let's Encrypt. Il n'y a donc pas encore beaucoup de diversité.) L'espoir est qu'un jour, on puisse faire comme suit :

  • On installe un serveur Web (avec des services comme le CMS),
  • La procédure d'installation vous demande le nom de domaine à utiliser (ce point là n'est pas automatisable, sans même parler de la procédure de location du nom de domaine),
  • Le logiciel vous propose une liste d'AC parmi lesquelles choisir (on a vu qu'il n'y en avait qu'une actuellement ; dans le futur, s'il y en a plusieurs, l'utilisateur aura sans doute autant de mal à choisir qu'il ou elle en a aujourd'hui à choisir un BE),
  • Le logiciel fait tout le reste automatiquement : requête à l'AC choisie en utilisant le protocole ACME normalisé dans notre RFC, réponse au défi de l'AC via le serveur HTTP installé, récupération du certificat et configuration de TLS,
  • Par la suite, c'est le logiciel qui effectuera automatiquement les demandes de renouvellement de certificat (aujourd'hui, avec les logiciels existants, c'est le point qui est le plus souvent oublié ; combien de sites Web ont annoncé fièrement qu'ils étaient désormais protégés par HTTPS, pour afficher un certificat expiré trois mois après…)

Ainsi, une procédure manuelle et pénible pourra devenir assez simple, encourageant une présence en ligne plus sécurisée. Cela pourrait devenir aussi simple que d'avoir un certificat auto-signé.

La section 4 du RFC expose de manière générale le protocole ACME (le RFC complet fait 94 pages, car il y a beaucoup de détails à spécifier). Rappelez-vous avant de la lire que, pour ACME, le client est celui qui demande le certificat (c'est donc typiquement un serveur Internet, par exemple un serveur HTTPS) et le serveur ACME est l'AC. Quand je dirais « client » ou « serveur » tout court, il s'agira du client et du serveur ACME.

ACME encode ses messages en JSON (RFC 8259). Le client doit d'abord avoir un compte auprès du serveur (avec Let's Encrypt, cela peut être fait automatiquement sans que l'utilisateur s'en rende compte). Par exemple, avec dehydrated, cela se fait ainsi :

% dehydrated --register --accept-terms 
+ Generating account key...
+ Registering account key with ACME server...
+ Done!

Et on trouve dans le répertoire accounts/ la clé privée du compte, et les informations du compte :

% cat accounts/aHR0cHM6Ly9...9yeQo/registration_info.json 
{
  "id": 5...1,
  "key": {
    "kty": "RSA",
    "n": "wv...HCk",
    "e": "AQAB"
  },
  "contact": [],
  "initialIp": "2001:4b98:dc0:41:216:3eff:fe27:3d3f",
  "createdAt": "2019-03-12T19:32:20.018154799Z",
  "status": "valid"
}

Pour certbot, on peut le faire tourner avec l'option -v pour avoir les mêmes informations. certbot affiche également des messages d'ordre administratif comme :

Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): stephane+letsencrypt@bortzmeyer.org
...
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: A

JWS payload:
b'{\n  "contact": [\n    "mailto:stephane+letsencrypt@bortzmeyer.org"\n  ],\n  "termsOfServiceAgreed": true,\n  "resource": "new-reg"\n}'
{
  "id": 53367492,
  "key": { ...
  "contact": [
    "mailto:stephane+letsencrypt@bortzmeyer.org"
  ],
  "createdAt": "2019-03-15T16:07:58.29914038Z",
  "status": "valid"
}

Reporting to user: Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: No
...
   

Le compte sera authentifié en utilisant une biclé (clé privée et clé publique). Il y aura ensuite quatre étapes :

  • Demander un certificat,
  • Répondre au défi (notez bien qu'ACME permet plusieurs types de défis possibles),
  • Envoyer le CSR,
  • Récupérer le certificat signé.

Mais comment transporte-t-on ces messages en JSON ? La section 6 du RFC répond à cette question : on utilise HTTPS. En prime, les messages sont signés avec JWS (RFC 7515), en utilisant la clé privée du client pour les requêtes. Voici par exemple la réponse d'un serveur ACME lorsqu'on l'interroge sur un défi en cours :

{
  "type": "http-01",
  "status": "pending",
  "url": "https://acme-v02.api.letsencrypt.org/acme/challenge/7TAkQBMmFqm8Rhs6Sn8SFCne2MoZXoEHCz0Px7f0dpE/13683685175",
  "token": "mMXGXjEijKBZXl2RuL0rjlektPPpy-ozJpZ2vB4w6Dw"
}     
    

Les messages d'erreur utilisent le RFC 7807. En voici un exemple :

{
  "type": "http-01",
  "status": "invalid",
  "error": {
    "type": "urn:acme:error:unauthorized",
    "detail": "Invalid response from http://mercredifiction.bortzmeyer.org/.well-known/acme-challenge/rE-rIfjjCfMlivxLfoJmMbRyspwmld97Xnxmy7K0-JA: \"\u003c!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\"\u003e\n\u003chtml\u003e\u003chead\u003e\n\u003ctitle\u003e404 Not Found\u003c/title\u003e\n\u003c/head\u003e\u003cbody\u003e\n\u003ch1\u003eNot Found\u003c/h1\u003e\n\u003cp\"",
    "status": 403  ...
   [Le message d'erreur indique également typiquement l'URL demandé,
   et les adresses IP utilisées, ce qui est crucial si le serveur HTTP
   a plusieurs adresses IP, par exemple une en IPv4 et une en IPv6. Il
   faut donc bien lire tout le message d'erreur.]
     

Une liste des erreurs possibles est enregistrée à l'IANA. Voici par exemple une erreur CAA (RFC 6844) :

  "error": {
    "type": "urn:acme:error:caa",
    "detail": "CAA record for mercredifiction.bortzmeyer.org prevents issuance",
    "status": 403
  },
     

Comment un client ACME trouve-t-il les URL pour les différentes opérations ? Il y a un URL à connaitre, le répertoire (directory). Une requête à cet URL (par exemple curl https://acme-v02.api.letsencrypt.org/directory va renvoyer un objet JSON qui contient la liste des autres URL (section 7, une des plus cruciales du RFC). Voici un exemple chez Let's Encrypt :

{ ...
  "meta": {
    "caaIdentities": [
      "letsencrypt.org"
    ],
    "termsOfService": "https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf",
    "website": "https://letsencrypt.org"
  },
  "newAccount": "https://acme-v02.api.letsencrypt.org/acme/new-acct",
  "newNonce": "https://acme-v02.api.letsencrypt.org/acme/new-nonce",
  "newOrder": "https://acme-v02.api.letsencrypt.org/acme/new-order",
  "revokeCert": "https://acme-v02.api.letsencrypt.org/acme/revoke-cert"
}
    

On peut ensuite créer un compte (le champ newAccount dans l'exemple ci-dessus) puis demander des certificats (champ newOrder dans l'exemple ci-dessus), ici (Let's Encrypt) en écrivant à https://acme-v02.api.letsencrypt.org/acme/new-order :

{
  "payload": "ewogICJpZGVudGlmaWVycyI6IFsKICAgIHsKICAgICAgInR5cGUiOiAiZG5zIiwKICAgICAgInZhbHVlIjogInRlc3QtYWNtZS5ib3J0em1leWVyLmZyIgogICAgfQogIF0KfQ",
  "protected": "eyJhbGciOiAiUlMyNTYiLCAidXJsIjogImh0dHBzOi8vYWNtZS12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL25ldy1vcmRlciIsICJraWQiOiAiaHR0cHM6Ly9hY21lLXYwMi5hcGkubGV0c2VuY3J5cHQub3JnL2FjbWUvYWNjdC81MzE3NzA1MCIsICJub25jZSI6ICJyZXNXTXFtQkJYbVZrZ2JfSnVQU3VEcmlmQzhBbDZrTm1JeDZuNkVwRDFZIn0",
  "signature": "MP9rXTjX4t1Be-y6dhPOP7JE3B401wokydUlG8gJGWqibTM_gydkUph1smtrUZ5W4RXNTEnlmiFwoiU4eHLD-8MzN5a3G668VbgzKd0VN7Y1GxQBGtsj9fShx4VMjSGLzVq1f7bKCbdX3DYn0LaiRDApgNXiMfoEnPLltu5Ud7RBNOaWY8zE0yAV7e3NFlF9Wfaii5Ff9OT1ZCD8LusOHP-gA4VkimQ9ofYr32wZYgsUFu6G--QflP0tjc5eKYMe1cKlgpyKZsDtBurWwvKlj2cU_PUdOZvjXSBbHX18jVlwglzfFnu0xTaDGTTvOuMBfjnWJCWpr-oA7Ih48dL-Jg"
}
    

Eh oui, tout est signé, en JWS (RFC 7515) et base64isé (cf. section 7.4 du RFC). Ici, le décodage Base64 nous dira que la requête était :

{
 {
  "identifiers": [
    {
      "type": "dns",
      "value": "test-acme.bortzmeyer.fr"
    }
  ]
  }
  , {"alg": "RS256", "url":
  "https://acme-v02.api.letsencrypt.org/acme/new-order", "kid":
  "https://acme-v02.api.letsencrypt.org/acme/acct/53177050", "nonce":
  "resWMqmBBXmVkgb_JuPSuDrifC8Al6kNmIx6n6EpD1Y"}
}
    

Donc, une demande de certificat pour test-acme.bortzmeyer.fr.

Les autres opérations possibles avec un serveur ACME sont enregistrées à l'IANA. Par exemple, on peut révoquer un certificat.

La réponse sera :

{
  "status": "pending",
  "expires": "2019-03-19T19:50:41.434669372Z",
  "identifiers": [
    {
      "type": "dns",
      "value": "test-acme.bortzmeyer.fr"
    }
  ],
  "authorizations": [
    "https://acme-v02.api.letsencrypt.org/acme/authz/FVMFaHS_oWjqfR-rWd6eBKMlt1EWfIcf6i7D4wU_swM"
  ],
  "finalize": "https://acme-v02.api.letsencrypt.org/acme/finalize/53177050/352606317"
}      
    

Le client ACME va alors télécharger l'autorisation à l'URL indiqué, récupérant ainsi les défis qu'il devra affronter (section 7.5 du RFC). Une fois qu'il a fait ce qui lui était demandé par le serveur, il utilise l'URL donné dans le champ finalize pour indiquer au serveur que c'est bon, que le serveur peut vérifier. La commande certbot avec l'option -v vous permettra de voir tout ce dialogue.

Le protocole ACME peut être utilisé par d'autres AC que Let's Encrypt. Avec le client dehydrated, il suffira, quand de telles AC seront disponibles, de mettre CA=URL dans le fichier de configuration (l'URL par défaut est https://acme-v02.api.letsencrypt.org/directory). Un exemple d'autre AC utilisant ACME est BuyPass (pas testé).

Mais en quoi consistent exactement les défis, dont j'ai déjà parlé plusieurs fois ? La section 8 les explique. L'idée de base d'un défi ACME est de permettre de prouver qu'on contrôle réellement un identificateur, typiquement un nom de domaine. ACME ne normalise pas un type de défi particulier. Le cadre est ouvert, et de nouveaux défis pourront être ajoutés dans le futur. Le principe est toujours le même : demander au client ACME de faire quelque chose que seul le vrai titulaire de l'identificateur pourrait faire. Un défi, tel qu'envoyé par le serveur ACME, a un type (le plus utilisé aujourd'hui, est de loin, est le défi http-01), un état (en attente ou bien, au contraire, validé) et un texte d'erreur, au cas où la validation ait échoué. Plusieurs défis, comme http-01 ont également un jeton, un cookie, un texte généré par le serveur, et non prévisible par le client ou par le reste du monde, et qu'il faudra placer quelque part où le serveur pourra le vérifier. Le serveur ACME ne testera que lorsque le client lui aura dit « c'est bon, je suis prêt, j'ai fait tout ce que tu m'as défié de faire ». Le RFC demande également au serveur de réessayer après cinq ou dix secondes, si la vérification ne marche pas du premier coup, au cas où le client ait été trop rapide à se dire prêt.

Le plus connu et le plus utilisé des défis, à l'heure actuelle, est http-01. Le client ACME doit configurer un serveur HTTP où une page (oui, je sais, le terme correct est « ressource ») a comme nom le contenu du jeton. Le serveur ACME va devenir client HTTP pour récupérer cette page et, s'il y arrive, cela prouvera que le client contrôlait bien le nom de domaine qu'il avait indiqué. De manière surprenante, et qui déroute beaucoup de débutants, le défi se fait bien sur HTTP et pas HTTPS, parce que beaucoup d'hébergements Web partagés ne donnent pas suffisamment de contrôle à l'hébergé.

Le jeton est une chaîne de caractères utilisant le jeu de caractères de Base64, pour passer partout. Voici un exemple de défi HTTP envoyé par le serveur :

{
  "identifier": {
    "type": "dns",
    "value": "test-acme.bortzmeyer.fr"
  },
  "status": "pending",
  "expires": "2019-03-19T19:50:41Z",
  "challenges": [
    {
      "type": "http-01",
      "status": "pending",
      "url": "https://acme-v02.api.letsencrypt.org/acme/challenge/FVMFaHS_oWjqfR-rWd6eBKMlt1EWfIcf6i7D4wU_swM/13574068498",
      "token": "4kpeqw7DVMrY6MI3tw1-tTq9oySN2SeMudaD32IcxNM"
    } ...
    

L'URL qu'utilisera le serveur est http://DOMAINE-DEMANDÉ/.well-known/acme-challenge/JETON (ou, en syntaxe du RFC 6570, http://{domain}/.well-known/acme-challenge/{token}). Comme expliqué plus haut, c'est bien http:// et pas https://. Les URL avec .well-known sont documentés dans le RFC 5785 et acme-challenge est désormais dans le registre.

Imaginons qu'on utilise le serveur HTTP Apache et qu'on veuille répondre à ce défi. Le plus simple est de configurer le serveur ainsi :

      
<VirtualHost *:80>
   Alias /.well-known/acme-challenge /var/lib/dehydrated/acme-challenges
   <Directory /var/lib/dehydrated/acme-challenges>
        Options None
        AllowOverride None
	...

    

Cela indique à Apache que les réponses aux défis seront dans le répertoire /var/lib/dehydrated/acme-challenges, répertoire où le client ACME dehydrated va mettre ses fichiers. Avec le serveur HTTP Nginx, le principe est le même :

server {
    location ^~ /.well-known/acme-challenge {
         alias /var/lib/dehydrated/acme-challenges;
    }
}

Bien sûr, de nombreuses autres solutions sont possibles. Le serveur HTTP peut intégrer le client ACME, par exemple. Autre exemple, le client ACME certbot inclut son propre serveur HTTP, et peut donc répondre aux défis tout seul, sans Apache.

Ensuite, on lance le client ACME, éventuellement en lui spécifiant où il doit écrire la réponse aux défis :

% certbot certonly --webroot -w /usr/share/nginx/html -d MONDOMAINE.eu.org
    

certbot va mettre le certificat généré et signé dans son répertoire, typiquement /etc/letsencrypt/live/MONDOMAINE.eu.org/fullchain.pem. Et on programme son système (par exemple avec cron) pour relancer le client ACME tous les jours (les clients ACME typique vérifient la date d'expiration du certificat, et n'appellent l'AC que si cette date est proche.) Notez bien qu'il est crucial de superviser l'expiration des certificats. On voit fréquemment des sites Web utilisant Let's Encrypt devenir inaccessibles parce que le certificat a été expiré. Beaucoup d'administrateurs système croient que parce que Let's Encrypt est « automatique », il n'y a aucun risque. Mais ce n'est pas vrai : non seulement la commande de renouvellement peut ne pas être exécutée, ou bien mal se passer mais, même si le certificat est bien renouvellé, cela ne garantit pas que le serveur HTTP soit rechargé.

Petite anecdote personnelle : pour le blog que vous êtes en train de lire, cela avait été un peu plus compliqué. En effet, le blog a deux copies, sur deux machines différentes. J'ai donc du rediriger les vérifications ACME sur une seule des deux machines. En Apache :

        ProxyRequests Off
        ProxyPass /.well-known/acme-challenge/ http://MACHINE-DE-RÉFÉRENCE.bortzmeyer.org/.well-known/acme-challenge/
        ProxyPreserveHost On

À noter qu'un serveur HTTP paresseux qui se contenterait de répondre 200 (OK) à chaque requête sous /.well-known/acme-challenge n'arriverait pas à répondre avec succès aux défis HTTP. En effet, le fichier doit non seulement exister mais également contenir une chaîne de caractères faite à partir d'éléments fournis par le serveur ACME (cf. section 8.3).

Un autre type de défi répandu est le défi dns-01, où le client doit mettre dans le DNS un enregistrement TXT _acme-challenge.DOMAINE-DEMANDÉ contenant le jeton. Cela nécessite donc un serveur DNS faisant autorité qui permette les mises à jour dynamiques, via le RFC 2136 ou bien via une API. Notez que le RFC recommande (section 10.2) que l'AC fasse ses requêtes DNS via un résolveur qui valide avec DNSSEC. (Le serveur ACME ne demande pas directement aux serveurs faisant autorité, il passe par un résolveur. Attention donc à la mémorisation par les résolveurs des réponses, jusqu'au TTL.)

On peut utiliser le défi DNS avec des jokers (pour avoir un certificat pour *.MONDOMAINE.fr) mais c'est un peu plus compliqué (section 7.1.3 si vous voulez vraiment les détails).

D'autres types de défis pourront être ajouté dans le futur. Un registre IANA en garde la liste. Notez que des types de défis peuvent également être supprimés comme tls-sni-01 et tls-sni-02, jugés à l'usage pas assez sûrs.

Le but de ce RFC est la sécurité, donc toute faiblesse d'ACME dans ce domaine serait grave. La section 10 du RFC se penche donc sur la question. Elle rappelle les deux objectifs de sécurité essentiels :

  • Seul·e l·e·a vrai·e titulaire d'un identificateur (le nom de domaine) peut avoir une autorisation pour un certificat pour cet identificateur,
  • Une fois l'autorisation donnée, elle ne peut pas être utilisée par un autre compte.

Le RFC 3552 décrit le modèle de menace typique de l'Internet. ACME a deux canaux de communication, le canal ACME proprement dit, utilisant HTTPS, et le canal de validation, par lequel se vérifient les réponses aux défis. ACME doit pouvoir résister à des attaques passives et actives sur ces deux canaux.

ACME n'est qu'un protocole, il reçoit des demandes, envoie des requêtes, traite des réponses, mais il ne sait pas ce qui se passe à l'intérieur des machines. Les défis, par exemple, peuvent être inutiles si la machine testée est mal gérée (section 10.2). Si, par exemple, le serveur HTTP est sur un serveur avec plusieurs utilisateurs, et où tout utilisateur peut bricoler la configuration HTTP, ou bien écrire dans le répertoire .well-known, alors tout utilisateur sur ce serveur pourra avoir un certificat. Idem évidemment si le serveur est piraté. Et, si on sous-traite le serveur de son organisation à l'extérieur, le sous-traitant peut également faire ce qu'il veut et obtenir des certificats pour son client (« il n'y a pas de cloud, il y a juste l'ordinateur de quelqu'un d'autre »).

ACME permet d'obtenir des certificats DV et ceux-ci dépendent évidemment des noms de domaine et du DNS. Un attaquant qui peut faire une attaque Kaminsky, par exemple, peut envoyer les requêtes du serveur ACME chez lui. Plus simple, même si le RFC n'en parle guère (il se focalise sur les attaques DNS, pas sur celles portant sur les noms de domaine), un attaquant qui détourne le nom de domaine, comme vu fin 2018 au Moyen-Orient, peut évidemment obtenir les certificats qu'il veut, contrairement à la légende répandue comme quoi TLS protègerait des détournements.

Comment se protéger contre ces attaques ? Le RFC recommande d'utiliser un résolveur DNS validant (vérifiant les signatures DNSSEC) ce que peu d'AC font (Let's Encrypt est une exception), de questionner le DNS depuis plusieurs points de mesure, pour limiter l'efficacité d'attaques contre le routage (cf. celle contre MyEtherWallet en avril 2018), et pourquoi pas d'utiliser TCP plutôt qu'UDP pour les requêtes DNS (ce qui présente l'avantage supplémentaire de priver de certificat les domaines dont les serveurs de noms sont assez stupides pour bloquer TCP). Voir aussi la section 11.2, qui revient sur ces conseils pratiques. Par exemple, une AC ne doit évidemment pas utiliser le résolveur DNS de son opérateur Internet, encore moins un résolveur public.

ACME est un protocole, pas une politique. L'AC reste maitresse de sa poltique d'émission des certificats. ACME ne décrit donc pas les autres vérifications qu'une AC pourrait avoir envie de faire :

  • Acceptation par le client d'un contrat,
  • Restrictions supplémentaires sur le nom de domaine,
  • Autorisation ou pas des jokers dans le nom demandé,
  • Liste noire de noms considérés comme sensibles, par exemple parce désignant telle ou telle marque puissante,
  • Tests avec la PSL,
  • Tests techniques sur la cryptographie (par exemple rejeter les clés pas assez fortes, ou bien utilisant des algorithmes vulnérables),
  • Présence d'un enregistrement CAA (RFC 6844).

Les certificats DV (ceux faits avec ACME) sont sans doute moins fiables que les EV (les DV n'ont qu'une vérification automatique, avec peu de sécurité puisque, par exemple, DNSSEC n'est pas obligatoire) et il est donc prudent de limiter leur durée de validité. Let's Encrypt fait ainsi des certificats à courte durée de vie, seulement trois mois, mais ce n'est pas trop grave en pratique, puisque le renouvellement peut être complètement automatisé.

Quels sont les mises en œuvre disponibles d'ACME ? Comme le RFC est publié longtemps après les premiers déploiements, il y en a déjà pas mal. Let's Encrypt maintient une liste de clients. Personnellement, j'ai pratiqué certbot et dehydrated mais il en existe d'autres, comme acme-tiny, qui semble simple et compréhensible. Un avantage que je trouve à dehydrated est qu'il est bien plus simple de garde sa clé lors des renouvellements, par exemple pour DANE : il suffit de mettre PRIVATE_KEY_RENEW="no" dans le fichier de configuration. En revanche, dehydrated est à la fois pas assez et trop bavard. Pas assez car il n'a pas d'option permettant de voir la totalité du dialogue en JSON avec le serveur (contrairement à certbot) et trop car il affiche des messages même quand il n'a rien fait (parce que le certificat était encore valide pour assez longtemps). Pas moyen de le faire taire, et rediriger la sortie standard ne marche pas car on veut savoir quand il y a eu renouvellement effectif.

On dispose également de bibliothèques permettant au programmeur ou à la programmeuse de développer plus facilement un client ACME. (Par exemple Protocol::ACME (encore que j'ai l'impression qu'il n'est plus maintenu, un programmeur Perl disponible pour évaluer ce qui existe ?). Pour les programmeures Python, il y a le module acme qui est celui utilisé par le client certbot, mais qui est aussi distribué indépendamment. En Go, il y a LeGo. Mais on peut aussi mettre le client ACME dans le serveur HTTP, comme le permet Apache

Et les serveurs ACME ? Évidemment, peu de gens monteront une AC mais, si vous voulez le faire, le serveur de Let's Encrypt, Boulder, est en logiciel libre.

Notez que ce RFC ne parle que de la validation de noms de domaines mais ACME pourra, dans le futur, être utilisé pour valider la « possession » d'une adresse IP, ou d'autres identifiants.

Et si vous voulez un résumé rapide d'ACME par ses auteurs, allez lire cet article sur le blog de l'IETF.


Téléchargez le RFC 8555


L'article seul

Exposé sur DoH (DNS sur HTTPS) aux JDLL

Première rédaction de cet article le 7 avril 2019
Dernière mise à jour le 9 avril 2019


Comme chaque année, les Journées du Logiciel Libre à Lyon ont été passionnantes et très bien organisées. J'y ai fait un petit exposé sur une technique qui a fait un peu de bruit récemment, DoH (DNS sur HTTPS).

DoH a été normalisé dans le RFC 8484. Cette technique permet de chiffrer le trafic DNS, afin d'échapper à la surveillance et à la modification du trafic. (DNSSEC permet de détecter ces modifications mais pas d'y échapper.) Elle suscite donc les réactions de ceux qui avaient pris l'habitude de regarder le trafic DNS, voire de changer les réponses.

Mais le déploiement de DoH soulève une autre question. Un acteur important, Mozilla, a choisi de configurer DoH par défaut dans son navigateur Firefox (ce qui se défend) mais également de désigner comme résolveur par défaut celui d'un GAFA, Cloudflare. Question vie privée, passer d'une surveillance et d'une censure par le FAI à une surveillance et peut-être demain une censure, par une entreprise capitaliste états-unienne n'est pas forcément un progrès… Il est donc important et urgent que des résolveurs DoH vraiment libres soient déployés par des acteurs non-GAFA, par exemple des chatons.

Les supports de mon exposé sont disponibles ici (ainsi que leur source). La conférence a été filmée et la vidéo est sur PeerTube chez Benzo (cf. son article) et chez GoogleTube.

La démonstration de DoH a été faite avec un serveur DoH écrit en Python lors d'un hackathon à l'IETF, tournant sur https://doh.bortzmeyer.fr/. Attention : non seulement ce serveur DoH est purement expérimental, et toujours en panne, mais en outre il n'offre aucune vie privée, je regarde tout le trafic. Voici par exemple ce qui s'affiche lorsqu'un client DoH a fait une requête pour jdll.org :

INFO: id 0
opcode QUERY
rcode NOERROR
flags RD
;QUESTION
jdll.org. IN A
;ANSWER
;AUTHORITY
;ADDITIONAL
[2019-04-09 18:55:36,513] 10.251.62.29:35552 GET / 2 200 42 870793
    

Le client DoH de test utilisé (développé au même hackathon, utilisait la méthode HTTP GET. curl, lui, utilise POST. La requête curl --doh-url https://doh.bortzmeyer.fr/ https://jdll.org/ provoque :

INFO: id 0
opcode QUERY
rcode NOERROR
flags RD
;QUESTION
jdll.org. IN AAAA
;ANSWER
;AUTHORITY
;ADDITIONAL
INFO: id 0
opcode QUERY
rcode NOERROR
flags RD
;QUESTION
jdll.org. IN A
;ANSWER
;AUTHORITY
;ADDITIONAL
[2019-04-09 19:01:51,750] 82.251.62.29:35610 POST / 2 200 91 8403
[2019-04-09 19:01:51,750] 82.251.62.29:35608 POST / 2 200 42 6703
    

Notez également que curl a fait deux requêtes, A et AAAA. Voici ce qu'affiche curl de son activité :

      
% curl --doh-url https://doh.bortzmeyer.fr/ https://jdll.org/ 
* Hostname 'doh.bortzmeyer.fr' was found in DNS cache
* Connected to doh.bortzmeyer.fr (193.70.85.11) port 443 (#1)
* ALPN, offering h2
* ALPN, offering http/1.1
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=doh.bortzmeyer.fr
*  start date: Apr  5 12:54:32 2019 GMT
*  expire date: Jul  4 12:54:32 2019 GMT
*  subjectAltName: host "doh.bortzmeyer.fr" matched cert's "doh.bortzmeyer.fr"
*  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x56448a35af30)
> POST / HTTP/2
Host: doh.bortzmeyer.fr
Accept: */*
Content-Type: application/dns-message
Content-Length: 26

* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
< HTTP/2 200 
< content-type: application/dns-message
< content-length: 42
< cache-control: no-cache
< date: Tue, 09 Apr 2019 17:03:48 GMT
< server: hypercorn-h2

...

<!DOCTYPE html>
<html lang="">
<head>
    <meta charset="utf-8" />
    <title>Accueil | JdLL</title>

    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta property="og:image" content="/user/themes/jd-ll/images/favicon.ico"/>
    <meta name="generator" property="og:description" content="GravCMS" />

    

Sur Firefox, il faut configurer DoH dans un onglet about:config. Le mot-clé est TRR pour Trusted Recursive Resolver. On voit ici les réglages disponibles (2 veut dire « utiliser DoH mais se rabattre sur le DNS normal en cas de problème », 3 serait « uniquement DoH » et 0 « pas de DoH du tout ») : firefox-trr.png

Merci aux organisat·eur·rice·s. On peut trouver de jolies photos des JDLL. Merci à Syst et Marne pour leur excellent exposé sur « Le vrai coût écologique de la publicité en ligne ». J'ai modestement contribué à la lutte contre les panneaux de surveillance publicitaire en installant une copie de la page de désinscription. Autre exposé très utile, celui d'Oriane sur « La Fédération FDN et la fibre optique. Enjeux et perspectives de l'Internet associatif en haut débit. » analysant les chances pour un opérateur Internet « alternatif » de pouvoir utiliser la fibre optique des RIP (Réseaux d'initiative publique). L'énorme travail de récolte d'informations fait par la FFDN montre que ce n'est pas gagné


L'article seul

Deux « bots » de plus pour le fédivers

Première rédaction de cet article le 7 avril 2019


Je viens de mettre en service deux « bots », sur le réseau social « fédivers ». Ce court article est là pour documenter les techniques utilisées, si vous voulez vous-même réaliser un tel « agent logiciel autonome » sur ce réseau.

Un « bot », dans le contexte des réseaux sociaux, est simplement un logiciel qui, sans intervention humaine, va écrire sur le réseau social, parfois en réponse à des demandes des humains. C'est un concept très ancien (il y en avait déjà sur IRC) mais je vais me focaliser ici sur des bots participant au fédivers (également écrit fedivers, fediverse, féediverse, etc). J'avais déjà documenté en anglais un bot qui répondait à des requêtes DNS, les deux bots décrits ici en français sont plus simples, ils n'écoutent pas les messages, ils ne font qu'écrire. Cela permet de ne pas avoir un démon tournant en permanence (et qu'il faut superviser, redémarrer, etc) mais juste de les lancer de temps en temps depuis cron : ils écrivent puis retournent se coucher.

Ces deux bots sont balladependus, qui écrit le texte du poème « La ballade des pendus », de François Villon, et voirapp, qui récite la chanson « Voir » de Jacques Brel. À quoi ça sert d'envoyer des poèmes sur le fédivers ? À rien, mais plusieurs bots le font déjà, comme LInternationale qui nous réveille avec les paroles de l'Internationale (une version préliminaire, pas les paroles les plus connues).

Les deux bots sont hébergés par l'instance fédivers botsin.space, spécialisée dans les bots.

Le premier, balladependus, est écrit en shell, le second, voirapp, en Python. Voyons d'abord le premier.

Le code source de balladependus est distribué ici. Il repose sur l'excellent programme madonctl, qui permet d'accéder au fédivers depuis la ligne de commande. madonctl utilise l'API du serveur Mastodon (botsin.space est un Mastodon). Normalement, Pleroma gère également cette API et donc madonctl devrait marcher avec Pleroma, mais je n'ai jamais essayé. Autrement, que fait le programme ? En commençant du début :

  • Il récupère sur la ligne de commande ses deux arguments, le fichier contenant le poème, et le nom du fichier de configuration (le même programme peut être utilisé pour plusieurs poèmes et plusieurs comptes).
  • Il lit un fichier de configuration, contenant les paramètres spécifiques à un compte fédivers, notamment le jeton d'autorisation ; ce fichier a été créé avec madonctl config dump -i YOURINSTANCE -L YOURID -P YOURPASSWORD > ~/.config/madonctl/YOURCONFIG.yml. Il faut donc s'être créé un compte sur l'instance préalablement (en n'oubliant pas de cocher, dans le profil, la case « Je suis un bot »).
  • Il lit ensuite un fichier d'état ($checkpoint) qui indique à quelle strophe du poème on en est. Le logiciel ne tourne pas en permanence et c'est donc ce fichier d'état qui lui permettra de ne pas partir de zéro à chaque fois. Le même fichier contient un identificateur du pouète (message sur le fédivers) précédemment envoyé, de façon à organiser toutes les strophes du poème en un seul fil.
  • Ensuite, il lit le fichier contenant le poème, une ligne vide indique le passage au pouète suivant. (Le fichier a été créé manuellement avec un éditeur.)
  • Le bot attend ensuite un nombre aléatoire de secondes, pour mettre un peu de variété dans les messages. Notez qu'il n'existe pas de moyen standard en shell de faire des nombres aléatoires.
  • Enfin, on écrit le pouète avec madonctl, qui prend en argument le fichier de configuration de ce compte (paramètre --config) et, sauf pour la première strophe, l'identificateur du pouète précédent (paramètre --in-reply-to). On note en retour l'identificateur (Status ID) du pouète.
  • Enfin, il n'y a plus qu'à écrire identificateur du pouète et numéro de la strophe dans le fichier d'état.

Le programme est lancé automatiquement par cron, envoyant une strophe toutes les trois heures :

    
# Ballade des pendus
35 0,3,6,9,12,15,18,21 * * * ballade-pendus.sh ballade-pendus.txt ballade

Et l'autre programme, celui derrière voirapp ? Il est écrit en Python, pour le plaisir de la variété. Il dépend de la bibliothèque Mastodon.py. Il est plus court que la version shell, en partie parce que Python offre des possibilités supérieures, mais également parce qu'il lui manque certaines fonctions. Par exemple, il est moins générique, le nom du poème est en dur dans le code. Le source est également disponible.

Comment fonctionne ce programme ? Il ressemble beaucoup au précédent. Il ouvre le fichier d'état (CHECKPOINT), y lit l'identificateur du pouète précédent et le numéro de la strophe. Il lit le fichier contenant la chanson (même formatage que pour le script en shell), et attend une durée aléatoire avant d'envoyer, et enfin écrit le nouveau fichier d'état. Pour envoyer, il se connecte à une instance Mastodon (mastodon = Mastodon(...)) et envoie le pouète via mastodon.status_post(...). Avant cela, il aura fallu s'enregistrer auprès de l'instance, pour fabriquer les fichiers contenant les lettres de créance (voirapp_clientcred.secret et voirapp_usercred.secret). Cet enregistrement peut également se faire en Python (une fois suffit, les lettres de créance seront enregistrées) :

Mastodon.create_app(
     'voirapp',
     api_base_url = 'https://botsin.space/',
     to_file = 'voirapp_clientcred.secret'
)
mastodon = Mastodon(
    client_id = 'voirapp_clientcred.secret',
    api_base_url = 'https://botsin.space/'
)
mastodon.log_in(
    USER,
    PASSWORD,
    to_file = 'voirapp_usercred.secret'
)
   

Le programme est ensuite lancé depuis cron, comme le précédent.


L'article seul

Articles des différentes années : 2019  2018  2017  2016  2015  2014  2013  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.