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.
First publication of this article on 27 March 2023
On 25 and 26 March, it was the IETF 116 hackathon in Yokohama. I worked on TLS probing of authoritative DNS servers.
The goal of the IETF hackathons is to test ideas that may become RFC later. "Running code" is an important part of IETF principles. It is both useful and pleasing to sit down together at big tables and to hack all weekend, testing many ideas and seeing what works and what does not.
A bit of context on this "TLS probing". The DNS is a critical
infrastructure of the Internet. It is used
for most activities on the Internet. Requests and responses were
traditionally sent in the clear, and this is
bad for privacy since they can be quite
revealing ("this user queried www.aa.org
", see
RFC 7626). To improve DNS privacy, the IETF developed two
sets of solutions: minimizing data (RFC 9156), to protect against the server you query, and
encrypting data, to protect against third parties
sniffing on the cable. For the second set, encryption, several
techniques have been developed, I will focus here on DoT (DNS over
TLS, RFC 7858) and DoQ (DNS over QUIC, RFC 9250) but mostly on DoT. These various techniques are
standardized for the client-to-resolver link, the most critical for
the privacy (see RFC 7626 for details). They are now
deployed, although not universally. But this leaves the
resolver-to-authoritative link in the clear. The current idea is
therefore to use encryption for this link, what we call ADoT
(authoritative DNS over TLS). Technically, it is the same DNS over
TLS than for the client-to-resolver link. But in practice, there are
some issues. A client talks to only one (or may be two) resolvers,
which he or she knows. But a resolver talks to thousands of
authoritative name servers. Some will have enabled ADoT, some
not. How to know?
There were some attempts in the DPRIVE working
group to define a signaling mechanism,
through which the operators of authoritative name servers could say
they support ADoT. But these efforts all fail. A general problem of
signalisation of services on the Internet is that the signal can be
wrong, and often is (advertising a service which actually des not
work). So, the approach on the draft we
worked on, draft-ietf-dprive-unilateral-probing
,
was different: probing the authoritative name server to see if it
really does ADoT.
The work was done by Yorgos Thessalonikefs (from NLnet Labs) and myself. Yorgos worked on adding probing support to Unbound and I tested existing ADoT deployment, as well as adding DoT support to the Drink authoritative name server.
First, let's see DoT in action, here with a local installation of Drink and the kdig client (part of Knot):
% kdig +tls @localhost -p 8353 foobar.test ;; TLS session (TLS1.3)-(ECDHE-X25519)-(ECDSA-SECP256R1-SHA256)-(AES-256-GCM) ;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 16994 ;; Flags: qr aa rd; QUERY: 1; ANSWER: 0; AUTHORITY: 1; ADDITIONAL: 1 ;; EDNS PSEUDOSECTION: ;; Version: 0; flags: ; UDP size: 1440 B; ext-rcode: NOERROR ;; Option (65023): 067265706F7274076578616D706C6503636F6D00 ;; PADDING: 348 B ;; QUESTION SECTION: ;; foobar.test. IN A ;; AUTHORITY SECTION: foobar.test. 10 IN SOA ns1.test. root.invalid. 2023032512 1800 300 604800 86400 ;; Received 468 B ;; Time 2023-03-25 21:43:26 JST ;; From 127.0.0.1@8353(TCP) in 44.7 ms
OK, it worked, a TLS connection was established (the TLS cryptographic algorithms used are displayed at the beginning of the response), and we got a DNS reply. Also, the answer was padded (RFC 7830) to 468 bytes (RFC 8467) in order to avoid traffic analysis.
ADoT can also be tested with the check-soa tool. Here, all the authoritative name servers of the domain have ADoT:
% check-soa -dot aaa-cool.fr ns01.one.com. 195.206.121.10: OK: 2023032101 2001:67c:28cc::10: OK: 2023032101 ns02.one.com. 185.10.11.10: OK: 2023032101 2001:67c:28cc:1::10: OK: 2023032101
And here only one has:
% check-soa -dot bortzmeyer.fr ns2.bortzmeyer.org. 2400:8902::f03c:91ff:fe69:60d3: ERROR: dial tcp [2400:8902::f03c:91ff:fe69:60d3]:853: connect: connection refused 172.104.125.43: ERROR: dial tcp 172.104.125.43:853: connect: connection refused ns4.bortzmeyer.org. 2001:4b98:dc0:41:216:3eff:fe27:3d3f: ERROR: dial tcp [2001:4b98:dc0:41:216:3eff:fe27:3d3f]:853: i/o timeout 92.243.4.211: ERROR: dial tcp 92.243.4.211:853: i/o timeout puck.nether.net. 2001:418:3f4::5: OK: 2023032600 204.42.254.5: OK: 2023032600
ADoT can also be tested with the RIPE Atlas probes, here with
the Blaeu
tool against a root name server,
B.root-servers.net
:
% blaeu-resolve --requested 100 --nameserver 199.9.14.201 --type SOA --tls --nsid . Nameserver 199.9.14.201 [TIMEOUT] : 2 occurrences [NSID: b4-ams; a.root-servers.net. nstld.verisign-grs.com. 2023032501 1800 900 604800 86400] : 54 occurrences [NSID: b4-lax; a.root-servers.net. nstld.verisign-grs.com. 2023032501 1800 900 604800 86400] : 12 occurrences [NSID: b4-iad; a.root-servers.net. nstld.verisign-grs.com. 2023032501 1800 900 604800 86400] : 15 occurrences [NSID: b4-mia; a.root-servers.net. nstld.verisign-grs.com. 2023032501 1800 900 604800 86400] : 3 occurrences [a.root-servers.net. nstld.verisign-grs.com. 2023032501 1800 900 604800 86400] : 1 occurrences [NSID: b4-sin; a.root-servers.net. nstld.verisign-grs.com. 2023032501 1800 900 604800 86400] : 1 occurrences Test #51316045 done at 2023-03-26T02:05:11Z
This is quite good, only two probes fail to connect over DoT, may be because they were on a network which blocks port 853 outgoing.
OK, so, some authoritative name servers have ADoT and some have
not. Now, what should the resolver do? Since there is no signaling,
should it try port 853? How to do it without waiting forever if the
server does not reply at all? The idea behind the
draft draft-ietf-dprive-unilateral-probing
is indeed to always try ADoT and use it if it works
but in an "intelligent" way. Instead of waiting
for ADoT to succeed or fail, the resolver should do it on the "happy
eyeballs" way (RFC 8305), trying both DoT and traditional
DNS a the same time. Note that it is an unilateral choice: the
resolver does all of the work and requires nothing from the
authoritative server.
One resolver today does probing, although not in the way of the
draft: PowerDNS
Recursor. Let's try it (their DoT support is dscribed
online). We get the
git, we follow the good
compilation instructions, we autoreconf
,
then ./configure
, then
make
. We then can run it with the option
--max-busy-dot-probes=100
(by default, it does not do probing). Note that probing in PowerDNS
is lazy: it is not always tried, only of there is a "sufficient"
traffic with the authoritative name server. You can see probing
going on with tcpdump on port 853 and
PowerDNS can report the results:
% rec_control dump-dot-probe-map /tmp/foo && fgrep Good /tmp/foo dump-dot-probe-map: dumped 13962 records 45.54.76.1 qtmlabs.xyz. 14 Good 2023-03-29T04:27:32 157.53.224.1 qtmlabs.xyz. 39 Good 2023-03-29T04:28:37 77.55.125.10 abstractionleak.com. 1 Good 2023-03-29T04:25:39 77.55.126.10 abc-sport.fr. 3 Good 2023-03-29T04:25:39 185.10.11.11 one.com. 4 Good 2023-03-29T04:28:02 195.206.121.11 one.com. 4 Good 2023-03-29T04:28:03 51.77.156.11 BILLAUD.EU.ORG. 1 Good 2023-03-29T00:09:32 129.134.30.12 facebook.com. 5 Good 2023-03-29T04:29:26 129.134.31.12 facebook.com. 6 Good 2023-03-29T04:27:39 185.89.218.12 facebook.com. 14 Good 2023-03-29T04:27:39 185.89.219.12 facebook.com. 5 Good 2023-03-29T04:27:39 139.99.155.59 EU.ORG. 1 Good 2023-03-29T04:27:32 185.49.140.60 nlnetlabs.nl. 3 Good 2023-03-29T04:26:37 172.105.50.79 geekyboo.net. 1 Good 2023-03-29T04:22:37 23.234.235.93 obo.sh. 1 Good 2023-03-29T03:34:12 212.102.32.200 cdn77.org. 1 Good 2023-03-29T03:47:44 199.9.14.201 . 17 Good 2023-03-29T04:30:39 136.243.57.208 distos.org. 1 Good 2023-03-29T03:44:00
We see here that all these nameservers have a working ADoT. In order to probe many domains, I have fed the resolver with data from the .fr zone, Cloudflare Domain Rankings and a list of fediverse instances. A partial list of domains which currently have working ADoT for at least one name server is:
. # Yes, the root facebook.com nlnetlabs.nl powerdns.com eu.org billaud.eu.org dyn.sources.org # Drink name server bortzmeyer.fr nether.net one.com # and the zones is hosts such as aaa-cool.fr desec.io # and the zones it hosts such as qtmlabs.xyz nazwa.pl # and the zones it hosts such as abc-sport.fr psyopwar.com # and the zones it hosts such as obo.sh cdn77.org distos.org
As you can see, even the root has a working ADoT server (see the announcement), despite a prudent statement of the root operators some time ago.
What about the work on Unbound? Unfortunately, it was more complicated than it seemed, since "happy eyeballs" probing (sending the request twice) runs against some assumptions in Unbound code. But the work by Yorgos made possible to better understand the issues, which is an important point for the hackathon.
The work of this weekend raised several questions, to be carried to the working group:
For the issue of consistency between port 53 and 853, this can go
at odds with current practices. For instance, the only
authoritative name server of
dyn.bortzmeyer.fr
hosts two different
services, Drink for the autoritative service on port 53 and an
open resolver, dnsdist, on port 853. So, the
answers are different. Should we ban this practice?
% check-soa -dot dyn.bortzmeyer.fr ns1-dyn.bortzmeyer.fr. 2001:41d0:302:2200::180: ERROR: REFUSED 193.70.85.11: ERROR: REFUSED % check-soa -tcp dyn.bortzmeyer.fr ns1-dyn.bortzmeyer.fr. 2001:41d0:302:2200::180: OK: 2023032702 193.70.85.11: OK: 2023032702
(The REFUSED is because check-soa does not send the RD - recursion desired - bit.)
All the presentations done at the end of the hackathon are online, including our presentation.
Auteur(s) du livre : Stéphane Mahieu
Éditeur : Éditions du Sandre
9-782358-211505
Publié en 2022
Première rédaction de cet article le 21 mars 2023
Un livre qui donne envie de voyager et de lire : l'auteur a dressé un catalogue de bateaux qui apparaissent dans des œuvres de fiction. Chaque entrée résume le bateau, sa carrière, son rôle dans le roman, et est l'occasion de passer d'œuvres archi-connues à des textes bien moins célèbres.
On y trouve le Titan, du roman « Le naufrage du Titan » (avec des notes mettant en avant, non pas les ressemblances avec le Titanic, comme souvent, mais les différences), le Fantôme du « Loup des mers » (un roman politique bien plus qu'un livre d'aventures maritimes), et bien sûr le Nautilus. Mais l'auteur présente aussi des vaisseaux issus de romans plus obscurs (en tout cas en France), comme l'Étoile polaire (dans le roman d'Obroutchev), le Leviatán, issu de l'imagination de Coloane ou le Rose-de-Mahé, créé par Masson.
À noter que la bande dessinée n'est pas représentée (ni le cinéma, d'ailleurs). L'entrée pour la Licorne ne décrit pas le vaisseau du chevalier de Hadoque mais elle parle d'un recueil d'histoires anciennes (le livre est sur le projet Gutenberg).
Bref, plein d'idées de lectures vont vous venir en parcourant ce livre… Mettez votre ciré, vérifiez les provisions de bord (et les armes !) et embarquez.
Auteur(s) du livre : Clément Osé, Noémie Calais
Éditeur : Tana
979-10-301-0424-0
Publié en 2022
Première rédaction de cet article le 20 mars 2023
Le débat sur la consommation de viande est souvent binaire, entre d'un côté les partisans de l'agriculture industrielle et de l'autre des animalolâtres qui refusent toute utilisation des animaux. Ce livre raconte le point de vue d'une éleveuse de porcs, qui essaie de faire de l'élevage de viande en échappant aux logiques industrielles.
Les partisans de l'agriculture industrielle reprochent souvent à leux opposants animalistes de ne rien connaitre à l'agriculture et au monde paysan. Ce n'est pas faux, mais, justement, ce livre est écrit, à deux mains, par des personnes qui vivent à la campagne, et qui travaillent la terre. Noémie Calais, l'éleveuse, a quitté son travail de consultante internationale pour s'occuper de porcs dans le Gers. Le travail quotidien est difficile (être paysanne n'a rien de romantique), et plein de questions se bousculent : a-t-on le droit d'élever des animaux pour finalement les tuer, comment faire une agriculture bio (ou à peu près) sans faire venir des kiwis, certes bios, mais transportés par avion depuis l'autre bout du monde, comment essayer de faire les choses proprement dans un environnement légal et économique où tout pousse à la concentration et à l'agriculture industrielle, avec toutes ses horreurs (pour les humains, comme pour les animaux). Les auteurs n'esquivent aucune question. Oui, il est légitime de manger les animaux, non, cela ne veut pas dire qu'il faut faire n'importe quoi.
Le livre va du quotidien d'une paysanne aux questions plus directement politiques, comme la réglementation délirante qui, sous couvert de protéger les consommateurs, crée tellement de normes et de certifications abstraites et déconnectées du terrain que seuls les gros, donc en pratique l'agriculture industrielle, ont des chances d'arriver à les respecter. Les arguments sanitaires, par exemple, peuvent être un prétexte pour démolir les circuits locaux. (Le cas n'est pas dans ce livre mais, par exemple, les règles de respect de la chaine du froid pour les fromages, qui sont les mêmes pour le fromage industriel sous plastique qui vient de l'autre bout du pays, et pour les dix ou vingt fromages de chèvre produits à deux kilomètres du marché sont un exemple typique.)
À lire avant d'aller au supermarché faire ses achats !
Première rédaction de cet article le 17 mars 2023
Si vous avez déjà créé/géré une machine virtuelle chez OVH, vous avez sans doute noté la bizarrerie de la configuration IPv6. En fait, la configuration automatiquement installée lorsque vous créez votre machine est incorrecte. Voici pourquoi, et comment la réparer.
Créons d'abord un serveur (j'ai utilisé l'API OpenStack mais le problème existait déjà avant).
% ip -6 addr show ... 2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000 inet6 2001:41d0:801:1000::233c/56 scope global valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fe10:748f/64 scope link valid_lft forever preferred_lft forever
La machine a une adresse IPv6,
2001:41d0:801:1000::233c
, dans un préfixe de
longueur 56. Chic, je me dis, je vais pouvoir parler directement
(sans passer par le routeur) avec les autres
machines dans ce même préfixe.
La table de routage va d'ailleurs en ce sens :
% ip -6 route show ... 2001:41d0:801:1000::/56 dev ens3 proto kernel metric 256 pref medium default via 2001:41d0:801:1000::1 dev ens3 metric 1 pref medium
Elle nous dit que tout le /56 est joignable sans intermédiaire (pas
de via).
Essayons depuis la
2001:41d0:801:1000::23b2
:
% ping -c 3 2001:41d0:801:1000::233c PING 2001:41d0:801:1000::233c(2011:41d0:801:1000::233c) 56 data bytes From 2001:41d0:801:1000::23b2 icmp_seq=1 Destination unreachable: Address unreachable ... --- 2001:41d0:801:1000::233c ping statistics --- 3 packets transmitted, 0 received, 100% packet loss, time 2030ms
Aïe, c'est raté, alors que les deux machines sont bien dans le même préfixe de longueur 56. Pourquoi ? Parce que les machines ne sont pas réellement sur un réseau local dont le préfixe ferait 56 bits. Elles doivent en fait toujours passer par le routeur, comme si elles étaient sur une liaison point-à-point avec ce routeur. (Pour la même raison, un tcpdump ne montrera pas de trafic, à part avec le routeur, par exemple du trafic NTP.)
Pour que cela marche, il faut renoncer à parler directement avec
les machines proches, et corriger la configuration. Ce sont des
Debian, on va éditer le fichier
/etc/network/interfaces.d/50-cloud-init
et
remplacer la longueur incorrecte (56) par la bonne (128). Et on
n'oublie pas de rajouter une route vers le routeur avant de définir
la route par défaut. On
redémarre et ça marche :
% ping -c 3 2001:41d0:801:1000::233c PING 2001:41d0:801:1000::233c(2001:41d0:801:1000::233c) 56 data bytes 64 bytes from 2001:41d0:801:1000::233c: icmp_seq=1 ttl=57 time=0.810 ms 64 bytes from 2001:41d0:801:1000::233c: icmp_seq=2 ttl=57 time=0.746 ms 64 bytes from 2001:41d0:801:1000::233c: icmp_seq=3 ttl=57 time=0.764 ms --- 2001:41d0:801:1000::233c ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2037ms rtt min/avg/max/mdev = 0.746/0.773/0.810/0.026 ms
Et la table de routage nous montre que tout va passer par le routeur :
% ip -6 route show ... 2001:41d0:801:1000::1 dev ens3 metric 1 onlink pref medium 2001:41d0:801:1000::233c dev ens3 proto kernel metric 256 pref medium default via 2001:41d0:801:1000::1 dev ens3 metric 1 pref medium
Il est dommage qu'on ne puisse pas parler directement aux autres machines du même préfixe. Peut-être est-ce pour des raisons de sécurité, ces machines pouvant appartenir à des organisations différentes ? C'est en effet la même chose en IPv4 mais, cette fois, avec une configuration par défaut qui est correcte (un préfixe de longueur 32).
Si vous créez beaucoup de machines virtuelles et qu'on vous n'avez pas envie de tout corriger à la main, et que, comme moi, vous utilisez Ansible, voici un playbook qui fait les choses plus ou moins proprement :
--- - hosts: coursipv6 become: yes become_method: sudo tasks: - name: Broken IPv6 config at OVH lineinfile: path: /etc/network/interfaces.d/50-cloud-init regexp: 'address {{ ipv6_address }}/56$' line: 'address {{ ipv6_address }}/128' backrefs: yes - name: Broken IPv6 routing at OVH lineinfile: path: /etc/network/interfaces.d/50-cloud-init regexp: 'route add -A inet6 default gw {{ ipv6_gateway }} || true$' line: '\tpost-up ip -6 route add {{ ipv6_gateway }} dev ens3 && route add -A inet6 default gw {{ ipv6_gateway }} || true' backrefs: yes
Auteur(s) du livre : Audric Gueidan, Halfbob
Éditeur : Dunod
978-2-10-083732-1
Publié en 2023
Première rédaction de cet article le 15 mars 2023
La plupart des lecteurices de ce blog savent que, sur l'Internet, on laisse des traces partout et que nos données personnelles sont peu protégées. (Le discours policier « l'Internet, c'est l'anonymat » est de la pure propagande.) Mais bien que ce fait soit largement connu et documenté, même au-delà des lecteurices de ce blog, on ne constate pas de changement de comportement des utilisateurices. Ceux et celles-ci continuent à distribuer trop largement leurs données personnelles. Il y a de nombreuses raisons à cet état de fait et je ne vais pas toutes les discuter. Mais une des raisons, pensent les auteurs de ce livre, est le manque d'information ; les utilisateurices ont bien entendu dire qu'il y avait des problèmes avec les données personnelles mais ielles ne mesurent pas complètement l'ampleur de ces problèmes, ni les formes qu'ils prennent. Comment leur expliquer ? Les auteurs tentent une BD, « Datamania ».
Alors, une BD, c'est du texte et du dessin. Personnellement, je ne suis pas très enthousiaste pour ce style de dessin (vous pouvez voir certains de ces dessins dans l'article de GeekJunior) mais comme les goûts artistiques varient, je n'argumenterai pas sur ce point. Le texte est léger et amusant, et couvre bien les sujets. Aucune erreur technique, les auteurs font preuve de beaucoup de rigueur. L'idée est d'expliquer à des gens qui n'ont pas envie de lire tous les rapports de la CNIL. Est-ce réussi ? Bon, il est clair que je ne suis pas le cœur de cible, le livre vise des gens pas très informés. Mais, justement, je trouve que le livre n'explique pas assez et qu'il abuse des private jokes. Certaines de ces allusions sont expliquées dans l'annexe « Avez-vous remarqué ? » mais j'avoue que je me suis demandé si un·e lecteurice ordinaire ne va pas rester perplexe. (Les lecteurices qui connaissent le sujet peuvent s'amuser, sans lire cette annexe, à repérer toutes les allusions à des personnes ou organisations connues.)
Le sujet, il est vrai, est très riche et très complexe. Le livre parle de surveillance, de logiciel libre, de cookies, de chiffrement et de beaucoup d'autres choses, et ne peut évidemment pas tout traiter en détail. (Un gros plus pour avoir cité Exodus Privacy.) Je conseillerai de l'utiliser comme introduction, ou comme support à des ateliers mais je ne suis pas sûr qu'il puisse être utilisé en autonomie par des utilisateurices ordinaires. (Bon, après, c'est vrai que je suis exigeant ; qu'en pensent d'autres personnes ? Voyez l'article de GeekJunior ou celui de Médias-Cité.)
Note : j'ai reçu un exemplaire gratuit du livre de la part des auteurs.
First publication of this article on 13 February 2023
The great technical conference FOSDEM resumed physical activity after two years of purely online events. I had the pleasure to make a small talk at the DNS devroom about the Drink dynamic authoritative DNS server.
The slides are available on the official site. The recorded video is available at the same place, as well as links to the source code, etc.
Première rédaction de cet article le 23 janvier 2023
Le 20 janvier de cette année, à la conférence informatique Touraine Tech, j'ai fait un exposé sur la neutralité du code. Lorsque le ou la développeureuse dit « je ne fais pas de politique, ce programme pourra être utilisé pour différents buts », est-ce possible ?
Voici les supports de l'exposé et leur source (en LaTeX avec Beamer).
Il y avait à cet évènement de nombreuses autres conférences intéressantes(sans compter les fouées aux rillettes) :
Les vidéos de ces différentes conférences sont en ligne (la mienne aussi).
Un exposé d'Olivier Mertens portait sur le générateur d'images
DALL-E (fait par la même société que ChatGPT). J'ai donc demandé à DALL-E « Une
conférence à TouraineTech dans le style de François
Boucher [puisqu'il y avait une exposition de ce dernier
au Musée des Beaux-Arts] » et le résultat
est… pas terrible : .
Et merci mille fois aux organisateurices (une telle conférence crée beaucoup de travail), et à Florian Roulet pour m'avoir invité à y prendre la parole.
Ploum et moi dédicaçant nos livres :
Première rédaction de cet article le 3 janvier 2023
Si Ada Lovelace était la première programmeuse, les programmeuses et programmeurs actuel·les sont-ielles les dernièr·es ? Un programme « intelligent » comme ChatGPT va-t-il les condamner à la reconversion ou au chômage ? Je continue ici mon exploration de ChatGPT.
ChatGPT a beaucoup été utilisé pour la programmation. Voici une session où je lui demande des programmes (ce sera la seule image, dans le reste de l'article, je copierai le texte).
Exemple d'utilisation :
Le programme est écrit en Elixir car mes questions précédentes portaient sur ce langage et ChatGPT mémorise les conversations (et il s'en sert, est-on averti, pour s'améliorer donc ne lui donnez pas d'informations confidentielles). L'utilisation d'Elixir montre que ChatGPT connait beaucoup de choses, y compris des langages assez rarement utilisés. C'est logique : pour le quantitatif (la mémorisation et la restitution d'énormes quantités d'informations), les ordinateurs sont certainement meilleurs que les humains. D'autre part, celles et ceux qui ont lu mon précédent article noteront que j'ai maintenant un compte ChatGPT, ayant trouvé un numéro de téléphone sacrifiable.
Beaucoup de gens ont été impressionnés par les qualités de ChatGPT et, voyant la facilité avec laquelle il génère des programmes (et les explique !), se sont dit que les programmeuses et les programmeurs allaient disparaitre. Un tel discours fait rêver bien des patrons, qui apprécieraient de remplacer des développeurs et développeuses payés chers par une IA docile et obéissante. Ce discours est donc très ancien dans l'informatique, Cobol avait déjà été promu avec cet argument. Et c'était également le cas de Fortran (« FORTRAN should virtually eliminate coding and debugging »). Même si ces affirmations semblent techniquement ridicules aujourd'hui, à l'époque, le progrès par rapport au langage d'assemblage était tel que cela ne semblait pas absurde. Puis on a entendu à nouveau ce raisonnement du temps des L4G puis récemment du nocode. Le fait que les espérances des patrons aient été déçues à chaque fois ne les empêche pas de garder espoir.
Voyons donc ce que sait faire ChatGPT. D'abord, on l'a dit, il
connait beaucoup de choses. Je demande « Je dois trier une liste de
chaînes de caractères [un exercice banal de débutant]. Comment
faire ? » et ChatGPT répond « Pour trier une liste de
chaînes de caractères en Elixir, vous pouvez utiliser la fonction
Enum.sort/1
. Cette fonction prend en entrée une
liste et renvoie une liste triée de ses éléments. Voici un exemple
de code qui trie une liste de chaînes de caractères en ordre
croissant : »
strings = ["c", "b", "a", "d"] sorted_strings = Enum.sort(strings) IO.inspect(sorted_strings) # ["a", "b", "c", "d"]
Il connait la bibliothèque standard d'Elixir
(où il y a bien un Enum.sort
)
et aussi des idiosyncrasies d'Elixir comme le
fait de nommer une fonction en terminant par son
arité. Et il ajoute spontanément « Si vous
voulez trier la liste en ordre décroissant, vous pouvez utiliser la
fonction Enum.sort/2 et lui fournir une fonction de comparaison qui
renvoie :gt si le premier argument est plus grand que le second, :lt
si le premier argument est plus petit que le second, et :eq s'ils
sont égaux. Voici un exemple de code qui trie une liste de chaînes
de caractères en ordre décroissant : »
strings = ["c", "b", "a", "d"] sorted_strings = Enum.sort(strings, fn x, y -> if x > y do :gt else if x < y do :lt else :eq end end) IO.inspect(sorted_strings) # ["d", "c", "b", "a"]
(Les :gt
et :lt
sont
appelés atomes et sont une des particularités d'Elixir. Je vous ai
dit qu'il « connaissait » bien le langage.)
Mais ce second code pose déjà des problèmes. D'abord, il ne
compile même pas (il manque un end
). Mais,
surtout, ChatGPT n'a pas bien lu la
documentation : la fonction passée en argument à
sort
est censée renvoyer un booléen, pas ces
atomes. On voit là une première chose à garder en mémoire : on
ne peut pas en général utiliser les programmes produits par ChatGPT
tels quels, il faut les vérifier, et cette vérification doit
être faite par une personne compétente en programmation. Et il n'est
pas évident que l'entièreté du processus soit plus rapide et plus
sûr que si cette personne compétente avait tout fait
elle-même. (L'erreur de syntaxe est la moins grave, car elle est
détectée à la compilation. Le pire serait une erreur qui ne
planterait pas mais donnerait un résultat faux.)
Ceci dit, ChatGPT apprend et il est interactif. Voyons cela sur un autre exemple : « Comment décomposer un entier de grande taille en ses facteurs premiers ? ». ChatGPT répond « Pour décomposer un entier en ses facteurs premiers en Elixir, vous pouvez utiliser une approche récursive. Voici comment vous pouvez implémenter cette approche : »
defmodule PrimeFactors do def factors(n, divisor \\ 2) when divisor > n do [n] end def factors(n, divisor \\ 2) do if rem(n, divisor) == 0 do [divisor | factors(n / divisor, divisor)] else factors(n, divisor + 1) end end end
Cette fois, il n'y a pas d'erreur de syntaxe, mais cela ne compile quand même pas, en raison d'une erreur sémantique. Et il y a deux autres bogues (trouver l'une d'elles nécessite de connaitre Elixir). Mais comme ChatGPT est interactif, je lui explique : « Attention, on ne peut pas avoir deux définitions avec des valeurs par défaut. Il faut réécrire ton code. » Il me répond que j'ai raison (d'autres personnes ont vu ChatGPT nier des problèmes) et « Vous avez raison, il n'est pas possible d'avoir deux définitions avec des valeurs par défaut pour le même nom de fonction en Elixir. Pour résoudre ce problème, vous pouvez utiliser une seule définition de fonction avec une valeur par défaut pour le diviseur, et ajouter une condition pour gérer le cas où le diviseur est supérieur à n. Voici comment vous pouvez réécrire le code : »
defmodule PrimeFactors do def factors(n, divisor \\ 2) when divisor > n do [n] end def factors(n, divisor \\ 2) do if divisor > n do [n] else if rem(n, divisor) == 0 do [divisor | factors(n / divisor, divisor)] else factors(n, divisor + 1) end end end end
Et il ajoute spontanément « Je vous recommande également de changer
le nom de la fonction factors/2
en
factors/1
et de changer la condition de la
clause de cas factors/1
pour qu'elle vérifie si
n est inférieur ou égal à 1. Voici comment le code final devrait
ressembler [sic] : »
defmodule PrimeFactors do def factors(n, divisor \\ 2) when n <= 1 do [] end def factors(n, divisor \\ 2) do if rem(n, divisor) == 0 do [divisor | factors(n / divisor, divisor)] else factors(n, divisor + 1) end end end
C'est amusant, le code réécrit a toujours la même bogue sur la
définition de fonctions avec des valeurs par défaut. Par contre,
ChatGPT a « réfléchi » et a corrigé une des autres bogues (qui
faisait que le nombre 1 était listé comme facteur premier). Bon,
tant pis, je corrige manuellement et je lui signale la troisième
bogue (celle qui nécessite de connaitre Elixir) : « Et attention, le
/ fait une division de réels, il faudrait utiliser
div
. ». Il le reconnait : « Vous avez raison,
la fonction / en Elixir effectue une division de réels et non une
division entière. Pour effectuer une division entière, vous pouvez
utiliser la fonction div/2
. Voici comment vous
pouvez utiliser la fonction div/2
dans la
fonction factors/1
: »
defmodule PrimeFactors do def factors(n, divisor \\ 2) when n <= 1 do [] end def factors(n, divisor \\ 2) do if rem(n, divisor) == 0 do [divisor | factors(div(n, divisor), divisor)] else factors(n, divisor + 1) end end end
À noter que ChatGPT avait montré qu'il connaissait l'éco-système Elixir, en expliquant comment tester avec l'interpréteur iex :
iex> PrimeFactors.factors(12) [2, 2, 3] iex> PrimeFactors.factors(2310) [2, 3, 5, 7, 11] iex> PrimeFactors.factors(7829) [7829]
Prenons un autre exemple qui illustre la question du cahier des charges. En programmation dans le monde réel, c'est souvent la principale difficulté ; le·a programmeur·se passe souvent davantage de temps à comprendre un cahier des charges incomplet, contradictoire et irréaliste qu'à programmer. Commençons simplement : « Écris un programme en Python qui donne la liste des nombres allant jusqu'à N. ». ChatGPT répond :
# Demande à l'utilisateur de saisir un entier N n = int(input("Entrez un entier N : ")) # Initialise une liste vide l = [] # Pour chaque entier i allant de 1 à N for i in range(1, n+1): # Ajoute i à la liste l.append(i) # Affiche la liste print(l)
Et explique « Ce programme demande à l'utilisateur de saisir un entier N, initialise une liste vide, puis utilise une boucle for pour ajouter chaque entier allant de 1 à N à la liste. Enfin, il affiche la liste. ». Modifions le cahier des charges (ce qui est courant) : « Il faut que cela marche avec des entiers relatifs. » ChatGPT ne se démonte pas pour si peu « Pas de problème, voici une version modifiée du programme qui prend en compte les entiers relatifs : »
# Demande à l'utilisateur de saisir un entier N n = int(input("Entrez un entier N : ")) # Initialise une liste vide l = [] # Si N est positif if n > 0: # Pour chaque entier i allant de 1 à N for i in range(1, n+1): # Ajoute i à la liste l.append(i) # Si N est négatif elif n < 0: # Pour chaque entier i allant de N à -1 (inclus) for i in range(n, 0): # Ajoute i à la liste l.append(i) # Si N est égal à 0 else: # Ajoute 0 à la liste l.append(0) # Affiche la liste print(l)
Et explique « Cette version vérifie si N est positif, négatif ou égal à 0 et utilise une boucle for appropriée pour ajouter les entiers à la liste. Si N est égal à 0, 0 est simplement ajouté à la liste. » On voit que ChatGPT est parfaitement capable de faire des exercices de programmation pour grands débutants, par exemple au lycée. Mais comme je suis méchant, je lui demande ensuite un programme qui marche avec les nombres réels, ce qui est impossible. Il me fait alors une variante qui énumère, mais avec un pas (qu'il a choisi arbitrairement) de 0,1. Dois-je le considérer comme très bête ou comme très intelligent d'avoir interprété de manière créative un cahier des charges absurde ? En tout cas, cet exemple montre à quoi ressemble souvent le travail de programmation : il faut de nombreuses itérations avant d'avoir une idée claire de ce qui est demandé et un programme comme ChatGPT n'est pas forcément le mieux placé pour cela. (Mes lecteurices qui connaissent la programmation auront déjà fait remarquer in petto que si on ne peut pas énumérer les réels - qui sont en nombre infini dans tout intervalle, quelle que soit sa taille, on pourrait par contre énumérer les nombres en virgule flottante représentables sur la machine. Mais ChatGPT n'y a pas pensé.)
Mais, bref, on est encore loin d'un système qui pourrait permettre de se passer complètement des développeurs et développeuses. Il peut aider, c'est certain, faisant gagner du temps lorsqu'on a oublié comment faire une requête HTTP dans tel ou tel langage de programmation, il peut servir d'« auto-complétion améliorée » mais cela ne permet pas de lui confier tout le travail. Alors, certes, il s'agit d'Elixir, mais est-ce que cela marcherait mieux avec des langages de programmation plus répandus ? J'en doute. Mémoriser une grande quantité d'informations et pouvoir en ressortir à volonté est justement le point fort des ordinateurs. Contrairement à un humain, apprendre un langage de plus ne nécessite guère d'effort. Les différents messages lus sur les réseaux sociaux semblent indiquer que des langages bien plus communs qu'Elixir ont le même problème (pareil pour la simple algèbre).
Parmi les autres faiblesses de ChatGPT qui font obstacle à une utilisation en totale autonomie, notons aussi :
En conclusion, oui, ChatGPT et les logiciels similaires vont changer la programmation (comme Cobol et Fortran l'avaient fait) mais croire qu'ils vont permettre de se passer de programmeur-ses est illusoire. Les développeur·euses qui produisent du code de basse qualité à la chaîne ont du souci à se faire pour leur avenir professionnel mais cela ne concerne pas celles et ceux qui font du code créatif, difficile, novateur, ou simplement bien adapté à leur problème. ChatGPT est très fort pour des tests simplistes, comme ceux souvent faits à l'embauche mais pour de vrais programmes ? C'est un point souvent oublié par les personnes qui ne connaissent pas la programmation : écrire un petit script qui fait une tâche simple n'est pas la même chose que de programmer Apache, Louvois, ou un logiciel de calcul scientifique comme Blast.
Quelques réflexions et éléments sur ce sujet :
Première rédaction de cet article le 14 décembre 2022
Tout le monde aujourd'hui utilise ChatGPT et envoie sur les réseaux sociaux les résultats les plus amusants ou les plus spectaculaires. La plupart des retours sont admiratifs devant les performances de ce système d'« Intelligence Artificielle » (IA) pour répondre à des questions et générer des textes. Mais il faut nuancer un peu.
Tout d'abord, un sérieux avertissement : je n'ai pas essayé ChatGPT moi-même, en raison de leur demande excessive de données personnelles (il faut indiquer une adresse de courrier et un numéro de téléphone, et les deux sont vérifiées par envoi d'un code à retourner ensuite ; on ne peut donc pas tricher). J'ai seulement lu les résultats de ChatGPT tels que publiés par ses utilisateurs. Si vous pensez que cela rend cet article sans valeur, arrêtez votre lecture tout de suite et allez faire autre chose, par exemple regarder les belles aquarelles d'Aemarielle.
Maintenant, revenons à ChatGPT. On lui pose des questions et il répond. Le résultat est souvent étonnant, par exemple lorsqu'on lui demande des textes « écrits dans le style de [telle personne] » ou lorsqu'on l'interroge sur des questions techniques complexes. Cela illustre de manière publique les progrès importants des techniques connues sous le nom commercial d'IA (Intelligence Artificielle), ainsi que la quantité vraiment colossale de données que ChatGPT a lues et assimilées. Il est par exemple à noter que ChatGPT écrit des textes bien meilleurs que ce que font beaucoup d'humains, y compris dans un environnement professionnel (« écris un communiqué de presse se félicitant de l'augmentation de X % du chiffre d'affaires de la société Machin » donnera un résultat indistinguable du « vrai »). Notamment, il ne fait aucune faute d'orthographe ou de grammaire.
Est-ce que cela signifie qu'on peut réellement parler d'intelligence et que les humains sont désormais inutiles ? Pas si vite. D'abord, l'intelligence, ce n'est pas de savoir exécuter une tâche, c'est de savoir quelle tâche exécuter. De ce point de vue, ChatGPT est loin de l'intelligence. Plusieurs personnes ont pu constater qu'on pouvait lui demander des textes contradictoires (« explique pourquoi il est important d'augmenter les impôts » puis « explique pourquoi il faut baisser les impôts », et ChatGPT s'exécutera).
Ensuite, ce que révèle ChatGPT, ce ne sont pas tellement les progrès de l'IA que le creux et l'absence de contenu de beaucoup de textes produits par des humains. ChatGPT sait faire des devoirs d'étudiants de première année, écrire des communiqués de presse, du reporting et produire les discours des ministres. Grâce à ce système, on voit bien que ces textes n'ont pas tellement de fond et ne nécessitent pas beaucoup d'intelligence, uniquement la lecture et le traitement d'une grande quantité d'informations, tâche où les humains sont certainement inférieurs aux ordinateurs. ChatGPT ne remplacera donc pas les humains mais lui ou ses successeurs pourront prendre en charge des tâches qui étaient considérées à tort comme nécessitant de l'intelligence. Comme le note Stéphane Mouton, ChatGPT est toujours « correct mais superficiel ».
Cela va certainement « disrupter » certains secteurs, comme celui des rédacteurs sous-payés qui écrivent vite et mal. Pour prendre un autre exemple, j'ai vu des étudiants de master produire des notes qui ne valaient pas ce que fait ChatGPT. L'enseignement devra donc s'adapter. Mais cela poussera à réfléchir à ce que nous voulons que les humains fassent. Écrire des synthèses fades et sans originalité ou bien travailler de manière plus créative ?
Mais, diront certains, ChatGPT et l'IA en général vont continuer à progresser. Les limites actuelles seront forcément dépassées. Eh bien non, ou plus exactement, c'est plus compliqué que cela. La marche du progrès technique peut faire croire que le progrès est forcément linéaire, chaque année marquant une amélioration technique. Des observations comme la loi de Moore vont en ce sens. Mais ce n'est pas une règle générale du progrès. Il y a également des techniques qui stagnent, ou qui ne progressent que par bonds imprévisibles. L'IA en est un bon exemple : depuis ses débuts (qui sont à peu près ceux de l'informatique), elle alterne des bonds spectaculaires avec de longues périodes de repos, le bond spectaculaire ayant été suivi d'une constatation qu'on n'arrivait pas à l'améliorer. Peut-être que ChatGPT va progresser, ou peut-être qu'il ne dépassera pas son stade actuel avant longtemps, mais on ne peut pas affirmer qu'il fera forcément mieux dans le futur.
Et sinon, non, petits coquins, cet article n'a pas été écrit par ChatGPT, Bruce Schneier a fait la blague avant moi (et je suis d'accord avec la plupart des commentaires à son article ; l'article est sans erreur mais enfonce des portes ouvertes et ne fait preuve d'aucune réflexion).
Date de publication du RFC : Novembre 2018
Auteur(s) du RFC : S. Hollenbeck (Verisign Labs), A. Newton (ARIN)
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 23 novembre 2022
Contrairement à son lointain prédécesseur
whois, le protocole d'accès aux informations
des registres RDAP a un mécanisme standard de
découverte du serveur faisant autorité. Ce mécanisme est prévu pour
des données organisées de manière
arborescente comme les noms de domaine ou les adresses IP. Pour des objets qui n'ont
pas cette organisation, comment faire ? Ce RFC fournit une solution pour des objets
quelconques, s'ils obéissent à une convention de nommage
simple. C'est le cas des handles, ces
identificateurs d'entités (titulaires de noms de domaine, contacts
pour un préfixe IP, etc) ; s'ils sont de la forme
QUELQUECHOSE-REGISTRE
, on pourra trouver le
serveur responsable.
Le mécanisme habituel de découverte du serveur est normalisé dans
le RFC 9224. En gros, l'IANA garde un
registre des serveurs, indexé par un nom de domaine ou par un
préfixe IP. Ce registre est au format JSON. Un client
RDAP est juste censé télécharger ce fichier,
trouver le serveur faisant autorité, puis le contacter. Pour pouvoir
faire la même chose avec des objets non structurés, il faut leur
imposer une convention de nommage. Si on interroge avec RDAP (ou
whois) l'adresse IP 88.198.21.14
, on voit que
son contact technique est HOAC1-RIPE
. Cette
convention de nommage est « d'abord un identifiant unique au sein du
registre, puis un tiret, puis le nom du registre, ici le RIPE-NCC ». L'IANA a juste à garder un registre de ces
registres qui nous dira, par exemple, que
RIPE
a comme serveur RDAP
https://rdap.db.ripe.net/
. On peut ensuite
l'interroger en RDAP :
% curl https://rdap.db.ripe.net/entity/HOAC1-RIPE "vcardArray" : [ "vcard", [ [ "version", { }, "text", "4.0" ], [ "fn", { }, "text", "Hetzner Online GmbH - Contact Role" ], [ "kind", { }, "text", "group" ], [ "adr", { "label" : "Hetzner Online GmbH\nIndustriestrasse 25\nD-91710 Gunzenhausen\nGermany" }, "text", [ "", "", "", "", "", "", "" ] ], [ "tel", { "type" : "voice" }, "text", "+49 9831 505-0" ], [ "tel", { ...
Petit piège, la partie du début peut elle-même comporter des tirets donc lorsqu'on coupe en deux un handle, il faut le faire sur le dernier tiret (section 2 du RFC). Pourquoi le tiret, d'ailleurs ? Parce qu'il est couramment utilisé comme séparateur, et parce que son utilisation dans un URL ne pose pas de problème particulier (RDAP utilise HTTPS et donc des URL).
Le registre des serveurs RDAP est géré à l'IANA comporte un tableau JSON des services, chaque entrée étant composée de trois tableaux, la liste des adresses de courrier du responsable, la liste des suffixes (en général, un seul par registre) et la liste des serveurs RDAP. Par exemple, pour l'ARIN, on aura :
[ [ "andy@arin.net" ], [ "ARIN" ], [ "https://rdap.arin.net/registry/", "http://rdap.arin.net/registry/" ] ]
Les entrées dans le registre sont ajoutées selon la politique « Premier Arrivé, Premier Servi » du RFC 8126.
Comme ce RFC décrit une
extension à RDAP, le serveur RDAP doit ajouter à sa réponse,
dans l'objet rdapConformance
,
rdap_objectTag_level_0
. (Apparemment, personne
ne le fait.)
Vous voulez du code ? La bibliothèque
ianardap.py
, qui est disponible
en ligne, peut récupérer la base et l'analyser. Cela permet
ensuite de faire des requêtes pour des objets :
% ./dump-object.py F11598-FRNIC | jq . ... [ "", "", "Association Framasoft c/o Locaux Motiv", "Lyon", "", "69007", "FR" ] ...
Le programme dump-object.py
étant simplement :
#!/usr/bin/env python3 import ianardap import requests import sys rdap = ianardap.IanaRDAPDatabase(category="objects") for object in sys.argv[1:]: url = rdap.find(object) if url is None: print("No RDAP server for %s" % object, file=sys.stderr) continue response = requests.get("%sentity/%s" % (url[0], object)) print(response.text)
Première rédaction de cet article le 21 novembre 2022
Dernière mise à jour le 21 février 2023
Les 19 et 20 novembre 2022, c'était Capitole du Libre à Toulouse, un rassemblement libriste important, qui reprenait après l'interruption Covid-19. Il y avait beaucoup de rencontres, d'ateliers et de conférences intéressantes. (En tout, 118 orateurs, 97 conférences, 16 ateliers et 39 stands au village associatif.)
J'ai fait deux exposés. Le premier, un peu plus politique, portait sur une question de programmation. Maintenant que tout est chiffré, comment déboguer les applications réseau alors qu'on ne peut plus utiliser Wireshark comme avant ? Il existe plusieurs solutions, de la lutte pour limiter le chiffrement, comme le font encore certains réactionnaires, à la coopération de l'application, qui est l'avenir. Voici la version en PDF, et le source en LaTeX/Beamer.
Le second exposé tirait les leçons du développement de Drink, un serveur DNS faisant autorité voué aux réponses dynamiquement générées. Voici la version en PDF, et le source en LaTeX/Beamer. Le code de Drink est disponible en ligne.
J'ai aussi participé à une table ronde sur le thème de la « souveraineté numérique », avec Gaël Duval, Philippe Latombe, et Amandine Le Pape, table ronde animée par Étienne Gonnu.
J'ai également participé à une séance dédicace de mon livre « Cyberstructure », qui avait été annoncé pour la première fois publiquement au Capitole du Libre il y a quatre ans. La séance était organisée par la librairie « Les petits ruisseaux ». J'étais à côté de David Revoy, l'auteur de Pepper et Carrot, qui faisait de superbes dessins.
Le programme de Capitole du Libre était très riche. J'ai beaucoup aimé « Introduction à Rust embarqué » de Sylvain Wallez, avec des belles démonstrations (l'inévitable diode clignotante) de ce langage en direct, sur un ATtiny85 (0,5 ko de RAM, 8 ko de flash) et un plus gros microcontrôleur ESP32. Également très technique, la descente en profondeur dans les entrailles des composants Ethernet « Plongée au coeur d'Ethernet, et son support dans Linux », de Maxime Chevallier.
Des enregistrements vidéo ont été faits et sont disponibles en ligne (dont les miens, le serveur DNS Drink et le déboguage d'applications chiffrées).
First publication of this article on 18 November 2022
The fediverse is supposed to be decentralized. But the fact that the network has a decentralized architecture does not mean that it is perfectly decentralized in practice. We survey here the DNS authoritative servers for the domains used by the fediverse instances. Unfortunately, yes, they are too concentrated.
Mostly because of the incredibly childish behaviour of
Elon Musk, the current wave of migration from
Twitter to the
fediverse is much larger than the previous
ones. As a result, there is a renewed interest in the
fediverse, a system which is far from
recent. The fediverse is made of various
instances, each one being independently
administered, both from a technical point of view, and from a
political one. Nothing new or extraordinary, this is how the
Internet was architectured, and how its
services worked, before a few centralized US-based giants distorted
the vision of many people, making them believe that centralization
is normal. Each fediverse instance has a domain
name and this domain name is hosted on a set of
authoritative name servers, which will reply to
the DNS requests of
the clients, the DNS resolvers. As an example, the instance I use,
mastodon.gougere.fr
is in the domain
gougere.fr
, whose set of authoritative name
servers is currently composed of three
servers. Unix users can for instance
display them with dig :
% dig +short NS gougere.fr nsb.bookmyname.com. nsa.bookmyname.com. nsc.bookmyname.com.
Note you can also perform DNS queries on the fediverse itself, as explained here (see the current result).
A domain "works", for the users, only if the authoritative name servers reply, and reply properly. This makes them critical infrastructure for an instance. If your DNS authoritative servers fail, or lie, the instance is unusable, even if the Pleroma, Mastodon or other server still works fine. So, what are the DNS servers used for this critical role in the current fediverse?
To get data, I started from a list of current instances. I got one
from instances.social
. Note
that, on a decentralized network like the fediverse, you cannot get
an official and complete list of all instances. All lists are
imperfect. But I have to start from somewhere (other possibilities:
http://demo.fedilist.com/instance
or the public
list of peers of a big instance such as https://mastodon.social/api/v1/instance/peers
). So, I created an
authentication token on instances.social
, and
retrieved a list with curl:
% curl -s -H "Authorization: Bearer MY-TOKEN" https://instances.social/api/1.0/instances/list\?count=10000\&include_down=false > list.json
I included only live instances
(include_down=false
) but, still, some instance
names already were missing from the DNS.
The resulting list was in the JSON format and processed by two Python scripts (see the code later). The first one queried the DNS (with the excellent dnspython library) to get information such as the list of authoritative name servers and their IP addresses. The second script processed the JSON data of the first to output meaningful statistics. Of course, like with any quantitative survey, the hard problem is often which questions to ask, rather than the answers.
Let's start with the actual numbers:
%./nameservers-fediverse-analyze.py There are 1596 instances
Remember that the list is incomplete (no one knows how many instances
really live). Some of these instances are grouped into the same
registered domain (the domain you bought from a
registry, may be through a
registrar). I choosed to group the instance
names in the DNS zones (a set of contiguous names hosted on the same
set of name servers). For instance,
medievalist.masto.host
and
piano.masto.host
are in the same zone,
masto.host
, they have the same servers. There
are a bit less zones than instance names:
There are 1596 instances in 1540 zones. The largest zone, masto.host., encompasses 10 instances
Except masto.host
, there are few fediverse
hosters with such subdomains, so no sign of
concentration/centralisation here. There is no giant fediverse
hoster.
Now, the name servers themselves:
There are 2062 nameservers. The largest one, dns2.registrar-servers.com., hosts 85 instances.
Some name servers are more common, such as this
dns2.registrar-servers.com
, used by a big DNS
hoster. But it is still only for a small minority of instances.
But wait, name servers come in groups, called sets. The instance administrator, except if he or she is a DNS fan, typically does not cherry-pick his or her name servers, they use a set indicated by the DNS hoster. Let's study sets instead:
There are 951 nameserver sets. The largest one, dns1.registrar-servers.com.;dns2.registrar-servers.com., hosts 85 instances. The top 10 % hosts 677 instances.
There are less sets than instances (a sign of a small centralisation) but not by a large margin. The biggest set is not so big but we note that the top tenth of the sets hosts more than a third of the instances. There are not so many DNS providers.
But there is a catch. Some DNS hosters use a lot of names so you
may think there are many various sets but they actually depend on
one company, a bad thing for decentralisation. The typical example
is Cloudflare, with a lot of cool names
depending on the domain they host
(eleanor.ns.cloudflare.com
,
sue.ns.cloudflare.com
, etc). So, we have to
group the name servers by company. One of the simplest ways is to find
the registered domain of the name servers' names. To do so, we use
the Public Suffix List,
which is not authoritative and not perfect but sufficient for us. It
will put all the Cloudflare names into one bucket, the registered
domain cloudflare.com
:
There are 667 nameserver's domains. The largest one, cloudflare.com, hosts 366 instances. The top 10 % hosts 1324 instances
This time, we have a clear sign of centralisation. A lot of seemingly independent instances have a shared provider, Cloudflare. If Cloudflare breaks, or does evil things, it will affect many instances. And the top 10 % of hosters serves the overwhelming majority of fediverse instances. For your information, the biggest ones are:
cloudflare.com
: 366registrar-servers.com
: 91gandi.net
: 90domaincontrol.com
: 55googledomains.com
: 49digitalocean.com
: 48linode.com
: 39inwx.de
: 23inwx.eu
: 23
(It can be noticed that, unlike the fediverse itself, which uses a
lot of "new TLDs" such as
.social
, the name servers are faithful to the
old TLDs.) Playing with names, as seen in the Cloudflare case,
complicated things. We have also the case of
AWS which does not appear on the above list
but they should; they use a lot of registered domains
(awsdns-46.co.uk
,
awsdns-20.com
, etc) so they appear as many
different hosters. Also, about Cloudflare, remember that this
survey is only about the DNS: I did not try to
see where the server instance is hosted. I repeat: just DNS, no HTTP
was used or considered here. (One also could note that some
authoritative name servers are installed at one big machine hoster,
which gives Cloudflare an even
more prominent place.)
So, as you can see, but this is hardly a surprise, the fediverse is not perfect and there are signs of centralisation, at least as far as DNS is concerned.
The two programs used for this article are:
nameservers-fediverse-gather.py
:
it gathers information from the DNS and produces a JSON file
storing, for each instance, its DNS zone, the name servers and
their IP addresses (which were not used for this article). To find
the DNS zone, it just climbs the DNS tree until it founds
name server records.nameservers-fediverse-analyze.py
:
it reads the JSON file produced by the first program and displays
statistics.Among the weaknesses of this analysis, you'll note that our
instances are considered equal. But there are very small and very big
instances. It could be useful to tweak results depending on the size
of the instance. instances.social
gives us this
information but it has to be handled with care, it is just a declaration
from the instance (and many accounts may be inactive, anyway).
Première rédaction de cet article le 10 novembre 2022
C'était le thème d'une réunion pendant l'IETF 115 à Londres. Pour censurer sur l'Internet, le censeur doit regarder ce à quoi l'utilisateur voulait accéder, ce qui pose des problèmes de vie privée évidents. Peut-on censurer sans violer la vie privée ?
Le problème était posé sous la forme d'une question technique
mais, évidemment, c'est plus compliqué que cela. Avant de détailler
tous les aspects, voyons comment fonctionne un mécanisme de censure
simple : l'utilisateur veut visiter
https://truc-interdit.example/
, les équipements
intermédiaires sur le réseau voient cette demande, la comparent à
une liste de choses interdites et bloquent éventuellement la
visite. On peut réaliser cela de plusieurs façons, par exemple par
un résolveur DNS menteur ou par un relais
HTTP qui regarde les URL. Peu importe : dans tous les cas, des
équipements intermédiaires apprendront qu'on voulait accéder à telle
ou telle ressource. C'est ennuyeux pour les droits
humains et ça rentre en conflit avec des techniques
comme DoT ou HTTPS, qui sont justement là
pour empêcher ce genre d'interceptions.
Mes lectrices et lecteurs informaticien·nes peuvent faire une pause ici et se demander comment techniquement réaliser une telle interception sans apprendre l'identité de la ressource à laquelle on voulait accéder. C'est un exercice intellectuellement stimulant. Mais, je le dis tout de suite, le problème n'est pas purement technique. En attendant d'élargir la question, je vous laisse quelques minutes pour réfléchir à une solution technique.
C'est bon, les quelques minutes sont écoulées ? Une solution possible serait, pour le cas du Web, que votre navigateur condense l'URL et envoie cet URL condensé à un service de filtrage qui répond Oui ou Non. Cela préserverait la vie privée, tout en permettant le filtrage. (Oui, il y a quelques détails techniques à prendre en compte, il faut saler pour éviter la constitution de dictionnaires, et il faut canonicaliser pour éviter qu'un malin ne fasse varier les URL pour éviter la détection, etc. Mais ne laissez pas ces détails techniques vous absorber trop longtemps.)
Le vrai problème de cette technique ? Elle nécessite la coopération de l'utilisateur (plus exactement de ses logiciels). C'était le point soigneusement dissimulé par le groupe qui avait organisé cette réunion, l'Internet Watch Foundation. (Au passage, cette réunion était un side meeting, réunion qui utilise les salles de l'IETF mais qui n'est pas forcément approuvée par l'IETF.) Le titre officiel de la réunion était « Is Privacy preserving Web Filtering Possible? » alors qu'en fait le désir n'était pas de filtrer mais de censurer (le filtrage se fait avec le consentement de l'utilisateur, la censure contre son gré). Le présentateur avait introduit la réunion en disant que le but était d'éviter qu'un internaute innocent ne soit exposé contre son gré à des images pédo-pornographiques. Si c'était vraiment le cas, le problème serait simple : un logiciel de filtrage sur la machine de l'utilisateur, comme on fait avec les bloqueurs de publicité. Le cas de la pédo-pornographie est un peu plus complexe car, contrairement aux bloqueurs de publicité, on ne souhaite pas distribuer les listes d'URL bloqués (des pédophiles pourraient les utiliser pour aprendre de nouvelles ressources). Mais ce n'est pas un gros problème, la solution du service qu'on interroge en lui envoyant un condensat de l'URL suffit.
Non, le vrai problème, c'est le consentement de l'utilisaeur. Si vraiment, on veut rendre un service à l'utilisateur, le problème est relativement simple. Si on veut censurer sans l'accord de l'utilisateur, cela devient effectivement plus compliqué. L'argument comme quoi on voulait protéger l'utilisateur contre une exposition accidentelle ne tient donc pas.
L'Internet Watch Foundation a présenté le problème comme purement technique, sachant très bien que les participant·es à l'IETF sont passionné·es de technique et vont sauter sur l'occasion, cherchant des solutions complexes et intéressantes (par exemple du côté du chiffrement homomorphique, la solution miracle souvent citée). Mais on est là dans un cas où la question n'est pas technique, mais politique. Il n'y a aucun moyen magique de censurer sans le consentement de l'utilisateur et en respectant sa vie privée. Le but de l'IWF était probablement justement de montrer qu'il n'y avait pas de solution technique (« on a été de bonne volonté on a soumis, la question à l'IETF ») et qu'il fallait donc censurer sans se soucier de vie privée.
Notons aussi qu'une infrastructure de censure, une fois en place, sert à beaucoup de choses. La pédo-pornographie, par l'horreur qu'elle suscite, sert souvent à couper court aux débats et à mettre en place des systèmes de censure, qui seront ensuite utilisés pour bien d'autres choses.
First publication of this article on 8 November 2022
Remember that IETF develops the
standards for the
Internet, roughly from layer
3 to some parts of layer 7. One
of the most important technologies at IETF is of course the
DNS. The role of
the IETF hackathons, that take place the
weekend just before the IETF meeting, is to try to implement new
ideas (not yet standardized) to see if it works (IETF prides itself
on relying on "running code"). This time, we were four persons
working on DNS error reporting. This technique is currently
specified in an Internet Draft
draft-ietf-dnsop-dns-error-reporting
. The
problem it tries to solve is the one of informing the managers of a
zone that there is some technical problem detected by a DNS
resolver. Today, if you don't test your zone thoroughly with tools
like DNSviz or Zonemaster, you'll know that
there is a problem only when users will complain on
Twitter. It would be better to be told by
your clients, the resolvers, before that.
The way it works is a follows:
You can see there are three actors and therefore three programs to modify (the last ones, the servers for the report receiving domain, could be unmodified authoritative name servers). I worked on the authoritative side, using the experimental name server Drink. Willem Toorop worked on the Unbound resolver and Shane Kerr on another (proprietary) authoritative server. Roy Arends helped, replied and modified the draft with our feedback.
The draft is quite simple and straightforward. Drink was modified to emit a new EDNS option:
report_to = Binary.from_list(encode(config()["reporting-agent"])) length = byte_size(report_to) Binary.append(<<Drink.EdnsCodes.reporting::unsigned-integer-size(16), length::unsigned-integer-size(16)>>, report_to)
And then to process the reports (the query type for the reports is TXT):
"report" -> if config()["services"]["report"] do case qtype do :txt -> result = Drink.Reports.post(Enum.join(qname, ".")) %{:rcode => Drink.RCodes.noerror, :data => [result], :ttl => @report_ttl} :any -> @any_hinfo _ -> %{:rcode => Drink.RCodes.noerror} end
Reports are stored in a separate agent. Note that we check they are
properly formed, with the label _er
at the
beginning and at the end:
def post(value) do labels = String.split(value, ".") if List.last(labels) == "report" do labels = List.delete_at(labels, length(labels)-1) # Remove report if List.first(labels) == "_er" and List.last(labels) == "_er" do labels = List.delete_at(List.delete_at(labels, length(labels)-1), 0) # Remove _er error_code = List.last(labels) qtype = List.first(labels) qname = Enum.join(List.delete_at(List.delete_at(labels, length(labels)-1), 0), ".") Agent.update(__MODULE__, fn state -> [{error_code, qtype, qname} | state] end) "Thanks for the report of error #{error_code} on #{qname}" else # Else do nothing, probably QNAME minimization (or may be broken report) "Invalid report" end end end
From the outside, this looks like this:
% dig _er.1.foobar.example.9._er.report.dyn.courbu.re TXT ... ;; ANSWER SECTION: _er.1.foobar.example.9._er.report.dyn.courbu.re. 24 IN TXT "Thanks for the report of error 9 on foobar.example" ;; AUTHORITY SECTION: dyn.courbu.re. 15 IN NS dhcp-80f2.meeting.ietf.org. ...
Since Drink is fully dynamic, it allows us to send a response depending on the analysis of the report (but the draft says you can return anything, you can use a static TXT record). If the report is invalid:
% dig _er.1.foobar.example.9.missing-end.report.dyn.courbu.re TXT ... ;; ANSWER SECTION: _er.1.foobar.example.9.missing-end.report.dyn.courbu.re. 30 IN TXT "Invalid report"
One can then retrieve the report, by asking the name server:
% echo report | socat - UNIX-CONNECT:/home/stephane/.drink.sock REPORT state: foobar.example, 9 funny.example, 9;2
Lessons from the hackathon? Drink is well suited for rapid exprimentation (it was one of its goals), the draft is clear and simple and interoperability is fine. By querying the modified Unbound resolver, we see report queries arriving and being accepted. Here, a DNSSEC error:
% dig @2a04:b900:0:100::28 www.bogus.bortzmeyer.fr ... ;; OPT PSEUDOSECTION: ... ; EDE: 9 (DNSKEY Missing): (validation failure <www.bogus.bortzmeyer.fr. A IN>: key for validation bogus.bortzmeyer.fr. is marked as invalid because of a previous validation failure <www.bogus.bortzmeyer.fr. A IN>: No DNSKEY record from 2001:41d0:302:2200::180 for key bogus.bortzmeyer.fr. while building chain of trust) ...
(Errors are the errors specified in RFC 8914.) And Drink saw:
nov. 08 18:17:14 smoking Drink[365437]: [info] 185.49.141.28 queried for _er.1.www.bogus.bortzmeyer.fr.9._er.report.dyn.courbu.re/txt
% echo report | socat - UNIX-CONNECT:/home/stephane/.drink.sock REPORT state: foobar.example, 9 funny.example, 9;2 www.bogus.bortzmeyer.fr, 9
So, it is a success. You can see all the reports made at the end
of the hackathon (ours is
"IETF115-DNS-Hackathon-Results.pdf "). Outside of that, there is
a Wireshark implementation of the EDNS
option:
Because it is a work in progress, note that there were changes before, during and after the hackathon. The query type for the reports changed from NULL to TXT (the code for NULL was tested and works), the EDNS option is now returned only when the resolver asks for it (this is not yet done in Drink), the format of the query report changed, etc. The goal is to test, not to make stable code.
The code, written in Elixir, is available
online. It is not yet merged in the master, you have to use
the git branch
error-reporting
.
First publication of this article on 7 November 2022
While adding IPC support to an Internet server written in Elixir, I did not find a lot of documentation or examples about Unix sockets with Elixir. So, here it is.
Unix sockets (also mentioned as "domain" or "local") are a way to communicate between processes (hence IPC = Inter-Process Communication) on the same machine. As such, they are less general than TCP/IP sockets but they are more secure (you cannot connect from the outside, and you can retrieve the process identifier, user identity, etc of the client) and you can finely control access through normal file permissions since the socket appears as a file.
To have the server written in Elixir listen on such a socket, we first clean (we use the standard module File):
sockname = System.get_env("HOME") <> "/.test-ipc.sock" File.rm(sockname) # We don't test the results, since we are not interested # if the file existed or not.
Then we can open the socket, using the Erlang
socket library, indicating the type
:local
:
{:ok, s} = :socket.open(:local, :stream, %{}) :ok = :socket.bind(s, %{family: :local, path: sockname}) # You can add File.chmod here to set permissions :ok = :socket.listen(s)
Note that we use pattern matching to be sure the operations succeeded (we would have better error messages with a code testing the different cases).
Now, we can accept connections (the rest of the code is not specific to Unix local sockets):
{:ok, client} = :socket.accept(socket)
And read from the clients, and write to them (here, we just echo the message), like we would do with more common TCP/IP sockets:
{:ok, result} = :socket.recv(socket, 0) msg = String.trim(result) Logger.info("Received \"#{msg}\"") :ok = :socket.send(socket, msg <> "\r\n")
A complete echo server using these techniques is
. Just run it:
ipc-serve.exs
% elixir server.exs Listening on /home/stephane/.test-ipc.sock
Clients can be netcat:
% netcat -U ~/.test-ipc.sock allo allo Tu es là? Tu es là?
Or socat:
% echo foobar | socat - UNIX-CONNECT:/home/stephane/.test-ipc.sock foobar
Or of course a client you write yourself.
Date de publication du RFC : Octobre 2022
Auteur(s) du RFC : A. Cabellos
(UPC-BarcelonaTech), D. Saucez
(INRIA)
Pour information
Réalisé dans le cadre du groupe de travail IETF lisp
Première rédaction de cet article le 3 novembre 2022
Le protocole réseau LISP (Locator/ID Separation Protocol, rien à voir avec le langage de programmation du même nom) est normalisé dans le RFC 6830 et plusieurs autres qui l'ont suivi. Ce RFC 6830 est un peu long à lire et ce nouveau RFC propose donc une vision de plus haut niveau, se focalisant sur l'architecture de LISP. Cela peut donc être un bon point de départ vers les RFC LISP.
L'idée de base de LISP est de séparer l'identificateur du localisateur, comme recommandé par le RFC 4984. Un identificateur désigne une machine, un localisateur sa position dans l'Internet. Aujourd'hui, les adresses IP servent pour les deux, ne satisfaisant parfaitement aucun des deux buts : les identificateurs devraient être stables (une machine qui change de réseau ne devrait pas en changer), les localisateurs devraient être efficaces et donc être liés à la topologie, et agrégeables.
LISP a donc deux classes : les identificateurs, ou EID (End-host IDentifier) et les localisateurs, ou RLOC (Routing LOCators). Les deux ont la syntaxe des adresses IP (mais pas leur sémantique). Un système de correspondance permet de passer de l'un à l'autre (par exemple, je connais l'EID de mon correspondant, je cherche le RLOC pour lui envoyer un paquet). LISP est plutôt prévu pour être mis en œuvre dans les routeurs, séparant un cœur de l'Internet qui n'utilise que les RLOC, d'une périphérie qui utiliserait les EID (avec, entre les deux, les routeurs LISP qui feraient la liaison).
En résumé (accrochez-vous, c'est un peu compliqué) :
C'est ce côté « solution dans les routeurs » et donc le fait que les machines terminales ne savent même pas qu'elles font du LISP, qui distingue LISP des autres solutions fondées sur la séparation de l'identificateur et du localisateur, comme ILNP (RFC 6740).
Au passage, pourquoi avoir développé LISP ? Quel était le problème à résoudre ? Outre la beauté conceptuelle de la chose (il n'est pas esthétique de mêler les fonctions d'identificateur et de localisateur), le principal problème à traiter était celui du passage à l'échelle du système de routage de l'Internet, décrit dans le RFC 4984. En gros, le nombre de routes distinctes augmente trop vite et menace la stabilité des routeurs de la DFZ. Le but était donc, par une nouvelle architecture, de rendre inutile certains choix qui augmentent la taille de la table de routage (comme la désagrégation des préfixes IP, afin de faire de l'ingénierie de trafic). Le RFC 7215 décrit comment LISP aide dans ce cas.
La section 7 du RFC décrit les différents scénarios d'usage de LISP :
La section 3 du RFC décrit en détail l'architecture de LISP (après, vous serez mûr·e·s pour lire les RFC LISP eux-mêmes, en commençant par le RFC 6830). Elle repose sur quatre principes :
La séparation entre identificateur et localisateur n'est pas faite au niveau de la machine individuelle, comme avec ILNP, mais à la frontière entre la périphérie de l'Internet (the edge) et son cœur (the core, en gros, la DFZ et quelques routeurs en plus). La périphérie travaille avec des EID (elle ne sait même pas que LISP est utilisé), le cœur avec des RLOC. Contrairement à ILNP, il n'y a donc pas une stricte séparation entre identificateurs et localisateurs : ils ont la même syntaxe (qui est celle d'une adresse IP, v4 ou v6) et, à part les routeurs d'entrée et de sortie des tunnels LISP, personne ne sait s'il utilise un EID ou un RLOC : les machines terminales manipulent des EID, les routeurs du cœur des RLOC, tout en croyant que ce sont des adresses IP ordinaires. Seuls les routeurs à la frontière entre les deux mondes connaissent LISP (et auront donc besoin d'un logiciel adapté).
Un Internet LISP est donc une série de « sites LISP » (des
réseaux de périphérie accessibles par LISP) connectés par des
tunnels entre eux, au-dessus du cœur
actuel. Le routeur d'entrée du tunnel se nomme ITR (pour
Ingress Tunnel Router) et celui de sortie ETR
(pour Egress Tunnel Router). Le terme de xTR
(pour « ITR ou bien ETR ») est parfois utilisé pour désigner un
routeur LISP, qu'il soit d'entrée ou de sortie
Le sous-système des données (data plane) se charge d'encapsuler et de décapsuler les paquets, puis de les transmettre au bon endroit. (Sa principale qualité est donc la rapidité : il ne faut pas faire attendre les paquets.) Les ITR encapsulent un paquet IP qui vient d'un site LISP (dans un paquet UDP à destination du port 4341), puis l'envoient vers l'ETR. À l'autre bout du tunnel, les ETR décapsulent le paquet. Dans le tunnel, les paquets ont donc un en-tête intérieur (un en-tête IP normal, contenant les EID source et destination), qui a été placé par la machine d'origine, et un en-tête extérieur (contenant le RLOC source, celui de l'ITR, et le RLOC de destination, celui de l'ETR puis, après l'en-tête UDP, l'en-tête spécifique de LISP). Rappelez-vous que les routeurs du cœur ne connaissent pas LISP, ils font suivre ce qui leur semble un paquet IP ordinaire. Les routeurs LISP utilisent des tables de correspondance entre EID et RLOC pour savoir à quel ETR envoyer un paquet.
Ces tables ont été apprises du sous-système de contrôle (control plane, qui contient la fonction de correspondance - mapping), le routeur ayant un cache des correspondances les plus récentes. Cette fonction de correspondance, un des points les plus délicats de LISP (comme de tout système de séparation de l'identificateur et du localisateur) est décrite dans le RFC 6833. Son rôle peut être comparé à celui du DNS et de BGP dans l'Internet classique.
Une correspondance est une relation entre un préfixe d'identificateurs (rappelez-vous que les EID sont, syntaxiquement, des adresses IP ; on peut donc utiliser des préfixes CIDR) et un ensemble de localisateurs, les RLOC des différents routeurs possibles pour joindre le préfixe convoité.
Le RFC 6833 normalise une interface avec ce système de correspondance. Il y a deux sortes d'entités, le Map Server, qui connait les correspondances pour certains préfixes (car les ETR lui ont raconté les préfixes qu'ils servent), et le Map Resolver, qui fait les requêtes (il est typiquement dans l'ITR, ou proche). Quatre messages sont possibles (les messages de contrôle LISP sont encpasulés en UDP, et vers le port 4342) :
Map-Register
: un ETR informe son
Map Server des préfixes EID qu'il sait
joindre,Map-Notify
: la réponse de l'ETR au
message précédent,Map-Request
: un ITR (ou bien un
outil de débogage comme lig, cf. RFC 6835) cherche les RLOC correspondant à un EID,Map-Reply
: un Map
Server ou un ETR lui répond.Un point important de LISP est qu'il peut y avoir plusieurs mécanismes de correspondance EID->RLOC, du moment qu'ils suivent les messages standard du RFC 6833. On pourra donc, dans le cadre de l'expérience LISP, changer de mécanisme pour voir, pour tester des compromis différents. Notre RFC rappele l'existence du système ALT (RFC 6836, fondé, comme BGP sur un graphe. Mais aussi celle d'un mécanisme utilisant une base « plate » (NERD, RFC 6837), un mécanisme arborescent nommé DDT (RFC 8111), des DHT, etc. On pourrait même, dans des déploiements privés et locaux, avoir une base centralisée avec un seul serveur.
ALT, normalisé dans le RFC 6836, est le système de correspondance « historique » de LISP, et il est souvent présenté comme le seul dans les vieux documents. Il repose sur BGP, protocole bien maitrisé par les administrateurs de routeurs, ceux qui auront à déployer LISP. L'idée de base est de connecter les serveurs ALT par BGP sur un réseau virtuel au-dessus de l'Internet.
DDT, dans le RFC 8111, lui, ressemble beaucoup plus au DNS, par sa structuration arborescente des données, et sa racine.
Évidemment, tout l'Internet ne va pas migrer vers LISP instantanément. C'est pour cela que notre RFC mentionne les problèmes de communication avec le reste du monde. Les EID ne sont typiquement pas annoncés dans la table de routage globale de l'Internet. Alors, comment un site pourra-t-il communiquer avec un site LISP ? Le mécanisme décrit dans le RFC 6832 utilise deux nouvelles sortes de routeurs : les PITR (Proxy Ingress Tunnel Router) et les PETR (Proxy Egress Tunnel Router). Le PITR annonce les EID en BGP vers l'Internet, en les agrégeant le plus possible (l'un des buts de LISP étant justement d'éviter de charger la table de routage globale). Il recevra donc les paquets envoyés par les sites Internet classiques et les fera suivre par les procédures LISP normales. A priori, c'est tout : le site LISP peut toujours envoyer des paquets vers l'Internet classiques en ayant mis un EID en adresse IP source. Mais cela peut échouer pour diverse raisons (uRPF, par exemple) donc on ajoute le PETR : il recevra le paquet du site LISP et le transmettra.
Voici pour les principes de LISP. Mais, si vous travaillez au quotidien comme administrateur d'un réseau, vous avez sans doute à ce stade plein de questions concrètes et opérationnelles. C'est le moment de lire la section 4 de ce RFC. Par exemple, la gestion des caches : un routeur LISP ne peut pas faire appel au système de correspondance pour chaque paquet qu'il a à transmettre. Le sous-système des données tuerait complètement le sous-système de contrôle, si un routeur s'avisait de procéder ainsi. Il faut donc un cache, qui va stocker les réponses aux questions récentes. Qui dit cache dit problèmes de cohérence des données, puisque l'information a pu changer entre la requête, et l'utilisation d'une réponse mise en cache. Pour gérer cette cohérence, LISP dispose de divers mécanismes, notamment un TTL (Time To Live) : l'ETR le définit, indiquant combien de temps les données peuvent être utilisées (c'est typiquement 24 h, aujourd'hui).
Autre problème pratique cruciale, la joignabilité des
RLOC. C'est bien joli de savoir que telle machine a tel RLOC mais
est-ce vrai ? Peut-on réellement lui parler ou bien tous les
paquets vont-ils finir dans un trou noir ? Un premier mécanisme
pour transporter l'information de joignabilité est les LSB
(Locator Status Bits). Transportés dans les
paquets LISP, ces bits indiquent si un RLOC donné est joignable
par l'ETR qui a envoyé le paquet. Évidemment, eux aussi peuvent
être faux, donc, s'il existe une communication bi-directionnelle,
il est préférable d'utiliser le mécanisme des numniques. Quand un ITR écrit à un ETR, il
met un numnique dans le paquet, que l'ETR renverra dans son
prochain paquet. Cette fois, plus de doute, l'ETR est bien
joignable. Si l'ITR est impatient et veut une réponse tout de
suite, il peut tester activement la joignabilité, en envoyant des Map-Request
.
LISP est souvent présenté avec un modèle simplifié où chaque site est servi par un seul ETR, qui connait les EID du site et les annonce au Map Server. Mais, dans la réalité, les sites sérieux ont plusieurs ETR, pour des raisons de résilience et de répartition de charge. Cela soulève le problème de leur synchronisation : ces ETR doivent avoir des configurations compatibles, pour annoncer les mêmes RLOC pour leurs EID. Pour l'instant, il n'existe pas de protocole pour cela, on compte sur une synchronisation manuelle par l'administrateur réseaux.
Enfin, comme LISP repose sur des tunnels, il fait face à la malédiction habituelle des tunnels, les problèmes de MTU. Du moment qu'on encapsule, on diminue la MTU (les octets de l'en-tête prennent de la place) et on peut donc avoir du mal à parler avec les sites qui ont une MTU plus grande, compte-tenu de la prévalence d'erreurs grossières de configuration, comme le filtrage d'ICMP. La section 4.4 de notre RFC décrit le traitement normal de la MTU dans LISP et ajoute que des mécanismes comme celui du RFC 4821 seront peut-être nécessaires.
Dans l'Internet d'aujourd'hui, une préoccupation essentielle est bien sûr la sécurité : d'innombrables menaces pèsent sur les réseaux (section 8 du RFC). Quelles sont les problèmes spécifiques de LISP en ce domaine ? Par exemple, certains systèmes de correspondance, comme DDT, sont de type pull : on n'a pas l'information à l'avance, on va la chercher quand on en a besoin. Cela veut dire qu'un paquet de données (sous-système des données) peut indirectement déclencher un événement dans le sous-système de contrôle (par la recherche d'une correspondance EID->RLOC afin de savoir où envoyer le paquet). Cela peut affecter la sécurité.
D'autant plus que le sous-système de contrôle sera typiquement
mis en œuvre dans le processeur généraliste des routeurs, beaucoup
moins rapide que les circuits électroniques spécialisés qui
servent à la transmission des données. Un attaquant qui enverrait
des tas de paquets vers des EID différents pourrait, à un coût
très faible pour lui, déclencher plein de demandes à DDT,
ralentissant ainsi sérieusement les routeurs LISP. Une mise en œuvre naïve de LISP où toute requête pour
un EID absent du cache déclencherait systématiquement une
MAP-Request
serait très vulnérable. Une
limitation du trafic est donc
nécessaire.
En parlant du système de correspondance, il représente évidemment un talon d'Achille de LISP. Si son intégrité est compromise, si des fausses informations s'y retrouvent, les routeurs seront trahis et enverront les paquets au mauvais endroit. Et si le système de correspondance est lent ou en panne, par exemple suite à une attaque par déni de service, le routage ne se fera plus du tout (à part pour les EID encore dans les caches des routeurs). On peut donc comparer ce système de correspondance au DNS dans l'Internet classique, par son côté crucial pour la sécurité.
Il faut donc des « bons » Map Server, qui suivent bien le RFC 6833 (notamment sa section 6) et, peut-être dans le futur, des Map Servers qui gèrent l'extension de sécurité LISP-Sec (si son RFC est publié un jour).
Dernier point de sécurité, le fait que LISP puisse faire du routage asymétrique (le chemin d'Alice à Bob n'est pas le même que celui de Bob à Alice). Rien d'extraordinaire à cela, c'est pareil pour lee routage Internet classique, mais il faut toujours se rappeler que cela a des conséquences de sécurité : par exemple, un pare-feu ne verra, dans certains cas, qu'une partie du trafic.
On trouvera plus de détails sur les attaques qui peuvent frapper LISP dans le RFC 7835.
Pour ceux qui sont curieux d'histoire des technologies, l'annexe A du RFC contient un résumé de LISP. Tout avait commencé à Amsterdam en octobre 2006, à l'atelier qui avait donné naissance au RFC 4984. Un groupe de participants s'était alors formé, avait échangé, et le premier Internet-Draft sur LISP avait été publié en janvier 2007. En même temps, et dans l'esprit traditionnel de l'Internet (running code), la programmation avait commencé et les premiers routeurs ont commencé à gérer des paquets LISP en juin 2007.
Le groupe de travail IETF officiel a été ensuite créé, en mars 2009. Les premiers RFC sont enfin sortis en 2013.
LISP n'a pas toujours été comme aujourd'hui ; le protocole initial était plutôt une famille de protocoles, désignés par des numéros, chacun avec des variantes sur le concept de base. Cela permettait de satisfaire tous les goûts mais cela compliquait beaucoup le protocole. On avait LISP 1, où les EID étaient routables dans l'Internet normal (ce qui n'est plus le cas), LISP 1.5 où ce routage se faisait dans un réseau séparé, LISP 2 où les EID n'étaient plus routables, et où la correspondance EID->RLOC se faisait avec le DNS, et enfin LISP 3 où le système de correspondance était nouveau (il était prévu d'utiliser une DHT...). Le LISP final est proche de LISP 3.
Première rédaction de cet article le 21 octobre 2022
La chaine de blocs, malgré quelques soubresauts et critiques, reste aujourd'hui un puissant argument marketing. On voit par exemple une université se vanter d'attester ses diplômes sur cette chaine, et son service de la communication pense apparemment que cela va jouer en sa faveur. Il est vrai que la chaine de blocs résout élégamment des problèmes auparavant considérés comme difficiles, voire impossibles, par exemple le triangle de Zooko. Mais, comme tous les outils, la chaine de blocs ne résout pas tous les problèmes. Voyons un cas où elle n'apporte rien, qui est justement le cas d'usage de l'université citée plus haut.
La chaine de blocs n'est pas simplement une liste chainée de données. Si c'était le cas, elle n'aurait rien d'intéressant, cette structure de données étant une des plus anciennes qui soit. De même, le fait que les transactions contenues dans le bloc soient signées est assez banal, les signatures numériques sont un concept ancien et répandu bien avant l'invention de la chaine de blocs. L'intérêt de la chaine de blocs est ailleurs : dans le fait qu'elle est pair-à-pair, que n'importe qui puisse y écrire, et qu'un consensus émerge entre des entités qui ne se font pas mutuellement confiance. C'est ainsi que fonctionne son utilisation la plus emblématique, les cryptomonnaies. Les gens qui acquièrent et dépensent les jetons de la cryptomonnaie utilisée ne se font pas confiance et ne se connaissent même pas. Il faut pourtant arriver à un consensus sur le fait qu'Alice ait trois jetons et, qu'après en avoir donné un à Bob, elle n'en a plus que deux (alors qu'Alice préfererait qu'on croit qu'elle en a toujours trois). C'est cela que permet la chaine de blocs et, dans ce cas d'usage, elle est irremplaçable. (Le tout est bien expliqué dans l'article de Satoshi Nakamoto qui décrit le Bitcoin, et qui a intoduit le concept de chaine de blocs. Ironiquement, le terme de blockchain n'apparait pas dans cet article.)
Le but affiché des cryptomonnaies est justement de réaliser une monnaie qui ne dépende pas d'une autorité extérieure, par exemple une banque centrale. Mais si on a une telle autorité et qu'on lui fait confiance, la chaine de blocs devient inutile. Elle fonctionne toujours mais n'est pas l'utilisation la plus intelligente des ressources informatiques.
Or, c'est justement le cas de l'université citée plus haut. Pour valider des diplômes, on ne veut pas du pair-à-pair, bien au contraire. Il y a une autorité, l'université, et c'est elle, et elle seule, qui peut dire si j'ai une maitrise de physique ou pas (mon diplôme universitaire le plus élevé). Si on met les diplômes sur une chaine de blocs, on ne souhaite certainement pas que tout le monde puisse y écrire. Le cas est donc très différent de celui des cryptomonnaies, ou d'autres utilisations de la chaine de blocs comme la réservation de noms. Notons en outre qu'aucun détail n'est donné sur la chaine utilisée par l'université : laquelle est-ce, qui la contrôle, son logiciel est-il publié, etc. En l'absence de tous ces éléments, la chaine de blocs n'apporte aucune confiance supplémentaire. L'université aurait tout aussi bien publier les diplômes sur son site Web… (Avec quelques techniques de sécurité comme HTTPS.) Et si on veut un système commun à toutes les universités, il existe déjà.
Les défenseurs de ces utilisations inutiles de la chaine de blocs citent parfois l'argument de la signature, qui permet d'authentifier le diplôme, y compris si l'information circule et n'est pas récupérée sur le site d'origine. C'est vrai, les signatures numériques sont une très bonne idée, mais elles sont bien antérieures aux chaines de blocs et peuvent être utilisées dans ce cas (par exemple en publiant des documents signés sur le site Web). Là encore, la chaine de blocs n'apporte rien. Notez que le responsable du projet est parfaitement conscient de cette inutilité puisque, dans les commentaires à un article de NextInpact, il dit ouvertement « j'assume que le mot blockchain a un peu été un prétexte ».
Une faiblesse courante avec les chaines de blocs est que peu de gens vérifient directement sur la chaine, pourtant la seule source fiable. Ils passent en général par un système centralisé. L'entreprise privée qui a été payée par l'université le dit d'ailleurs dans les commentaires à l'article de Next Inpact déjà cité : « Techniquement, tout est fait dans BCdiploma pour que la lecture des attestations soit la plus simple possible en masquant la complexité de la blockchain. Par exemple, les certificats sont ici lus depuis le site de l’université, augmentant ainsi la confiance. » (un exemple de ce que ça donnerait). Ça annule une bonne partie de l'intérêt du projet.
Pour les mêmes raisons, tous les projets de « chaines de blocs privées » ou de « chaines à permission » sont des non-sens, en raison de leur inutilité. (À l'exception peut-être de chaines pas publiques mais pas complètement privées, entre un petit nombre d'acteurs qui ne se font qu'une confiance limitée.) Évidemment, cette inutilité n'empêche pas les projets (par exemple au niveau européen), car il y a beaucoup de consultants et d'ESN à nourrir.
Mais attention, dit la lectrice attentive de cet article : l'université peut publier des diplômes signés sur son site Web, d'accord, mais elle peut aussi arrêter de les publier. Si on fait confiance à l'université pour certifier les diplômes, mais qu'on craint qu'elle n'essaie de réécrire le passé en prétendant qu'un diplôme n'a jamais existé, la publication sur le site Web n'aide pas. Bonne remarque, et il faut donc aller plus loin que la simple publication de documents signés sur un site Web. Mais on n'a pas besoin de chaine de blocs, il suffit d'utiliser des journaux publics à ajout seul (append-only logs).
Le principe de ces journaux est de publier l'information sous une forme qui rend toute altération ultérieure détectable. Il existe plusieurs solutions pour cela, mais la plus simple conceptuellement (mais pas la plus rapide) est de numéroter chaque document publié. La suppression d'un document peut ainsi être détectée. Si on a le document 67 et qu'on ne trouve pas le 66, le problème est visible. (Ce système de numérotation des documents pour assurer l'intégrité est un très vieux système, antérieur à l'informatique, avec les journaux papier utilisés par exemple dans les commissariats. Comme le dit Wikipédia, « Il est strictement interdit de modifier ou même de raturer une inscription en main courante sous peine de la rendre caduque, c'est pourquoi les pages d'une main courante papier sont toujours numérotées. ») En dehors du monde papier, un tel système est simple à faire : l'autorité numérote les documents, les signe et les publie. Les données étant publiques, n'importe qui peut facilement vérifier leur intégrité. Un tel système est par exempe décrit dans l'article « How to time-stamp a digital document » (à télécharger ici). Un des systèmes les plus anciens à être effectivement déployé est Stamper (cf. son historique), qui publiait ses signatures (faites avec PGP)… sur Usenet.
Depuis, bien d'autres systèmes de journaux à ajout seul sont apparus et sont utilisés. L'un des plus connus est le Certificate Transparency (normalisé dans le RFC 9162). Pas du tout besoin d'une chaine de blocs pour cette tâche cruciale, puisqu'il n'y a qu'un petit nombre d'émetteurs faisant autorité (les AC).
Au passage, j'ai dit qu'un tel système permettait de détecter les modifications. Or il y a des modifications légitimes, par exemple le retrait d'un diplôme obtenu à tort. Il faut donc prévoir la possibiité qu'un document annule un précédent (mais on ne supprime pas le précédent, on le remplace). La vérification publique est contradictoire avec la possibilité d'oubli, ce qui peut être une bonne ou une mauvaise chose.
Première rédaction de cet article le 20 octobre 2022
Le 18 octobre 2022, la société Unstoppable Domains a annoncé
qu'elle arrêtait de vendre des noms sous
.coin
. Il y a des leçons intéressantes à en
tirer.
Ces noms ont la syntaxe de noms de domaine mais ne « fonctionnent » pas, au sens où un nom de domaine habituel fonctionne, via une résolution par le DNS. Ils sont enregistrés, oui, dans une chaine de blocs, mais ne fonctionnent pas avec vos logiciels classiques. En pratique, ils sont très peu utilisés, ceux qui les achètent sont plutôt dans une démarche d'investissement, espérant qu'ils prendront de la valeur plus tard, plutôt que dans une démarche de présence en ligne, comme lorsqu'on achète un nom de domaine plus classique pour recevoir du courrier électronique ou afficher un site Web.
Plusieurs services analogues existent, certains commerciaux (on peut vendre des noms, des identificateurs), d'autres pas. C'est aussi un monde où on rencontre de tout, du marketing boursouflé (Web3 !), à certains vendeurs qui induisent en erreur leurs clients, par exemple en leur faisant croire que ces noms auront le même usage qu'un nom classique, utilisable via le DNS. Et il y a aussi de purs escrocs. L'intérêt, en théorie, d'enregistrer des noms dans une chaine de blocs, plutôt que via un registre traditionnel, est que la chaine fonctionne automatiquement, sans intervention humaine une fois lancée, et qu'on peut donc rester titulaire de son nom de domaine, sans risque de le voir saisi ou censuré (d'où le nom de la société Unstoppable Domains). L'idée est très ancienne (elle avait commencé avec Namecoin en 2010) mais a pris de l'ampleur ces dernières années, avec l'apparition d'intermédiaires comme Unstoppable Domains, ENS, Emercoin, etc. La vogue récente des NFT a mené certains à renommer leurs produits « NFT » mais il n'y a pas de changement de fond.
Un point important de toute cette offre est qu'il n'y a pas de coordination. Tout le monde (et son chat) peut créer une chaine de blocs ou, si on est moins ambitieux, un service de création de noms sur une chaine de blocs existante (la façon de réaliser un tel service avait été détaillée lors de la JCSA 2016). L'Internet est « sans permission » ce qui veut dire, et heureusement, qu'une innovation peut être créée et déployée sur l'Internet, sans l'autorisation de personne. Cela a permis Bitcoin (qui a créé le concept de chaine de blocs), BitTorrent mais aussi le Web (si Tim Berners-Lee avait dû patienter jusqu'à l'autorisation d'un comité, on attendrait toujours le Web). La contrepartie de cette liberté est qu'il y a aussi de mauvaises idées et des concurrences dommageables. Comme chacun (et son hamster) peut lancer un service de création et de vente de noms sur une chaine de blocs, si ces noms ont une syntaxe compatible, il y a des risques de collision. Une collision, c'est quand deux noms identiques sont enregistrés via des services différents. C'est inévitable si deux services vendent des noms avec le même suffixe.
Et c'est justement ce qui s'est produit ici : Unstoppable Domains
vendait du .coin
mais Emercoin le faisait
également. Les collisions étaient donc inévitables. Finalement,
Unstoppable Domains a décidé d'arrêter, notant qu'Emercoin était
présent avant (mais qu'ils ne
le savaient pas). Notons l'ironie du nom Unstoppable Domains
puisque cette entreprise peut supprimer un nom à sa guise…
Ces problèmes de collision sont inévitables dès que plusieurs
organisations créent des noms sans aucune coordination. C'est une
des raisons pour lesquelles les racines DNS alternatives n'ont
jamais décollé. Leurs promoteurs évacuent souvent le problème des
collisions avec de vagues promesses « on s'arrangera ». Ici, un des
deux acteurs impliqués dans la collision a décidé d'arrêter mais il
n'y a aucune garantie que cela se passera toujours bien. C'est pour
cela que le RFC 2826 insiste sur l'importance
d'une coordination formelle. Dit autrement, il ne faut qu'un seul
registre pour .coin
(ou
.nimporte-quoi
).
J'ai dit plus haut qu'Unstoppable Domains avait supprimé les noms
en .coin
. En fait, c'est plus compliqué que
cela. Les noms sont toujours dans la chaine de blocs (dont l'un des
buts est justement d'empêcher l'effacement du passé) et Unstoppable
Domains peut donc expliquer qu'en fait, ils n'ont pas supprimé les
noms. Mais, comme indiqué précédemment, presque aucune application
ne va regarder directement dans la chaine de blocs. Elles passent
quasiment toutes par des passerelles diverses, utilisant des
protocoles normalisés comme HTTP ou DNS. Ce qu'Unstoppable Domains
a coupé, comme
ils l'indiquent, ce sont ces
passerelles. C'est en effet une malhonnêteté intellectuelle
fréquente chez certains services se présentant comme
pair-à-pair : le support (ici, la chaine de
blocs) est bien pair-à-pair, mais en pratique presque tout le monde
y accède via une passerelle qui, elle, est centralisée. Lorsque
cette passerelle est fermée, vous perdez tout. (Le problème est loin
d'être spécifique à Unstoppable Domains ; regardez comme les
ressources IPFS, service censément pair-à-pair sont
toujours annoncées sous forme d'un URL passant par une passerelle
centralisée. C'est en partie dû au fait que le logiciel est très
complexe à compiler et installer.)
Cette dépendance de nombreux services pair-à-pair vis-à-vis de passerelles centralisées, donc vulnérables à la censure ou à des décisions business, est un des gros problèmes, à l'heure actuelle, de beaucoup de solutions pair-à-pair.
Quelques bonnes lectures pour finir :
.wallet
.Première rédaction de cet article le 20 octobre 2022
Dernière mise à jour le 21 octobre 2022
Cet article n'aura probablement pas beaucoup de lecteurs car peu de gens utilisent le protocole EPP. Ce dernier sert uniquement à la communication entre registres et bureaux d'enregistrement. Il utilise le format XML, et est décrit via un langage de schéma, ce qui permet sa validation par un programme. Comme ce n'est pas tout à fait évident, je montre ici comment on peut faire cette validation.
EPP est normalisé dans le RFC 5730. Mais attention, cela ne normalise que le cœur du protocole. EPP est un protocole d'avitaillement d'objets et il peut manipuler différents types d'objets, par exemple des noms de domaine. Il faut donc ajouter un RFC par type d'objet (pour les noms de domaine, c'est le RFC 5731) et à chaque fois un nouvel espace de noms. Et EPP a diverses extensions, avec à chaque fois un espace de noms. Tout cela est formellement décrit en XML Schema. Avantage : on peut valider un document EPP automatiquement et s'assurer qu'il est conforme à ce qu'on attend, avant de le traiter.
On va utiliser ici xmllint, qu'on trouve
dans tous les bons systèmes d'exploitation. xmllint exige qu'on lui
passe un fichier unique pour le schéma, donc je vous ai fait un joli
fichier XML Schema qui inclut toutes les extensions auxquelles j'ai
pensé,
. Ensuite,
on doit récupérer à
l'IANA tous les fichiers epp-wrapper.xsd
.xsd
concernant
les différents schémas qu'on utilise. Cela peut se faire avec le
script Python
et cette boucle
shell :
extract-xsd.py
for schema in $(./extract-xsd.py epp-wrapper.xsd); do wget https://www.iana.org/assignments/xml-registry/schema/${schema} done
On peut ensuite valider un document EPP. Prenons celui-ci comme exemple :
<?xml version="1.0" encoding="utf-8"?> <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:authInfo> <domain:pw>2fooBAR</domain:pw> </domain:authInfo> </domain:create> </create> <clTRID>ABC-12345</clTRID> </command> </epp>
Cela donne :
% xmllint --noout --schema epp-wrapper.xsd test.xml test.xml validates
Parfait. Un programme qui va traiter les données peut, s'il a
validé, être tranquille. Il sait par exemple qu'il aura une et une
seule commande dans l'élément <epp>
.
Notez que c'est en écrivant cet article qu'une faille a été trouvée dans le RFC 9167.
Si maintenant on prend un document EPP invalide (ajout d'un
<foobar>
) :
% xmllint --noout --schema epp-wrapper.xsd test-invalid.xml test-invalid.xml:12: element foobar: Schemas validity error : Element '{urn:ietf:params:xml:ns:epp-1.0}foobar': This element is not expected. test-invalid.xml fails to validate
Parfait encore, le document invalide est rejeté.
Si on utilise une extension à EPP comme celle pour DNSSEC du RFC 5910 :
<?xml version="1.0" encoding="utf-8"?> <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> <secDNS:create xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1"> <secDNS:dsData> <secDNS:keyTag>12345</secDNS:keyTag> <secDNS:alg>3</secDNS:alg> <secDNS:digestType>1</secDNS:digestType> <secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest> <!-- <secDNS:keyData>, la clé elle-même, est *facultatif* --> </secDNS:dsData> </secDNS:create> </extension> <clTRID>ABC-12345</clTRID> </command> </epp>
La validation sera possible grâce à tous les schémas chargés :
% xmllint --noout --schema epp-wrapper.xsd test-dnssec.xml test-dnssec.xml validates
Première rédaction de cet article le 15 septembre 2022
Le mardi 13 septembre est sortie la version 15 d'Unicode. Une description officielle des principaux changements est disponible mais voici ceux qui m'ont intéressé particulièrement. (Il n'y a pas de changement radical.)
Pour explorer plus facilement la grande base Unicode, j'utilise un programme qui la convertit en SQL et permet ensuite de faire des analyses variées. Faisons quelques requêtes SQL :
ucd=> SELECT count(*) AS Total FROM Characters; total -------- 149251
Combien de caractères sont arrivés avec la version 15 ?
ucd=> SELECT version,count(version) FROM Characters GROUP BY version ORDER BY version::float; ... 12.0 | 554 12.1 | 1 13.0 | 5930 14.0 | 838 15.0 | 4489
4489 nouveaux caractères, c'est pas mal (la version 14 était plus calme). Quels sont ces nouveaux caractères ?
ucd=> SELECT To_U(codepoint) AS Code_point, name FROM Characters WHERE version='15.0' ORDER BY Codepoint; code_point | name -----------+---------------------------------------------------------------------------- ... U+1D2C0 | KAKTOVIK NUMERAL ZERO ... U+1E4D4 | NAG MUNDARI LETTER ONG ... U+1F776 | LUNAR ECLIPSE ... U+1F77C | MAKEMAKE U+1F77D | GONGGONG ... U+1FACF | DONKEY
Cette version amène en effet des écritures nouvelles comme le Nag Mundari, ou les chiffres de Kaktovik.
Si vous avez les bonnes polices de caractères, vous allez pouvoir voir quelques exemples (sinon, le lien mène vers Uniview). Voici par exemple la lettre ETT du Nag Mundari 𞓩 et le chiffre de Kaktovik 2 𝋂 Il y a également de nouveaux symboles, notamment liés à l'astronomie, comme l'occultation 🝵 ou la petite planète Orcus 🝿. En plus anecdotique, on a le sans-fil 🛜, les maracas 🪇 ou l'élan 🫎. Ce dernier a d'ailleurs eu droit au point de code, U+1FACE, une utilisation amusante de l'hexadécimal.
Tiens, d'ailleurs, combien de caractères Unicode sont des symboles (il n'y a pas que les emojis parmi eux, mais Unicode n'a pas de catégorie « emoji ») :
ucd=> SELECT count(*) FROM Characters WHERE category IN ('Sm', 'Sc', 'Sk', 'So'); count ------- 7770
Ou, en plus détaillé, et avec les noms longs des catégories :
ucd=> SELECT description,count(category) FROM Characters,Categories WHERE Categories.name = Characters.category AND category IN ('Sm', 'Sc', 'Sk', 'So') GROUP BY category, description; description | count -----------------+------- Modifier_Symbol | 125 Other_Symbol | 6634 Math_Symbol | 948 Currency_Symbol | 63
Articles des différentes années : 2023 2022 2021 2020 2019 2018 2017 Précédentes années
Syndication : Flux Atom avec seulement les résumés et Flux Atom avec tout le contenu.