Autres trucs

Accueil

Seulement les RFC

Seulement les fiches de lecture

echoping

Ève

Recherche dans ce blog :

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.


delv, le futur outil principal de déboguage de DNSSEC ?

Première rédaction de cet article le 26 juillet 2014


La version 9.10 de BIND vient avec un nouvel outil de déboguage en ligne de commande, delv. Qu'apporte-t-il par rapport à dig ? C'est surtout pour ce qui concerne DNSSEC qu'il est utile.

L'outil de base de celui qui débogue un problème DNS, l'équivalent de ce que sont ping et traceroute pour l'ingénieur réseaux, c'est dig. Cet outil permet de récupérer des données DNS et de les afficher. Avec le DNS traditionnel, on n'avait pas besoin de plus. Mais avec DNSSEC, un problème supplémentaire survient : les données obtenues sont-elles vérifiées cryptographiquement ? dig avait bien une option +sigchase pour tester cela mais, très boguée, elle est restée peu connue. On n'avait donc pas d'outil simple, en ligne de commande, pour valider du DNSSEC. On a bien sûr drill (livré avec ldns) mais son option -S est très bavarde.

Depuis la version 9.10 de BIND, est apparue avec ce logiciel un nouvel outil, nommé delv. delv permet de faire la même chose que dig (en moins bavard par défaut) :

% delv  www.ssi.gouv.fr
; unsigned answer
www.ssi.gouv.fr.	21600	IN	A	86.65.182.120

% delv -t mx  laposte.net
; unsigned answer
laposte.net.		600	IN	MX	10 smtp4.laposte.net.

Mais c'est surtout lorsque le domaine est signé que delv est utile. (Autrement, comme vous l'avez vu plus haut, il affiche un triste unsigned answer.) Testons avec un nom correctement signé :

% delv www.bortzmeyer.org
; fully validated
www.bortzmeyer.org.	52712	IN	A	204.62.14.153
www.bortzmeyer.org.	52712	IN	RRSIG	A 8 3 86400 20140813162126 20140724102037 37573 bortzmeyer.org. ZU9fgjc34O2xfBoN1crbGpMsw8Ov56Jex/6yuSHiAEUFSYTK1H4kBrcK H6MIoeUPrA6/4gmtPTBrc6+KWv765ioWgvHiGPPa8v85T2w0zLsX24NT 4Zz0XVsfybu1re/Nr6V//BOnVF3cZ2hFvdI1Yp9EiKLnRFfPrYX316Hx aJI=

Cette fois, il a procédé à une validation DNSSEC de la réponse (correcte, dans ce cas, fully validated).

Vous allez le dire « mais il suffit d'utiliser un résolveur DNS validant et on sait, via le bit AD (Authentic Data) que les données sont correctement signées ». Mais delv donne des informations supplémentaires. Par exemple, si le domaine est signé mais pointe vers un domaine non-signé :

% delv www.paypal.co.uk           
; fully validated
www.paypal.co.uk.	3600	IN	CNAME	www.intl-paypal.com.edgekey.net.
www.paypal.co.uk.	3600	IN	RRSIG	CNAME 5 4 3600 20140816050243 20140717044122 22776 paypal.co.uk. TU8EubeTmR0NCU+bLlhWPTJL91fUkoExM/86ZIL6qr1kJU7g69lqRzLJ N33TzZdIOgj7g1O4eEyUUgBKxe8GpH5dLisXlHhi64aluT4DkL3PwqDi W9enJobC660aPSp0vebasOnJlCZ2rRwKKqmWUiRfXhhkKmu1U1l4IOMV wmY=

; unsigned answer
www.intl-paypal.com.edgekey.net. 120 IN	CNAME	e493.a.akamaiedge.net.
e493.a.akamaiedge.net.	20	IN	A	23.58.178.37

Dans ce cas (aucun CDN ne gère aujourd'hui DNSSEC, hélas), delv indique bien quelle partie de la réponse est signée et laquelle ne l'est pas.

Et en cas de problème ? Si on teste avec des domaines délibérement cassés :

% delv -t mx servfail.nl 
;; resolution failed: failure

Eh oui, on utilise un résolveur validant, il ne nous donne donc pas les données liées à ce domaine. Il faut donc (paradoxalement !) utiliser un résolveur non-validant ou bien, tout simplement, ajouter l'option +cd, Checking Disabled, à delv :

% delv +cd -t mx servfail.nl
;; validating servfail.nl/DNSKEY: verify failed due to bad signature (keyid=25594): RRSIG has expired
;; validating servfail.nl/DNSKEY: no valid signature found (DS)
;; no valid RRSIG resolving 'servfail.nl/DNSKEY/IN': 127.0.0.1#53
;;   validating R6K26LDO0GS7N66JPQALLM0JIDU6PHML.servfail.nl/NSEC3: bad cache hit (servfail.nl/DNSKEY)
;; broken trust chain resolving 'servfail.nl/MX/IN': 127.0.0.1#53
;; resolution failed: broken trust chain

Voilà, on connait désormais la raison du problème, l'enregistrement DNSKEY de servfail.nl a une signature expirée. Avec un autre domaine, comportant une autre erreur (une signature délibérement modifiée) :

% delv +cd -t mx dnssec-failed.org
;; validating dnssec-failed.org/DNSKEY: no valid signature found (DS)
;; no valid RRSIG resolving 'dnssec-failed.org/DNSKEY/IN': 127.0.0.1#53
;;   validating dnssec-failed.org/NSEC: bad cache hit (dnssec-failed.org/DNSKEY)
;; broken trust chain resolving 'dnssec-failed.org/MX/IN': 127.0.0.1#53
;; resolution failed: broken trust chain

Et, ici, avec une date de signature dans le futur :

% delv +cd -t aaaa futuredate-aaaa.test.dnssec-tools.org
;; validating futuredate-aaaa.test.dnssec-tools.org/AAAA: verify failed due to bad signature (keyid=19442): RRSIG validity period has not begun
;; validating futuredate-aaaa.test.dnssec-tools.org/AAAA: no valid signature found
;; RRSIG validity period has not begun resolving 'futuredate-aaaa.test.dnssec-tools.org/AAAA/IN': 127.0.0.1#53
;; resolution failed: RRSIG validity period has not begun

Comme vous avez vu, delv est parfait pour du premier déboguage, donc, quitte à utiliser des outils plus complexes par la suite. Au fur et à mesure que les versions récentes de BIND se diffuseront, et que DNSSEC sera plus courant, dig sera sans doute souvent remplacé par delv.


L'article seul

RFC 7301: Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : S. Friedl (Cisco Systems), A. Popov (Microsoft), A. Langley (Google), E. Stephan (Orange)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 24 juillet 2014


On le sait, désormais, tout l'Internet tourne sur le port 443 (et aussi sur 80 mais de moins en moins, en raison des révélations de Snowden). C'est en effet l'un des rares ports dont on peut être sûr qu'il est à peu près tout le temps ouvert, même sur les réseaux les plus affreux genre hôtel ou aéroport. En prime, les communications sur ce port sont chiffrées avec TLS (RFC 5246). Si seul HTTP se servait de ce port, cela marcherait mais de plus en plus de protocoles applicatifs se bousculent pour l'utiliser. Comment leur permettre de partager le port 443 sans émeutes ? En ajoutant une extension à TLS qui permet d'indiquer le protocole applicatif qui va utiliser ce port, une fois la session TLS démarrée.

Cette extension avait surtout été conçue pour les besoins de HTTP/2 (autrefois nommée SPDY), version qui est loin d'être terminée. ALPN (Application-Layer Protocol Negotiation), objet de notre RFC, sert à indiquer la version de HTTP, et aussi désigner les services qui vont tourner au dessus de HTTP, comme WebRTC. Mais elle pourra servir à d'autres protocoles. Comme, par exemple, faire coexister IMAP et HTTP sur le même port. Vous allez peut-être me dire « mais pourquoi ne pas faire cela dans une couche entre TLS et l'application, de manière à éviter de complexifier TLS ? » et le RFC vous répond que le but est de performance (éviter les aller-retour entre client et serveur) et de souplesse (choisir un certificat différent selon le protocole d'application).

À noter qu'il s'agit d'une extension TLS : si on veut faire coexister sur un même port des applications TLS comme HTTP et non-TLS comme SSH, il faut une autre solution (par exemple sslh).

Comment fonctionne ALPN (section 3 de notre RFC) ? Le client envoie la liste des applications qu'il gère dans son message TLS ClientHello (section 7.4.1.2 du RFC 5246), le serveur en choisit une et la renvoie dans son ServerHello. Tout se fait donc dans la poignée de mains de TLS, sans imposer d'aller-retours supplémentaires. Si le client ne veut qu'une seule application, il envoie un message avec une liste ne comportant que cette application.

L'extension se nomme application_layer_protocol_negotiation et a le numéro 16 (désormais dans le registre IANA). Elle a un contenu (de type ProtocolNameList) qui contient la liste des protocoles applicatifs, en ordre de préférence décroissante. En termes TLS, cela s'écrit :


enum {
       application_layer_protocol_negotiation(16), (65535)
   } ExtensionType;

opaque ProtocolName<1..2^8-1>;

struct {
       ProtocolName protocol_name_list<2..2^16-1>
   } ProtocolNameList;

Les valeurs possibles pour les protocoles applicatifs sont dans un registre IANA. Des nouveaux protocoles pourront être ajoutés après un examen par un expert (RFC 5226, section 4.1). Pour l'instant, on y trouve le HTTP actuel, 1.1 (RFC 7230) et les variantes de SPDY. HTTP/2 sera placé dans ce registre après sa normalisation.

Cette extension sert aussi bien au client (liste des applications que le client veut utiliser) qu'au serveur (application - au singulier - choisie, après avoir ignoré celles que le serveur ne connait pas). Si le serveur ne met pas cette extension dans sa réponse, c'est ce qu'est un vieux serveur, qui ne la connait pas. S'il répond avec une alerte no_application_protocol, c'est que l'intersection de la liste du client avec celle configurée dans le serveur est hélas vide.

Si vous utilisez une application qui peut attirer l'attention de la NSA, attention : la négociation ALPN est en clair, avant que le chiffrement ne démarre (sections 4 et 5). C'était un des points les plus controversés lors des discussions à l'IETF mais cela a été décidé pour être cohérent avec le modèle d'extension de TLS. Si vous tenez à cacher le protocole applicatif utilisé, vous devez faire une renégociation une fois le chiffrement commencé.

Des mises en œuvre de cette extension ? C'est apparemment fait pour NSS (voir #996250 et #959664). Et Wireshark va pouvoir le décoder.


Téléchargez le RFC 7301


L'article seul

RFC 7290: Test Plan and Results for Advancing RFC 2680 on the Standards Track

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : L. Ciavattone (AT&T Labs), R. Geib (Deutsche Telekom), A. Morton (AT&T Labs), M. Wieser (Technical University Darmstadt)
Pour information
Réalisé dans le cadre du groupe de travail IETF ippm
Première rédaction de cet article le 23 juillet 2014


Le RFC 2680 normalise une métrique utile à l'analyse des performances des réseaux informatiques, le taux de perte de paquets (mesuré sur un aller simple). Pour faire avancer ce RFC sur le chemin des normes, pour qu'il accède au statut de « norme tout court », il faut une évaluation : la description du RFC est-elle claire et complète ? Les mises en œuvres logicielles sont-elles équivalentes ? C'est ce travail d'évaluation que ce nouveau RFC documente.

Évaluer la norme d'une métrique est un peu plus compliqué qu'évaluer un protocole. La méthode traditionnelle à l'IETF pour évaluer un protocole est de regarder si deux mises en œuvre différentes peuvent interopérer. Pour une métrique, ce n'est pas possible et on utilise donc une autre méthode, regarder si deux mises en œuvre différentes d'une métrique, confrontées au même trafic, produisent des résultats compatibles (pas forcément identiques : les mesures ne sont jamais parfaitement précises).

Le groupe de travail IPPM de l'IETF travaille depuis longtemps sur ce problème d'avancement des métriques sur le chemin des normes. Les principes de l'évaluation d'une métrique ont été décrits dans le RFC 6576 et ont déjà été mis en pratique dans le RFC 6808, pour la métrique « délai d'acheminement d'un paquet (en aller simple) ». L'idée de base est que, si deux mesures, par des mises en œuvre différentes, donnent des résultats suffisamment proches, c'est que la métrique était correctement définie, claire et sans ambiguité. Cette évaluation permet aussi de voir quelles options de la métrique n'ont jamais été implémentées, et sont donc candidates à être supprimées dans une future révision de la norme.

En suivant le même plan que le RFC 6808, voici donc l'évaluation du RFC 2680 en pratique. La configuration de test est décrite en section 3 : les deux mises en œuvre testées étaient un NetProbe (ce qu'utilise WIPM), utilisant des paquets UDP avec une distribution périodique (RFC 3432) ou de Poisson (RFC 2330), et un Perfas+ (description en allemand), également avec UDP. Dans les deux cas, on passait par un tunnel L2TP (RFC 3931), pour dissimuler aux équipements intermédiaires les adresses IP et ports des équipements de tests (autrement, le trafic risquerait de passer par des chemins différents pour les deux tests). À l'extrémité du tunnel, le trafic est renvoyé donc cela permet d'avoir l'émetteur et le récepteur au même endroit, tout en faisant une mesure unidirectionnelle sur une longue distance. Pour introduire des phénomènes rigolos comme des pertes de paquet, un émulateur Netem est installé sur une machine Fedora, qui assure la communication entre le tunnel et un VLAN où se trouve l'équipement de test, via un lien FastEthernet. Les équipements testés ont envoyé des flots à 1 et 10 p/s, avec des centaines de paquets, de tailles variables (de 64 à 500 octets). Le type-P (cf. RFC 2680, section 2.8.1) des paquets était « UDP avec DSCP best effort » (de toute façon, l'encapsulation dans le tunnel L2TP fait que ces bits DSCP ont probablement été ignorés).

La section 4 de notre RFC décrit l'étalonnage (vérifier les horloges, par exemple).

On l'a dit, c'est bien joli de comparer deux programmes mais que doit être le résultat de la comparaison ? Une égalité rigoureuse ? Ce n'est pas possible. La section 5 indique les critères de comparaison : on fait de l'ADK avec un facteur de confiance à 0,95 (et de l'adéquation à la loi de distribution).

Enfin, après toutes ces précautions méthodologiques, la section 6 contient les résultats. Par exemple, en périodique, les deux systèmes mesurés donnent des résultats largement compatibles mais, en distribution de Poisson, les résultats sont tous juste acceptables. Les mesures ont bien résisté à des tests comme celui du délai maximal d'attente (avant de déclarer un paquet perdu) ou comme celui du désordonnement des paquets (lorsqu'un paquet est tellement en retard qu'il arrive après un paquet parti après).

Conclusions (section 7) : les deux mises en œuvre sont compatibles, ce qui indique que le RFC 2680 est suffisamment clair et précis. Le processus de test a toutefois noté quelques points où le RFC 2680 pouvait être rendu plus clair (sans compter les erreurs connues du RFC).

Pour les fanas, la section 10 rassemble toutes les commandes de configuration des équipements qui ont été utilisées. Par exemple, Netem a été configuré avec tc (ici, pour ajouter un retard variable et en prime 10 % de perte de paquets) :

tc qdisc change dev eth1 root netem delay 2000ms 1000ms loss 10%

Téléchargez le RFC 7290


L'article seul

Un petit galop d'essai avec le système Haka (filtrage, sécurité, paquets, protocoles, etc)

Première rédaction de cet article le 20 juillet 2014


Le 10 juillet dernier, lors des RMLL à Montpellier, j'ai eu le plaisir de participer à l'« Atelier HAKA : un langage open source [sic] de sécurité réseau ». Haka est un langage permettant d'analyser les paquets, de réagir à certaines caractéristiques des paquets et de générer des réponses diverses. Cela permet entre autres de faire des pare-feux très souples car complètement programmables.

Haka est fondé sur Lua et il faut donc un peu réviser ses connaissances Lua avant de pratiquer l'atelier. (J'ai déjà écrit ici et sur Lua et réalisé un tout petit programme avec.) Haka étend Lua en ajoutant au langage de base des extensions permettant de tripoter les paquets réseau facilement. Je ne vais pas vous faire un cours sur Haka (il y en a un en ligne et les transparents de l'atelier sont disponibles en ligne), juste documenter mon expérience.

L'atelier se faisait dans une machine virtuelle Debian dans VirtualBox. La machine virtuelle utilisée peut être téléchargée en https://hakasecurity.files.wordpress.com/2014/07/haka-live-iso.zip. (Je n'ai pas encore essayé d'installer Haka moi-même.) Une fois la machine démarrée, il faut faire un setxkbmap fr dans un terminal pour passer en AZERTY, le clavier par défaut étant QWERTY. Si vous éditez les sources Lua avec Emacs, notez qu'il n'y a pas de mode Lua pour Emacs dans la machine virtuelle. Bon, on s'en passe, sinon, on le télécharge avec apt-get. Les pcap d'exemple sont livrés avec la machine virtuelle, dans /opt/haka/share/haka/sample/hellopacket.

Premier exercice, s'entraîner aux bases de Haka et apprendre à afficher certains paquets. Allons-y en Lua :

-- On charge le dissecteur IPv4 (pas encore de dissecteur IPv6,
-- malheureusement ; un volontaire pour l'écrire ?)
local ip = require('protocol/ipv4')

-- Haka fonctionne en écrivant des *règles* qui réagissent à des
-- *évènements*, ici, l'évènement est le passage d'un paquet IPv4
haka.rule{
    hook = ip.events.receive_packet,
    -- Suit une série d'actions, qui reçoivent un paramètre qui dépend
    -- du dissecteur. Le dissecteur IPv4 passe à l'évènement
    -- receive_packet un paquet.
    eval = function (pkt)
        haka.log("Hello", "packet from %s to %s", pkt.src, pkt.dst)
    end
}

Et comment on a trouvé que les champs de pkt qui contenaient les adresses IP source et destination se nommaient src et dst ? On a lu la doc (également disponible dans la machine virtuelle en /lib/live/mount/medium/haka/manual/modules/protocol/ipv4/doc/ipv4.html#dissector). Il existe aussi un mode interactif de Haka, permettant d'explorer les paquets, pas montré ici.

Si on lance ce script Haka sur un pcap existant, il affiche :

% hakapcap hellopacket.lua hellopcaket.pcap

info  core: load module 'packet/pcap.ho', Pcap Module
info  core: load module 'alert/file.ho', File alert
info  core: setting packet mode to pass-through

info  core: loading rule file 'hellopacket.lua'
info  core: initializing thread 0
info  dissector: register new dissector 'raw'
info  pcap:      opening file 'hellopacket.pcap'
info  dissector: register new dissector 'ipv4'
info  core:      1 rule(s) on event 'ipv4:receive_packet'
info  core:      1 rule(s) registered

info  core:      starting single threaded processing

info  Hello:     packet from 192.168.10.1 to 192.168.10.99
info  Hello:     packet from 192.168.10.99 to 192.168.10.1
info  Hello:     packet from 192.168.10.1 to 192.168.10.99
info  Hello:     packet from 192.168.10.1 to 192.168.10.99
info  Hello:     packet from 192.168.10.99 to 192.168.10.1
info  Hello:     packet from 192.168.10.1 to 192.168.10.99
info  Hello:     packet from 192.168.10.99 to 192.168.10.1
info  Hello:     packet from 192.168.10.1 to 192.168.10.99
info  core:      unload module 'Pcap Module'
info  core:      unload module 'File alert'

Ça a bien marché, chaque paquet du pcap a été affiché.

Deuxième exercice, filtrage des paquets qui ne nous plaisent pas, en l'occurrence, ceux qui viennent du méchant réseau 192.168.10.0/27 :

local ip = require('protocol/ipv4')

local bad_network = ip.network("192.168.10.0/27")

haka.rule{
    hook = ip.events.receive_packet,
    eval = function (pkt)
      -- On teste si le paquet appartient au méchant réseau
      if bad_network:contains(pkt.src) then
        -- Si oui, on le jette et on journalise
        haka.log("Dropped", "packet from %s to %s", pkt.src, pkt.dst)
	pkt:drop()
      -- Si non (pas de 'else') le paquet suit son cours
      end
   end
}

Une fois lancé le script sur un pcap, on obtient :

...
info  Dropped:   packet from 192.168.10.1 to 192.168.10.99
info  Dropped:   packet from 192.168.10.1 to 192.168.10.99
info  Dropped:   packet from 192.168.10.1 to 192.168.10.99
info  Dropped:   packet from 192.168.10.1 to 192.168.10.99
info  Dropped:   packet from 192.168.10.10 to 192.168.10.99
info  Dropped:   packet from 192.168.10.10 to 192.168.10.99
info  Dropped:   packet from 192.168.10.10 to 192.168.10.99

Si vous regardez le pcap avec tcpdump, vous verrez qu'il y a d'autres paquets, en provenance de 192.168.10.99 qui n'ont pas été jetés, car pas envoyés depuis le méchant réseau :

14:49:27.076486 IP 192.168.10.1 > 192.168.10.99: ICMP echo request, id 26102, seq 1, length 64
14:49:27.076536 IP 192.168.10.99 > 192.168.10.1: ICMP echo reply, id 26102, seq 1, length 64
14:49:28.075844 IP 192.168.10.1 > 192.168.10.99: ICMP echo request, id 26102, seq 2, length 64
14:49:28.075900 IP 192.168.10.99 > 192.168.10.1: ICMP echo reply, id 26102, seq 2, length 64
14:49:31.966286 IP 192.168.10.1.37542 > 192.168.10.99.80: Flags [S], seq 3827050607, win 14600, options [mss 1460,sackOK,TS val 2224051087 ecr 0,nop,wscale 7], length 0
14:49:31.966356 IP 192.168.10.99.80 > 192.168.10.1.37542: Flags [R.], seq 0, ack 3827050608, win 0, length 0
14:49:36.014035 IP 192.168.10.1.47617 > 192.168.10.99.31337: Flags [S], seq 1811320530, win 14600, options [mss 1460,sackOK,TS val 2224052099 ecr 0,nop,wscale 7], length 0
14:49:36.014080 IP 192.168.10.99.31337 > 192.168.10.1.47617: Flags [R.], seq 0, ack 1811320531, win 0, length 0
14:49:46.837316 ARP, Request who-has 192.168.10.99 tell 192.168.10.10, length 46
14:49:46.837370 ARP, Reply 192.168.10.99 is-at 52:54:00:1a:34:60, length 28
14:49:46.837888 IP 192.168.10.10.35321 > 192.168.10.99.8000: Flags [S], seq 895344097, win 14600, options [mss 1460,sackOK,TS val 2224054805 ecr 0,nop,wscale 7], length 0
14:49:46.837939 IP 192.168.10.99.8000 > 192.168.10.10.35321: Flags [R.], seq 0, ack 895344098, win 0, length 0
14:49:50.668580 IP 192.168.10.10 > 192.168.10.99: ICMP echo request, id 26107, seq 1, length 64
14:49:50.668674 IP 192.168.10.99 > 192.168.10.10: ICMP echo reply, id 26107, seq 1, length 64
14:49:51.670446 IP 192.168.10.10 > 192.168.10.99: ICMP echo request, id 26107, seq 2, length 64
14:49:51.670492 IP 192.168.10.99 > 192.168.10.10: ICMP echo reply, id 26107, seq 2, length 64
14:49:51.841297 ARP, Request who-has 192.168.10.10 tell 192.168.10.99, length 28
14:49:51.841915 ARP, Reply 192.168.10.10 is-at 52:54:00:30:b0:bd, length 46

Bon, jusqu'à présent, on n'a rien fait d'extraordinaire, Netfilter en aurait fait autant. Mais le prochain exercice est plus intéressant. On va faire du filtrage TCP. La documentation du dissecteur TCP nous apprend que le champ qui indique le port de destination est dstport :

local ip = require('protocol/ipv4')

-- On charge un nouveau dissecteur
local tcp = require ('protocol/tcp_connection')

haka.rule{
    -- Et on utilise un nouvel évènement, qui signale un nouveau flot TCP
    hook = tcp.events.new_connection,
    eval = function (flow, pkt)
      -- Si le port n'est ni 22, ni 80...
      if flow.dstport ~= 22 and flow.dstport ~= 80 then
        haka.log("Dropped", "flow from %s to %s:%s", pkt.ip.src, pkt.ip.dst, flow.dstport)
	flow:drop()
      else	
        haka.log("Accepted", "flow from %s to %s:%s", pkt.ip.src, pkt.ip.dst, flow.dstport)
      end
   end
}

Et on le teste sur un pcap :

info  Accepted:  flow from 192.168.10.1 to 192.168.10.99:80
info  Dropped:   flow from 192.168.10.1 to 192.168.10.99:31337
info  Dropped:   flow from 192.168.10.10 to 192.168.10.99:8000

Il y avait trois flots (trois connexions TCP) dans le pcap, une seule a été acceptée.

Maintenant, fini de jouer avec des pcap. Cela peut être intéressant (analyse de pcap compliqués en ayant toute la puissance d'un langage de Turing) mais je vous avais promis un pare-feu. On va donc filtrer des paquets et des flots vivants, en temps réel. On écrit d'abord le fichier de configuration du démon Haka :

[general]
# Select the haka configuration file to use
configuration = "tcpfilter.lua"

# Optionally select the number of thread to use. 
#thread = 4

# Pass-through mode
# If yes, haka will only inspect packet
# If no, it means that haka can also modify and create packet
pass-through = no

[packet]
# Select the capture model, nfqueue or pcap
module = "packet/nfqueue"

# Select the interfaces to listen to
interfaces = "lo"
#interfaces = "eth0"

# Select packet dumping for nfqueue
#dump = yes
#dump_input = "/tmp/input.pcap"
#dump_output = "/tmp/output.pcap"

[log]
# Select the log module
module = "log/syslog"

[alert]
# Select the alert module
module = "alert/syslog"

Le script est le même que dans l'essai précédent, il accepte uniquement les connexions TCP vers les ports 22 ou 80. On lance le démon (notez que celui-ci fera appel à Netfilter pour lui passer les paquets) :

% sudo haka -c haka.conf --no-daemon
info  core: load module 'log/syslog.ho', Syslog logger
info  core: load module 'alert/syslog.ho', Syslog alert
info  core: load module 'alert/file.ho', File alert
info  core: load module 'packet/nfqueue.ho', nfqueue
info  nfqueue: installing iptables rules for device(s) lo
info  core:    loading rule file 'tcpfilter.lua'
info  core:    initializing thread 0
info  dissector: register new dissector 'raw'
info  dissector: register new dissector 'ipv4'
info  dissector: register new dissector 'tcp'
info  dissector: register new dissector 'tcp_connection'
info  core:      1 rule(s) on event 'tcp_connection:new_connection'
info  core:      1 rule(s) registered

info  core:      starting single threaded processing

Et on tente quelques connexions (143 = IMAP) :

info  Accepted:  flow from 127.0.0.1 to 127.0.0.1:80
info  Dropped:   flow from 127.0.0.1 to 127.0.0.1:143
info  Dropped:   flow from 127.0.0.1 to 127.0.0.1:143
info  Dropped:   flow from 127.0.0.1 to 127.0.0.1:143

Et en effet, les clients IMAP vont désormais timeouter. Dès qu'on arrête le démon, avec un Control-C, IMAP remarche :

% telnet 127.0.0.1 imap2
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
* OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE STARTTLS AUTH=PLAIN] Dovecot ready.

J'ai beaucoup aimé la possibilité de journaliser ou de jeter un flot TCP entier, pas en agissant paquet par paquet. Pour la partie IP au début de cet atelier, Haka a un concurrent évident, Scapy (mêmes concepts mais Python au lieu de Lua). Mais Scapy ne sait pas gérer les flots TCP, il ne travaille que par paquet.

Exercice suivant, le protocole du Web, HTTP. On va modifier les pages HTML en vol (et vous comprendrez pourquoi il faut toujours utiliser HTTPS). Il faut changer haka.conf pour indiquer le nouveau script, blurring-the-web.lua. D'abord, on se contente d'afficher les requêtes HTTP :

local ip = require('protocol/ipv4')

-- On charge un nouveau dissecteur
local http = require ('protocol/http')

-- Pas d'indication du protocole supérieur dans TCP, seulement du
-- numéro de port, donc il faut être explicite
http.install_tcp_rule(80)

haka.rule{
    -- Et on utilise un nouvel évènement
    hook = http.events.request,
    eval = function (connection, request)
      haka.log("HTTP request", "%s %s", request.method, request.uri)
      -- Il faut être sûr que les données ne soient pas comprimées,
      -- pour que la modification ultérieure marche. On modifie donc
      -- la requête.
      request.headers['Accept-Encoding'] = nil
      request.headers['Accept'] = "*/*"
    end
}

Haka fait beaucoup de choses mais il ne décomprime pas les flots HTTP. Comme la compression est souvent utilisée sur le Web, on modifie les en-têtes de la requête pour prétendre qu'on n'accepte pas la compression (cf. RFC 7231, sections 5.3.4 et 5.3.2).

On lance maintenant le démon Haka avec ce nouveau script. Pour tester que les requêtes du navigateur Web ont bien été modifiées, on peut aller sur un site qui affiche les en-têtes de la requête comme http://www.bortzmeyer.org/apps/env. Par défaut, le Firefox de la machine virtuelle envoie :

HTTP_ACCEPT: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
HTTP_ACCEPT_ENCODING: gzip, deflate

alors qu'une fois le démon en route, on n'a plus que (mettre une entrée de tableau à nil la détruit) :

HTTP_ACCEPT: */*

Attention au passage : Firefox peut maintenir des connexions TCP persistantes avec le serveur HTTP. Si vous lancez Firefox avant Haka, vous risquez de récupérer fréquemment des :

alert: id = 8
	time = Sat Jul 19 16:31:32 2014
	severity = low
	description = no connection found for tcp packet
	sources = {
		address: 127.0.0.1
		service: tcp/56042
	}
	targets = {
		address: 127.0.0.1
		service: tcp/80
	}

C'est parce que Haka a vu passer des paquets d'une connexion TCP antérieure à son activation et qu'il ne sait donc pas rattacher à un flot qu'il suit. (C'est le problème de tous les filtres à état.)

Attention aussi, Haka ne gère pas IPv6. Si on se connecte à un serveur Web en indiquant son nom, on peut utiliser IPv6 (c'est le cas de http://localhost/ sur la Debian de la machine virtuelle) et Haka ne verra alors rien. D'où l'option -4 de wget, pour tester :

% wget -v -4 http://localhost/
...
# Haka affiche
info  HTTP request: GET /

Maintenant, on ne va pas se contenter d'observer, on modifie la réponse HTTP en ajoutant du CSS rigolo :


local ip = require('protocol/ipv4')
local http = require ('protocol/http')

local re = require('regexp/pcre')

-- Ce CSS rend tout flou
local css = '<style type="text/css" media="screen"> * { color: transparent !important; text-shadow: 0 0 3px black !important; } </style>'

http.install_tcp_rule(80)

-- Bien garder cette règle, pour couper la compression, qui est
-- activée par défaut sur beaucoup de serveurs
haka.rule{
    hook = http.events.request,
    eval = function (connection, request)
      haka.log("HTTP request", "%s %s", request.method, request.uri)
      request.headers['Accept-Encoding'] = nil
      request.headers['Accept'] = "*/*"
    end
}

-- Deuxième règle, pour changer la réponse
haka.rule{
    hook = http.events.response_data,
    options = {
        streamed = true,
    },
    eval = function (flow, iter)
      -- Chercher la fin de l'élément <head>
      local regexp = re.re:compile("</head>", re.re.CASE_INSENSITIVE)
      local result = regexp:match(iter, true)
      -- Si on a bien trouvé un <head>
      if result then 
         result:pos('begin'):insert(haka.vbuffer_from(css))
      end
    end
}

Et re-testons :


% wget -O - -v -4 http://localhost/
--2014-07-19 16:41:48--  http://localhost/
Resolving localhost (localhost)... 127.0.0.1, 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 190 [text/html]
Saving to: `STDOUT'

 0% [                                               ] 0           --.-K/s              <html><head><style type="text/css" media="screen"> * { color: transparent !important; text-shadow: 0 0 3px black !important; } </style></head><body><h1>It works!</h1>
100%[==============================================>] 190         --.-K/s   in 0s      

2014-07-19 16:41:48 (45.0 MB/s) - written to stdout [190/190]

Le CSS a bien été inséré ! Et, avec un vrai site Web, on voit bien l'effet :

Attention, le haka.conf indiqué ici n'écoute que sur l'interface lo. Pour tester sur le Web en grand, pensez à changer le haka.conf (et rappelez-vous que Haka n'aura aucun effet sur les sites Web accessibles en IPv6 comme http://www.ietf.org/, si la machine virtuelle a une connectivité IPv6).

Voilà, sur ce truc spectaculaire, ce fut la fin de l'atelier. À noter que nous n'avons pas testé les performances de Haka, question évidemment cruciale pour des filtrages en temps réel. En première approximation, Haka semble bien plus rapide que Scapy pour les tâches d'analyse de pcap mais il faudra un jour mesurer sérieusement.


L'article seul

RFC 7314: Extension Mechanisms for DNS (EDNS) EXPIRE Option

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : M. Andrews (ISC)
Expérimental
Première rédaction de cet article le 18 juillet 2014


L'enregistrement DNS SOA (Start Of Authority) a un champ expire qui indique au bout de combien de temps un serveur esclave qui ne réussit pas à contacter le maître peut arrêter de servir la zone. Si l'esclave a eu les données, non pas directement du maître, mais d'un autre esclave, la durée d'expiration peut ne pas être correcte. C'est ce (rare) problème que règle l'option EDNS EXPIRE.

En effet, il faut se rappeler que la distribution d'une zone DNS depuis un serveur maître (le RFC utilise l'ancien terme de « serveur primaire ») n'est pas forcément directe. On peut avoir un maître qui envoie des données à un esclave, qui à son tour les envoie à un autre esclave. Cela permet notamment davantage de robustesse (si le maître n'est temporairement pas joignable depuis certains esclaves). Notez que l'esclave ne sait même pas si la machine qui lui a envoyé les données était un maître ou pas.

Si la période de validité donnée par le champ expire du SOA (RFC 1035, section 3.3.13) vaut V et que le transfert de l'esclave à un autre esclave prend place S secondes après le transfert depuis le maître (les transferts ne sont pas forcément instantanés, par exemple en cas de coupure réseau), la zone sera servie pendant V + S secondes et pas seulement pendant V secondes. Pire, s'il existe une boucle dans le graphe de distribution, la zone risque de ne jamais expirer, les esclaves se rafraichissant mutuellement.

Personnellement, le problème me semble rare et de peu d'importance. Il y a des tas d'autres choses plus urgentes à régler dans le DNS. Mais, bon, c'est juste une expérience.

La nouvelle option de ce RFC dépend de EDNS (RFC 6891). Elle a le numéro 9. Mise dans une requête (a priori une requête de type SOA, AXFR ou IXFR), elle indique que le client DNS connait cette option et voudrait sa valeur. Un serveur qui ne connait pas l'option EXPIRE ne mettra pas l'option dans sa réponse.

Par contre, un serveur qui connait cette option répondra avec une option EXPIRE dans sa réponse. Elle comporte quatre octets de données (comme pour le champ expire du SOA), indiquant le nombre de secondes de la période de validité. Si le serveur est un maître, il met toujours dans cette option la valeur du champ expire du SOA. S'il est un esclave, il met la valeur actuelle de son compteur d'expiration. Ainsi, si le champ expire du SOA vaut 7 200 secondes (2 heures) et que le serveur secondaire reçoit une demande 30 minutes après le transfert depuis le maître, il mettra 5 400 dans l'option EDNS EXPIRE (120 minutes de validité moins les 30 minutes écoulées). C'est ainsi qu'on évite l'accumulation des périodes de validité en cas de transfert indirect.

Un serveur esclave (le RFC utilise l'ancien terme de « serveur secondaire ») qui utilise l'option, et qui reçoit une réponse lors d'un transfert initial de la zone, devrait utiliser comme durée de validité la valeur de l'option EXPIRE (et pas le champ expire du SOA, sauf si ce champ a une valeur inférieure à celle de l'option EXPIRE). Cette durée de validité est ensuite mise dans un compteur d'expiration qui décroît avec le temps. Pour les rafraichissements ultérieurs, le serveur esclave doit également utiliser comme durée de validité la valeur de l'option, sauf si le compteur actuel a une valeur plus élevée. Par exemple, si le compteur du secondaire dit que la zone est encore valable pour 4 500 secondes, et qu'une réponse IXFR (RFC 1995) contient une option EXPIRE de valeur 9 300 secondes, alors le compteur est mis à 9 300 secondes. Si l'option EXPIRE avait valu 2 400 secondes, le compteur n'aurait pas été modifié.

Une conséquence amusante de cette option est qu'elle permet de savoir quand a eu lieu le dernier transfert de zone réussi, juste en interrogeant un esclave. Cela peut être vu comme indiscret, mais cela peut aussi être un outil de supervision très pratique.

Cette option est utilisable depuis dig, à partir de la version 9.10. Wireshark a récemment été modifié pour reconnaître cette option.


Téléchargez le RFC 7314


L'article seul

RFC 7291: DHCP Options for the Port Control Protocol (PCP)

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : M. Boucadair (France Telecom), R. Penno, D. Wing (Cisco)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF pcp
Première rédaction de cet article le 15 juillet 2014


Le protocole PCP, normalisé dans le RFC 6887 permet à la machine de M. Michu de configurer un routeur/traducteur, par exemple pour lui demander de laisser passer le trafic entrant vers un port donné. La norme PCP ne précisait pas tellement comment le client était censé trouver le serveur PCP. Dans certains cas, c'est trivial, c'est le routeur par défaut. Dans d'autres, il faut d'autres mécanismes et c'est là que notre RFC 7291 est utile : il normalise un moyen de découvrir le serveur PCP grâce à une nouvelle option DHCP.

Le mécanisme est le même pour DHCP IPv6 (RFC 3315) ou DHCP IPv4 (RFC 2131). Pour DHCP IPv6, l'option est décrite en section 3. Elle est nommée OPTION_V6_PCP_SERVER et a le code 86 (mis dans le registre IANA). Suit une liste d'adresses IPv6, qui sont celles d'un serveur PCP. S'il y a plusieurs serveurs PCP, on met plusieurs occurrences de l'option OPTION_V6_PCP_SERVER.

On note que c'est une adresse IP et pas un nom de domaine qui est utilisée pour identifier le ou les serveur(s) PCP. Un nom de domaine aurait permis un niveau d'indirection supplémentaire. Le sujet a donc fait l'objet d'une certaine controverse au sein du groupe de travail PCP à l'IETF avant que le consensus se fasse sur l'adresse IP.

Pour obtenir cette liste d'adresses, le client DHCP doit mettre l'option OPTION_V6_PCP_SERVER dans sa requête et, si le serveur DHCP connait des serveurs PCP, il mettra leur(s) adresse(s) dans sa réponse.

Le mécanisme est quasiment le même en IPv4 (section 4), avec l'option OPTION_V4_PCP_SERVER de code 158 (registre IANA). La principale différence est le mécanisme utilisé pour mettre plusieurs serveurs PCP (pas juste plusieurs adresses IP pour un serveur, mais plusieurs serveurs) : en IPv6, on répétait l'option. En IPv4, on envoie plusieurs listes dans la même réponse.

Donc, attention, aussi bien côté client que côté serveur, ce RFC permet d'avoir plusieurs serveurs PCP, ayant chacun plusieurs adresses IP. Par exemple, côté serveur, le mécanisme de configuration du serveur (mettons son fichier de configuration) doit permettre de distinguer les deux cas, afin de choisir le bon encodage. Si le fichier de configuration permet de mettre un nom de domaine pour le serveur PCP, et que le serveur DHCP, en résolvant ce nom, trouve plusieurs adresses IP, il doit bien les mettre dans une seule option (en IPv6) ou une seule liste (IPv4).

Notez une différence entre IPv6 et IPv4 : un serveur DHCP IPv6 peut retourner des adresses IPv4 (encodées selon la section 2.5.5.2 du RFC 4291) dans sa réponse (cas d'un réseau purement IPv6 mais qui reçoit une connectivité IPv4 via un mécanisme spécifique), l'inverse n'étant pas vrai.

À noter que, dans le cas de multi-homing, on peut recevoir plusieurs serveurs PCP via des réponses DHCP différentes, sur des liens réseau différents (par exemple un lien WiFi et un 3G). Dans ce cas, la machine doit bien faire attention à associer chaque serveur PCP à son réseau, pour ne configurer que le bon serveur PCP correspondant à l'adresse qu'on veut utiliser.

Les praticiens noteront avec plaisir qu'il existe au moins une mise en œuvre documentée (sur Fedora).


Téléchargez le RFC 7291


L'article seul

RFC 7320: URI Design and Ownership

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : M. Nottingham
Réalisé dans le cadre du groupe de travail IETF appsawg
Première rédaction de cet article le 12 juillet 2014


Ah, les URI... Comme ces identificateurs sont très souvent vus et manipulés par un grand nombre d'utilisateurs, ils suscitent forcément des passions et des discussions sans fin. Ce RFC de bonne pratique s'attaque à un problème fréquent : les applications ou extensions qui imposent des contraintes sur l'URI, sans que cela soit justifié par le format de ces URI. Par exemple, on voit des CMS imposer, au moment de l'installation, que le CMS soit accessible par un URI commençant par le nom du logiciel. S'il s'appelle Foobar, on voit parfois des logiciels qui ne marchent que si l'URI commence par http://www.example.org/Foobar/. Pourquoi est-ce une mauvaise idée et que faudrait-il faire à la place ?

D'abord, l'argument d'autorité qui tue : la norme des URI, le RFC 3986, dit clairement que la structure d'un URI est déterminé par son plan (scheme en anglais) et que donc l'application n'a pas le droit d'imposer des règles supplémentaires. Les deux seuls « propriétaires » sont la norme décrivant le plan (qui impose une syntaxe, et certains éléments, par exemple le nom du serveur avec le plan http://) et l'organisation qui contrôle cet URI particulier (par exemple, toujours pour http://, l'organisation qui contrôle le domaine dans l'URI). Imposer des règles, pour une application ou une extension, c'est violer ce principe de propriété. (Il est formalisé dans une recommandation du W3C, section 2.2.2.1.)

Un exemple de structure dans les URI est l'interprétation du chemin (path en anglais) comme étant un endroit du système de fichiers. Ainsi, bien des serveurs HTTP, en voyant l'URI http://www.example.org/foo/bar/toto.html, chercheront le fichier en $DOCUMENT_ROOT/foo/bar/toto.html. C'est une particularité de la mise en œuvre de ce serveur, pas une obligation du plan d'URI http://. Un autre exemple est l'utilisation de l'extension du nom de fichier comme moyen de trouver le type de média de la ressource. (« Ça se termine en .png ? On envoie le type image/png. ») Ces deux cas ne violent pas forcément le principe de propriété des URI car l'utilisateur, après tout, choisit son serveur.

Mais, dans certains cas, l'imposition d'une structure (autre que celle déjà imposée par la norme du plan d'URI) a des conséquences néfastes :

  • Risque de collisions entre deux conventions différentes.
  • Risque d'instabilité si on met trop d'informations dans l'URI (voir la section 3.5.1 du document « Architecture of the World Wide Web, Volume One », cité plus haut). Pour reprendre l'exemple de l'extension du fichier, si on change le format de l'image de PNG en JPEG, l'URI changera, ce qui est néfaste.
  • Rigidité accrue. Si un logiciel impose d'être installé en /Foobar comme dans l'exemple au début, il contraint les choix pour l'administrateur système (et si je veux des URI avec un chemin vide, genre http://foobar.example.org/, je fais quoi ?)
  • Risque que le client fasse des suppositions injustifiées. Si une spécification décrit le paramètre sig comme étant forcément une signature cryptographique, il y a une possibilité qu'un logiciel client croit, dès qu'il voit un paramètre de ce nom, que c'est une signature.

Donc, pour toutes ces raisons, notre RFC déconseille fortement de rajouter des règles de structure dans les URI. Ces règles diminuent la liberté du propriétaire de l'URI.

Qui risque de violer ce principe ? Les auteurs d'applications, comme dans l'exemple Foobar plus haut, mais aussi des gens qui font des extensions aux URI, par le biais de nouvelles spécifications (par exemple pour mettre des signatures ou autres métadonnées dans l'URI). En revanche, ce principe ne s'applique pas au propriétaire lui-même, qui a évidemment le droit de définir ses règles pour la gestion de ses URI (exemple : le webmestre qui crée un schéma de nommage des URI de son site Web). Et cela ne s'applique pas non plus au cas où le propriétaire de l'URI reçoit lui-même une délégation pour gérer ce site (par exemple, un RFC qui crée un registre IANA et spécifie la structure des URI sous http://www.iana.org/ est dans son droit, l'IANA agissant sur délégation de l'IETF). Le principe « bas les pattes » de ce RFC n'est pas technique : on n'interdit pas de mettre de la structure dans les URI, on dit juste qui a le droit de le faire.

Notre RFC reconnait que certaines normes IETF (non citées...) violent ce principe, et appelle à profiter de la prochaine révision pour les corriger.

La partie normative de notre RFC est la section 2. Elle explicite le principe « bas les pattes » (« get off my lawn » dans le nom original du document en anglais...) D'abord, éviter de contraindre l'usage d'un plan particulier. Par exemple, imposer http:// peut être trop contraignant, certaines applications ou extensions pourraient marcher avec d'autres plans d'URI comme par exemple file://.

D'autre part, si on veut une structure dans les URI d'un plan particulier, cela doit être spécifié dans le document qui définit le plan (RFC 7230 pour http://, RFC 6920 pour ni:, etc) pas dans une extension faite par d'autres (« touche pas à mon plan »).

Les URI comprennent un champ « autorité » juste après le plan. Les extensions ou applications ne doivent pas imposer de contraintes particulières sur ce champ. Ainsi, pour http://, l'autorité est un nom de domaine et notre RFC ne permet pas qu'on lui mette des contraintes (du genre « le premier composant du nom de domaine doit commencer par foobar-, comme dans foobar-www.example.org »).

Même chose pour le champ « chemin », qui vient après l'autorité. Pas question de lui ajouter des contraintes (comme dans l'exemple du CMS qui imposerait /Foobar comme préfixe d'installation). La seule exception est la définition des URI bien connus du RFC 5785. Le RFC 6415 donne un exemple d'URI bien connus, avec une structure imposée.

Autre conséquence du principe « bas les pattes » et qui est, il me semble, plus souvent violée en pratique, le champ « requête » (query). Optionnel, il se trouve après le point d'interrogation dans l'URI. Notre RFC interdit aux applications d'imposer l'usage des requêtes, car cela empêcherait le déploiement de l'application dans d'autres contextes où, par exemple, on veut utiliser des URI sans requête (je dois dire que mes propres applications Web violent souvent ce principe). Quant aux extensions, elles ne doivent pas contraindre le format des requêtes. L'exemple cité plus haut, d'une extension hypothétique, qui fonctionnerait par l'ajout d'un paramètre sig aux requêtes pour indiquer une signature est donc une mauvaise idée. Une telle extension causerait des collisions (applications ou autres extensions qui voudraient un paramètre de requête nommé sig) et des risques de suppositions injustifié (un logiciel qui se dirait « tiens, un paramètre sig, je vais vérifier la signature, ah, elle est invalide, cet URI est erroné »). Au passage, les préfixes n'aident pas. Supposons qu'une extension, voulant limiter le risque de collisions, décide que tous les paramètres qu'elle définit commencent par myapp_ (donc, la signature serait myapp_sig). Cela ne supprime pas le risque de collisions puisque le préfixe lui-même ne serait pas enregistré.

Sauf erreur, Dotclear gère bien cela, en permettant, via les « méthodes de lecture » PATH_INFO ou QUERY_STRING d'avoir les deux types d'URI (sans requête ou bien avec).

Pourtant, HTML lui-même fait cela, dans la norme 4.01, en restreignant la syntaxe lors de la soumission d'un formulaire. Mais c'était une mauvaise idée et les futures normes ne devraient pas l'imiter.

Comme précédemment, les URI bien connus ont, eux, droit à changer la syntaxe ou contraindre les paramètres puisque, d'une certaine façon, l'espace sous .well-known est délégué à l'IETF et n'est plus « propriété » de l'autorité.

Dernier champ de l'URI à étudier, l'identificateur de fragment (ce qui est après le croisillon). Les définitions d'un type de média (RFC 6838) ont le droit de spécifier la syntaxe d'un identificateur de fragment spécifique à ce type de média (comme ceux du texte brut, dans le RFC 5147). Les autres extensions doivent s'en abstenir.

Bon, assez de négativité et d'interdiction. Après les « faites pas ci » et les « faites pas ça », la section 3 de notre RFC expose les alternatives, les bonnes pratiques pour remplacer celles qui sont interdites ici. D'abord, si le but est de faire des liens, il faut se rappeler qu'il existe un cadre complet pour cela, décrit dans le RFC 5988. Une application peut utiliser ce cadre pour donner la sémantique qu'elle veut à des liens. Autre technique rigolote et peu connue, les gabarits du RFC 6570, qui permettent de gérer facilement le cas de données spécifiques à l'application dans un URI.

Et, comme cité plusieurs fois, les URI bien connus du RFC 5785 sont un moyen de déclarer sa propre structure sur des URI. Par contre, ils sont censés avoir un usage limité (accéder à des métadonnées avant de récupérer une ressource) et ne sont pas un moyen générique d'échapper aux règles qui nous dérangent !


Téléchargez le RFC 7320


L'article seul

RFC 7300: Reservation of Last Autonomous System (AS) Numbers

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : J. Haas (Juniper Networks), J. Mitchell (Microsoft Corporation)
Réalisé dans le cadre du groupe de travail IETF idr
Première rédaction de cet article le 11 juillet 2014


Ce très court RFC réserve formellement deux numéros d'AS, 65535 et 4294967295, qui ne doivent donc pas être alloués à un opérateur réseau.

Ces deux AS étaient déjà réservés en pratique mais cela n'avait pas été documenté (au sens du RFC 6996). Ils sont les derniers de leur plage (65535 est le dernier des AS traditionnels sur 16 bits et 4294967295 le dernier des AS sur 32 bits du RFC 6793), d'où le titre de ce RFC.

Pourquoi les réserver ? Pour 65535, c'est parce qu'il est déjà utilisé dans des communautés BGP bien connues (RFC 1997 et registre IANA, c'est le cas, par exemple, de NO_EXPORT, alias 65535:65281). Par contre, 4294967295 n'a pas de telles communautés mais il est réservé au cas où on ait besoin d'un AS qui ne soit jamais annoncé en BGP.

Donc, ces deux AS ne doivent pas être utilisés sur l'Internet et ne seront donc jamais (si tout le monde respecte les règles) dans un attribut BGP comme AS_PATH. Mais ils peuvent servir à des usages pas encore définis et les programmes qui mettent en œuvre BGP ne doivent donc pas interdire leur usage. Ils sont désormais dans le registre IANA des AS et dans celui des AS spéciaux.


Téléchargez le RFC 7300


L'article seul

RFC 7303: XML Media Types

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : H. Thompson (University of Edinburgh), C. Lilley (W3C)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF appsawg
Première rédaction de cet article le 11 juillet 2014


Voici la nouvelle norme décrivant les types MIME XML, comme application/xml. Elle remplace le RFC 3023. Cette norme décrit aussi l'utilisation de +xml comme suffixe pour des formats de données fondés sur XML comme TEI, avec son application/tei+xml.

XML est une norme du W3C et n'est donc pas dans un RFC mais dans un document du W3C. Lorsque des documents sont envoyés sur l'Internet, ils sont souvent étiquetés avec un type de média, dit aussi, pour des raisons historiques, type MIME. Pour XML, il existe cinq types, application/xml, text/xml, application/xml-external-parsed-entity, text/xml-external-parsed-entity et application/xml-dtd. Il existe aussi une convention, formalisée dans le RFC 6838, pour les formats bâtis au-dessus de XML, comme Atom (RFC 4287). On écrit les types de média pour ces formats avec un +xml à la fin, indiquant ainsi qu'un processeur XML généraliste pourra toujours en faire quelque chose, même s'il ne connait pas ce format spécifique. Ainsi, Atom est application/atom+xml. (Ces suffixes sont désormais banals mais, à l'époque du RFC 3023, ils étaient contestés et l'annexe A du RFC 3023 contient une justification détaillée, qui n'a pas été reprise dans ce nouveau RFC 7303.)

Pour normaliser quelque chose à propos de XML, il faut d'abord se pencher sur les problèmes d'encodage de caractères. Le modèle de caractères de XML est forcément Unicode.Les encodages de documents peuvent être très divers, mais la norme Unicode n'en définit que trois, UTF-8, UTF-16 et UTF-32. UTF-8 (RFC 3629) représente les caractères par une suite d'octets de longueur variable. Il n'a qu'une seule sérialisation en octets possible. UTF-16 en a deux, car il représente les caractères (enfin, ceux du PMB) par des seizets. Selon qu'on mette l'octet de poids fort du seizet en premier ou en dernier, on parle d'UTF-16 gros-boutien (utf-16be dans une déclaration XML) ou d'UTF-16 petit-boutien (utf-16le). Quant à UTF-32, qui est le plus simple des encodages, car le plus uniforme (tout caractère Unicode est représenté par quatre octets, codant son point de code), il a quatre sérialisations possibles en théorie mais deux seulement sont définies, UTF-32BE, gros-boutien, et UTF32-LE, petit-boutien. UTF-32 est malheureusement peu déployé (qu'on ne me dise pas que c'est parce qu'il prend plus de place : quatre octets par caractère, à l'époque où on s'échange des vidéos HD, ce n'est rien). Ce RFC déconseille désormais son usage. (Une bonne discussion des problèmes avec UTF-32 est dans ce rapport de bogue chez Mozilla.)

La section 3 du RFC décrit toutes les recommandations actuelles pour l'encodage des documents XML envoyés sur l'Internet. UTF-8 est recommandé (et sans BOM : il faut le retirer si, par exemple, on convertit de l'UTF-16 en UTF-8). Les producteurs/envoyeurs de XML devraient mettre un paramètre charset (pour les protocoles qui ont ce paramètre) et utiliser la déclaration XML (le truc qui commence par <?xml version="1.0" encoding=...) lorsqu'ils utilisent autre chose qu'UTF-8.

Cela suppose qu'ils sachent exactement quel est l'encodage. Si on envoie de l'XML et qu'on ne peut pas déterminer son encodage, il vaut mieux ne rien dire que de raconter n'importe quoi. Le RFC note que c'est particulièrement important pour les serveurs Web : avoir un paramètre global pour tout le site qui indique un encodage est dangereux, à moins qu'on puisse être absolument sûr que tous les documents du site auront cet encodage. Le RFC recommande que cet étiquetage soit configurable par l'utilisateur, pour qu'il puisse l'adapter à ses fichiers (AddDefaultCharset et AddCharset dans Apache.)

Quant aux consommateurs de documents XML, ils doivent considérer le BOM comme faisant autorité, et utiliser le paramètre charset si le BOM est absent. Les consommateurs qui comprennent le XML peuvent, s'il n'y a ni BOM, ni charset, utiliser les techniques purement XML, exposées dans la norme de ce format (en gros, utiliser la déclaration par exemple <?xml version="1.0" encoding="ISO 8859-1"?>. Il y a plein d'exemples en section 8 du RFC. Par exemple, ce cas-ci :


Content-Type: application/xml; charset=utf-8
...
<?xml version="1.0" encoding="utf-8"?>

est trivial : tout le monde est d'accord, le document est en UTF-8 et c'est explicite. En revanche, ce cas :


Content-Type: application/xml; charset=iso-8859-1
...
<?xml version="1.0" encoding="utf-8"?>

est pathologique : le type MIME dans l'en-tête HTTP et la déclaration XML se contredisent. Ici, un logiciel respectueux des normes doit traiter le document comme de l'ISO 8859-1, le type MIME ayant priorité (il n'y a pas de BOM, qui aurait une priorité supérieure). Mais, évidemment, l'envoyeur n'aurait pas dû générer ces informations incohérentes. Il vaut mieux ne pas envoyer de charset que d'envoyer un incorrect (voir aussi annexe C.2). Ainsi, dans ce troisième exemple :


Content-Type: application/xml
...
<?xml version="1.0"?>

il n'y a rien d'explicite : pas de BOM, pas de charset dans le type MIME et pas d'encodage indiqué dans la déclaration XML. Dans ce cas, puisque MIME ne dit rien, on applique la règle XML : l'encodage par défaut en XML est UTF-8.

À noter que le transport le plus utilisé pour XML, HTTP, avait un encodage de caractères par défaut, ISO 8859-1 mais ce n'est plus le cas depuis le RFC 7230. Voir aussi le RFC 6657 pour l'encodage par défaut des types text/*.

Assez fait d'encodage, place aux types MIME, sujet principal de ce RFC. La section 4 donne leur liste :

  • Les documents XML eux-mêmes auront le type application/xml ou text/xml. Le RFC ne tranche pas entre les deux possibilités, alors que ce choix a fait se déplacer beaucoup d'électrons, lors des innombrables discussions sur le « meilleur » type. (Le prédécesseur, le RFC 3023 recommandait text/xml.)
  • Les DTD auront le type application/xml-dtd.
  • Les « entités externes analysées » (external parsed entities), sont de type application/xml-external-parsed-entity ou text/xml-external-parsed-entity. Si elles sont par ailleurs des documents XML bien formés (ce qui n'est pas toujours le cas), elles peuvent avoir les types application/xml ou text/xml.
  • Les paramètres externes (external parameter entities) ont le même type que les DTD, application/xml-dtd.

Ils sont tous mis dans le registre IANA (cf. section 9 du RFC).

Il y a aussi le suffixe +xml (section 4.2), utilisant la notion de suffixe du RFC 6839. Il a vocation à être utilisé pour tous les formats XML. Si les concepteurs de ce format ne veulent pas d'un traitement générique XML (un cas probablement rare), ils doivent choisir un type MIME sans ce suffixe.

À noter que, si on envoie du XML en HTTP, le système de négociation de contenu de HTTP ne prévoit pas de mécanisme pour dire « j'accepte le XML, quel que soit le format ». Pas question de dire Accept: text/*+xml, donc.

La section 5 décrit ensuite le cas des identificateurs de fragment (le texte après le # dans un URI, pour désigner une partie d'un document). Ils sont décrits dans la section 3.5 du RFC 3986. Pour le cas particulier de XML, on se sert de la syntaxe XPointer pour écrire ces identificateurs (pas exemple, http://www.example.org/data.html#xpointer(/foo/bar)).

Le document XML envoyé peut utiliser l'attribut xml:base (section 6) pour spécifier un URI de référence, utilisé pour construire des URI absolus à partir des relatifs qu'on peut trouver dans le document XML (par exemple, lors de l'inclusion d'un autre document XML).

Et les versions de XML (il existe actuellement deux versions normalisées, 1.0 et 1.1) ? Voyez la section 7, qui précise que les types MIME sont exactement les mêmes pour toutes les versions : un analyseur XML doit utiliser les techniques XML (le champ version dans la déclaration) pour identifier les versions, pas les types MIME.

Enfin, si vous vous intéressez à la sécurité, voyez la section 10 qui résume rapidement les grandes questions de sécurité de XML. D'abord, les documents XML étant souvent modulaires (un document déclenche le chargement de paramètres ou d'entités XML externes, ou encore de feuilles de style), la sécurité doit prendre en compte tous les documents extérieurs et leur technique de chargement. Si un document XML est chargé en HTTPS mais qu'il contient une référence HTTP à une feuille de style CSS, la sécurité de HTTPS ne protégera pas cette dernière, alors qu'elle pourrait, par exemple, faire disparaître certains des éléments XML (display: none;...) Ainsi, beaucoup de documents XML se réfèrent à des entités XML qui sont sur les serveurs du W3C, sans se préoccuper d'analyser la sécurité desdits serveurs. Si la définition de l'entité mdash est remplacée par le texte de Winnie l'ourson, de drôles de résultats peuvent survenir.

Il y a aussi des attaques par déni de service : un document XML qui contient une référence à une entité qui contient à son tour de nombreuses autres références, qui contiennent chacune de nombreuses autres références, et ainsi de suite, jusqu'au dépassement de pile. (La forme la plus triviale de l'attaque, où l'entité se référence elle-même, est normalement empêchée par la prohibition de ces auto-références.)

Également d'un grand intérêt pratique, l'annexe C, sur les questions opérationnelles. La situation actuelle, notamment pour trouver l'encodage des caractères d'un document, n'est pas idéale. La nouvelle règle sur les BOM (priorité sur le paramètre charset) casse, en théorie, la compatibilité mais, en pratique, n'aggrave pas la situation et devrait l'améliorer à terme. Si on veut se simplifier la vie lors de l'envoi de XML, on le met en UTF-8 : c'est l'encodage recommandé par défaut, et il se passe de BOM. Si on n'utilise pas UTF-8, alors, il faut mettre un BOM.

Et le consommateur de XML ? Il lui suffit de regarder le BOM sinon le charset sinon la déclaration XML. En suivant ces règles simples, tout le monde devrait être heureux.

Depuis le RFC 3023, la précédente norme, que s'est-il passé ? L'annexe D résume les nombreux et sérieux changements. Les sous-types de text comme text/xml ont vu leur définition alignée avec celle du sous-type d'application. text/xml est donc désormais un simple synonyme de application/xml, ce qui reflète la réalité des mises en œuvre logicielles de XML. Le BOM est désormais prioritaire sur le charset. D'autre part, XPointer est désormais intégré. Et UTF-32 est maintenant officiellement déconseillé, au nom de l'interopérabilité.


Téléchargez le RFC 7303


L'article seul

RFC 7304: A method for mitigating namespace collisions

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : W. Kumari (Google)
Pour information
Première rédaction de cet article le 7 juillet 2014


Il arrive parfois, lors de l'utilisation d'un nom de domaine, qu'il y ait plusieurs noms qui correspondent à l'intention exprimée par l'utilisateur humain (dans les milieux ICANN, c'est souvent appelé une collision, terme brutal conçu pour faire peur). Par exemple, si l'utilisateur dans une entreprise example.com tape « www.foobar » dans son navigateur Web, peut-être comptait-il aller en http://www.foobar.example.com/ en comptant sur le fait que le logiciel complètera (ce qu'on nomme une search list) mais peut-être aussi voulait-il aller en http://www.foobar/ (le TLD .foobar n'existe pas aujourd'hui mais, au rythme où des TLD sont créés, cela pourrait arriver un jour). Dans ces conditions, comment faire pour désambiguer ? Ce très court RFC décrit une méthode... et explique pourquoi elle est fortement déconseillée.

Ce problème n'est pas spécifique aux search lists. On peut aussi l'avoir lorsque plusieurs espaces de nommage sont utilisés, par exemple en cas de « racines alternatives ». Les partisans desdites racines écartent souvent le problème de « collision » entre deux racines en disant « il suffira de demander à l'utilisateur son choix ». Pourquoi est-ce une mauvaise idée (mon intention était d'écrire « une idée idiote » mais le RFC dont je rends compte est plus prudent) ?

La section 2 de notre RFC décrit la méthode et ses défauts : si le nom n'a qu'une signification possible, on y va, sinon on présente à l'utilisateur une liste des possibilités « vouliez-vous aller en http://www.foobar.example.com/ ou en http://www.foobar/ ? » et on lui demande de choisir. On peut mémoriser ce choix, pour éviter de demander trop souvent à l'utilisateur. Mes lecteurs techniques voient sans doute immédiatement pourquoi cela ne peut pas marcher, mais ce RFC est conçu à des fins pédagogiques, pour tordre le cou une bonne fois pour toutes à cette fausse bonne idée, qui resurgit de temps en temps.

Quels sont les problèmes avec cette approche (si vous êtes enseignant en informatique, vous pouvez faire de cette question un sujet d'interrogation, pour voir si les étudiants ont bien compris les réseaux) ?

  • Il n'y a pas toujours un humain disponible devant l'écran. Il y a des serveurs fonctionnant automatiquement, et des applications qui s'exécutent en arrière-plan (comme les mises à jour périodiques).
  • L'humain peut ne pas savoir, notamment si le choix découle d'une tentative de hameçonnage ou autre tentative délibérée de l'induire en erreur.
  • Le délai supplémentaire pendant lequel l'utilisateur réfléchit peut conduire l'application à renoncer.
  • Ces demandes perpétuelles peuvent être pénibles pour l'utilisateur (songez qu'une seule page Web peut comporter des dizaines de noms de domaine).
  • Mémoriser les choix résoudrait partiellement le problème précédent, mais créerait d'autres ennuis, par exemple en cas de déplacement (si l'utilisateur se servait d'un portable et qu'il quitte les locaux de example.com).

Bref, c'est une mauvaise solution et notre RFC la déconseille. Par contre, il ne propose pas d'alternative (il n'y en a pas de sérieuse).

Pous les informaticiens, notez que, question mise en œuvre technique d'une telle solution, on aurait plusieurs possibilités : intercepter les requêtes de résolution de noms dans un shim, une mince couche logicielle entre l'application et la bibliothèque de résolution de noms (c'est ainsi que sont souvent mis en œuvre des TLD « alternatifs » comme .bit ou .onion) ou bien en remplaçant complètement la bibliothèque de résolution de noms. On pourrait encore mettre ce service dans le navigateur Web (ignorant les problèmes des applications non-Web), ou dans un relais sur le trajet.


Téléchargez le RFC 7304


L'article seul

RFC 7252: Constrained Application Protocol (CoAP)

Date de publication du RFC : Juin 2014
Auteur(s) du RFC : Z. Shelby (Sensinode), K. Hartke, C. Bormann (Universitaet Bremen TZI)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF core
Première rédaction de cet article le 2 juillet 2014


Le protocole CoAP n'est pas destiné aux ordinateurs, ni même aux smartphones mais principalement aux petits engins, aux machines qui n'ont parfois qu'un microcontrôleur 8 bits pour tout processeur, très peu de mémoire (cf. RFC 6574) et qui, en prime, sont connectées par des liens radio lents et peu fiables (les « LowPAN » des RFC 4919 et RFC 4944), allant parfois à seulement quelques dizaines de kb/s. Pour de tels engins, les protocoles comme HTTP et TCP sont trop contraignants. CoAP est un « HTTP-like » (il reprend pas mal de termes et de concepts de HTTP, comme le modèle REST) mais spécialement conçu pour des applications M2M (machine-to-machine) dans des environnements à fortes limitations matérielles.

Le succès de HTTP et notamment de REST a sérieusement influencé les concepteurs de CoAP. Le modèle de CoAP est celui de HTTP : protocole requête/réponse, verbes GET, POST, DELETE, etc, des URI et des types de media comme sur le Web (application/json...) Cette proximité facilitera notamment le développement de passerelles entre le monde des objets CoAP et le Web actuel (section 10). Une des pistes explorées pour l'Internet des Objets (terme essentiellement marketing mais répandu) avait été de prendre de l'HTTP normal et de comprimer pour gagner des ressources réseau mais CoAP suit une autre voie : définir un protocole qui est essentiellement un sous-ensemble de HTTP. Si vous connaissez HTTP, vous ne serez pas trop perdu avec CoAP.

CoAP tournera sur UDP (RFC 768), TCP étant trop consommateur de ressources, et, si on veut de la sécurité, on ajoutera DTLS (RFC 6347). CoAP fonctionne donc de manière asynchrone (on lance la requête et on espère une réponse un jour). On pourrait décrire CoAP comme composé de deux parties, un transport utilisant UDP avec des extensions optionnelles si on veut de la fiabilité (accusés de réception), et un langage des messages (requêtes et réponses en HTTP-like).

Et les messages ? L'en-tête CoAP (section 3 du RFC) a été conçu pour être facile à analyser par des programmes tournant sur de petites machines. Au lieu du texte lisible de HTTP, l'en-tête de CoAP commence par une partie fixe de quatre octets, qui comprend un Message ID de deux octets. Ce Message ID permet de détecter les duplicatas et d'associer un accusé de réception à un message précis. À noter qu'avec ces deux octets, on est limité à environ 250 messages par seconde (en raison du paramètre EXCHANGE_LIFETIME qui est à 247 secondes par défaut). Ce n'est pas une limite bien grave : les ressources (en énergie, en capacité réseau, etc) des machines CoAP ne leur permettront pas d'être trop bavardes, de toute façon.

Le message contient également un champ Token (utilisé pour mettre en correspondance les requêtes et les réponses, à un niveau supérieur à celui des messages, qui sont, eux, identifiés par le Message ID) et un code sur huit bits. Ce code s'écrit sous forme d'un groupe de trois chiffres, le premier indiquant la classe (2 : succès, 4 : erreur du client, 5 : erreur du serveur, etc) et suivi d'un point, et les deux autres chiffres fournissant des détails. Comme vous connaissez HTTP, vous ne serez pas étonné d'apprendre que le code 4.03 indique un problème de permission... Un registre de ces codes existe.

Notez bien que l'encodage, lui, est radicalement différent de celui de HTTP. Il est en binaire et le code 4.03 sera transmis sous forme d'un octet (10000011, avec 100 pour la classe et 11 pour le détail), pas sous forme de la chaîne de caractères qu'utilise HTTP.

Le message contient ensuite des options, qui jouent le rôle des en-têtes de requête de HTTP (leur liste est également dans un registre, on y trouve des trucs connus comme If-Match - section 5.10.8.1, Accept - section 5.10.4 - ou ETag - section 5.10.6). Pour économiser quelques bits, leur encodage est assez spécial. Le numéro de l'option, normalement sur 16 bits, n'est pas transmis tel quel mais sous forme d'une différence avec l'option précédente. Ainsi, si on utilise les options 4 (ETag) et 5 (If-None-Match), la première option indiquera bien 4 mais la seconde indiquera 1... De même, les valeurs de l'option sont violemment comprimées. Si on veut envoyer un entier, on ne doit transmettre que le nombre d'octets nécessaire (rien si l'entier vaut zéro, un seul octet s'il est inférieur à 255, etc).

Le transport utilisé par CoAP, UDP, ne fournit aucune garantie de remise : un paquet UDP peut être perdu, et UDP ne s'en occupera pas, ce sera à l'application de gérer ce cas. CoAP fournit un service pour faciliter la vie aux applications : on peut marquer un message CoAP comme nécessitant un accusé de réception (type CON pour Confirmable). Si ce type est présent dans l'en-tête CoAP, l'expéditeur recevra un accusé de réception (type ACK) portant le Message ID du message reçu. Le RFC fournit l'exemple d'un capteur de température interrogé en CoAP :

Client -> Serveur: CON [0xbc90] "GET /temperature" 
Serveur -> Client: ACK [0xbc90] "2.05 Content" "22.5 °C"

Ici, le second message, la réponse du serveur, contient un accusé de réception pour 0xbc90 (le Message ID). La requête est un GET et la réponse a le code 2.05 (un succès). Notez que, contrairement à HTTP, les codes sont écrits avec un point entre le premier chiffre (la classe) et les deux autres (le détail, cf. section 5.9). Ainsi, 4.04 a la classe 4 (erreur du client) et le détail 04 (ressource non trouvée). Comme indiqué plus haut, les classes sont les mêmes qu'en HTTP : 2 pour un succès, 4 pour une erreur du client (qui devrait changer sa requête avant de réessayer) et 5 pour une erreur du serveur (réessayer à l'identique plus tard peut marcher).

Le Message ID et ces types CON et NON (son contraire, Non-confirmable) permettent de mettre en œuvre des mécanismes simples de retransmission. L'émetteur envoie un message de type CON, s'il ne reçoit pas de message de type ACK à temps, il réessaie, avec croissance exponentielle du délai. Le Message-ID permet aussi de détecter les éventuels duplicatas.

On n'est pas obligé d'utiliser le type CON. Si un message n'a pas besoin de fiabilité (par exemple les lectures périodiques qu'envoie un capteur, que quelqu'un écoute ou pas), il sera du type NON.

Le Message ID doit évidemment être unique, pendant la durée de vie possible des échanges (variable EXCHANGE_LIFETIME, quatre minutes par défaut). Il peut être généré de manière incrémentale (l'émetteur garde un compteur indiquant le dernier Message ID généré) mais, dans ce cas, le RFC recommande (section 4.4) que la valeur initiale soit aléatoire, pour limiter les possibilités d'attaque par un méchant non situé sur le chemin des données (c'est un problème de sécurité analogue à celui des identificateurs de fragments IP - voir l'option --frag-id-policy de l'outil frag6 - ou celui des numéros de séquences initiaux de TCP - voir le RFC 6528).

Autre problème pour l'émetteur CoAP : quelle taille de messages ? Les machines pour qui CoAP est conçu peuvent avoir du mal à gérer parfaitement la fragmentation. Et puis des gros paquets ont davantage de chances d'être corrompus que des petits, sur les liens radios typiques des machines CoAP. Le RFC recommande donc de ne pas dépasser la MTU et, si elle est inconnue, de ne pas dépasser 1 280 octets en IPv6. (C'est plus compliqué pour IPv4 où le RFC recommande 576 octets et le bit DF - Don't Fragment - en notant que, pour les usages typiques de CoAP, le chemin sera simple, avec peu de routeurs et pas de liens à grande distance, donc peu de chances que la fragmentation soit nécessaire.)

Comme CoAP repose sur UDP, il n'y a pas de mécanisme de contrôle de la congestion par défaut. CoAP doit donc s'auto-limiter : réémissions avec croissance exponentielle des délais, et au maximum une requête en attente pour chaque machine avec qui on communique (paramètre NSTART). Ces règles sont pour le client. Si tous les clients les respectent, tout ira bien. Mais un serveur CoAP prudent doit considérer que certains clients seront bogués ou malveillants. Les serveurs ont donc tout intérêt à avoir une forme ou une autre de limitation de trafic.

La sémantique des requêtes et réponses CoAP figure en section 5. On l'a dit, elle ressemble à celle de HTTP, un client qui envoie une requête et un serveur qui transmet une réponse. L'une des plus grosses différences avec HTTP est le caractère asynchrone de l'échange.

Les requêtes sont les classiques (section 5.8) GET, POST, PUT et DELETE, agissant sur une ressource donnée (identifiée par un URI), comportant des données optionnelles (surtout utilisées pour POST et PUT) et utilisant les types de média. CoAP exige que GET, PUT et DELETE soient idempotents (une bonne pratique mais pas toujours respectée sur le Web). Une requête non reconnue et la réponse sera un 4.05. La liste des requêtes possibles est dans un registre IANA.

Du fait du caractère asynchrone de l'échange, mettre en correspondance une requête et une réponse est un peu plus compliqué qu'en HTTP, où toutes les deux empruntaient la même connexion TCP. Ici, la section 5.3.2 résume les règles de correspondance : une réponse correspond à une requête si l'adresse IP source de la réponse était l'adresse IP de destination de la requête et que le Message ID et le Token correspondent.

Comme HTTP, CoAP permet d'utiliser des relais. C'est d'autant plus important que les machines CoAP sont souvent indisponibles (hibernation pour économiser la batterie, par exemple) et un relais pourra donc répondre à la place d'une machine injoignable. Les relais sont traités plus en détail en section 5.7. Comme en HTTP, on pourra avoir des forward proxies sélectionnés par les clients ou des reverse proxies placés devant un serveur.

On l'a vu, CoAP, comme HTTP, utilise des URI, détaillés en section 6. Ils commencent par les plans coap: ou coaps:. On verra ainsi, par exemple, coap://example.com/~sensors/temp.xml ou coaps://[2001:db8::2:1]/measure/temperature/. Attention, tout le monde croit bien connaître les URI car ils sont massivement utilisés pour le Web mais le RFC met en garde les programmeurs : il y a plein de pièges dans le traitement des URI, notamment dans le décodage des valeurs encodées en pour-cent.

Si le port n'est pas indiqué dans l'URI (ce qui est le cas des deux exemples ci-dessus), le port par défaut est 5683 pour coap: et 5684 pour coaps:.

CoAP utilise, comme HTTP, la convention du /.well-known/ (RFC 5785) pour placer des ressources « bien connues », facilitant ainsi leur découverte.

Justement, à propos de découverte, la section 7 détaille comment un client CoAP va pouvoir trouver les URI des ressources intéressantes. CoAP est conçu pour des communications de machine à machine, où aucun humain n'est présent dans la boucle pour deviner, faire une recherche Google, utiliser son intuition, etc. La méthode privilégiée dans CoAP est celle du RFC 6690 : un fichier au format normalisé situé en /.well-known/core.

Les section 9 et 11 sont consacrées à la sécurité de CoAP. Évidemment, vu les ressources très limitées des machines CoAP typiques, il ne faut pas s'attendre à une grande sécurité. Par exemple, une protection par DTLS (RFC 6347) est possible mais pas obligatoire, juste conseillée. En outre, les ressources matérielles limitées des machines CoAP compliquent la tâche du programmeur : des processeurs très lents qui facilitent les attaques par mesure du temps écoulé, et pas de bon générateur aléatoire. En l'absence d'un tel générateur, on ne peut donc pas générer des clés cryptographiques localement, il faut le faire en usine et les enregistrer sur la machine. Et pas question de mettre les mêmes clés à tout le monde : comme les machines CoAP seront souvent dispersées dans des endroits ayant peu de sécurité physique (capteurs placés un peu partout), et n'auront en général pas de protection contre les manipulations physiques, pour des raisons de coût, un attaquant pourra facilement ouvrir une machine et en extraire ses clés.

La complexité est l'une des ennemies principales de la sécurité. Le RFC rappelle que le traitement des URI (bien plus compliqué qu'il n'en a l'air) impose des analyseurs relativements complexes et donc augmente la probabilité de bogues, pouvant mener à des failles de sécurité. Même chose pour le format des liens du RFC 6690.

Question réseau, CoAP souffrira sans doute d'attaques rendues possibles par l'usurpation d'adresses IP. Comme UDP ne nécessite pas d'échange préalable, il est trivial d'envoyer un paquet depuis une adresse usurpée et de le voir accepté. Cela permet des tas d'attaques comme :

  • Envoyer un RST (Reset) aux demandes de connexion vers un serveur, rendant ainsi ce serveur inutilisable (déni de service),
  • Envoyer une fausse réponse à une requête GET.

Pour cette deuxième attaque, il faut pouvoir deviner le Token utilisé. Si l'attaquant est aveugle (parce que situé en dehors du chemin des paquets), sa tâche sera difficile si le Token est bien aléatoire (mais ces machines contraintes en ressources n'ont souvent pas de vrai générateur aléatoire, au sens du RFC 4086).

En pratique, il est probable que les déploiements réels de CoAP aient lieu dans des réseaux fermés, ne contenant que des machines de confiance, et où l'accès à l'extérieur passera par une passerelle qui s'occupera de sécurité. Autrement, un simple ping à destination d'une machine CoAP pourrait suffir à vider sa batterie.

CoAP introduit aussi une autre menace, celle d'attaques par amplification (section 11.3). Comme SNMP et le DNS (tous les deux déjà utilisés pour ces attaques), CoAP a des réponses plus grandes que les questions (permettant l'amplification) et repose sur UDP et non pas TCP (permettant l'usurpation d'adresse IP).

La seule limite à ces attaques semble devoir être les faibles ressources des machines CoAP, qui devraient les rendre relativement peu intéressantes comme amplificateurs contre des serveurs Internet. En revanche, à l'intérieur d'un réseau de machines CoAP, le risque demeure élevé et le RFC ne propose aucune vraie solution.

CoAP est, d'une certaine façon, un vieux protocole (voir l'article « CoAP: An Application Protocol for Billions of Tiny Internet Nodes » de Carsten Bormann, Angelo Paolo Castellani et Zach Shelby publié en 2012 dans dans Internet Computing, IEEE (Volume:16 , Issue: 2) et malheureusement pas disponible en ligne, il faut demander une copie à l'auteur). Il existe déjà plusieurs mise en œuvre, qui ont été souvent testées dans des essais d'interopérabilité formels. Un exemple de mise en œuvre est celle du système Contiki.

Des exemples de requêtes CoAP décrites en détail figurent dans l'annexe A du RFC. Grâce à Renzo Navas, voici quelques traces CoAP au format pcap :

Si tcpdump ne connait pas encore CoAP, Wireshark, par contre, sait bien l'analyser. Ici, une la première requête GET du premier fichier pcap et sa réponse :


Constrained Application Protocol, TID: 27328, Length: 21
    01.. .... = Version: 1
    ..00 .... = Type: Confirmable (0)
    .... 0000 = Option Count: 0
    Code: GET (1)
    Transaction ID: 27328
    Payload Content-Type: text/plain (default), Length: 17, offset: 4
        Line-based text data: text/plain
            \273.well-known\004core

Constrained Application Protocol, TID: 27328, Length: 521
    01.. .... = Version: 1
    ..10 .... = Type: Acknowledgement (2)
    .... 0000 = Option Count: 0
    Code: 2.05 Content (69)
    Transaction ID: 27328
    Payload Content-Type: text/plain (default), Length: 517, offset: 4
        Line-based text data: text/plain
            \301(\261\r
            [truncated] \377</careless>;rt="SepararateResponseTester";title="This resource will ACK anything, but never send a separate response",</helloWorld>;rt="HelloWorldDisplayer";title="GET a friendly greeting!",</image>;ct="21 22 23 24";rt="Ima

Merci à Laurent Toutain pour sa relecture attentive.


Téléchargez le RFC 7252


L'article seul

RFC 7288: Reflections On Host Firewalls

Date de publication du RFC : Juin 2014
Auteur(s) du RFC : D. Thaler (Microsoft)
Pour information
Première rédaction de cet article le 2 juillet 2014


Aujourd'hui, on met des pare-feux partout et leur présence est en général considérée, sans aucune réflexion supplémentaire, comme indispensable. Il existe des pare-feux qui se branchent sur le réseau et protègent ainsi toutes les machines du réseau (network firewall). Et il existe des pare-feux sur les machines terminales, des host firewalls, qui font l'objet de ce RFC. En présence d'un tel pare-feu, on peut avoir une application qui tourne, et donc consomme des ressources, mais ne peut pas communiquer avec l'extérieur puisqu'elle est bloquée par le pare-feu. Le même résultat de sécurité pourrait être atteint en ne faisant pas tourner l'application, ou en la configurant pour limiter ses possibilités. Cette approche ne serait-elle pas meilleure ?

Le RFC 2979 discutait des pare-feux sur l'Internet et des propriétés qu'on attendait d'eux (et les RFC 4949 et RFC 4948 discutaient de la terminologie). Le RFC 2979 pointait notamment le risque de « faux positif », lorsqu'un pare-feu bloque accidentellement une communication légitime. Les vendeurs de pare-feux oublient toujours de mentionner ce risque. Pourtant, nombreux sont les pare-feux qui bloquent stupidement des connexions parfaitement légitimes, par exemple parce que les paquets IP contiennent des options, ou des en-têtes IPv6 (cf. RFC 7045). Le RFC 4924 revient sur cette question des pare-feux en notant qu'ils sont largement responsables de l'ossification de l'Internet, et que de plus en plus d'applications consacrent beaucoup d'effort à contourner ces obstacles. En outre, les pare-feux contribuent souvent à un faux sentiment de sécurité, qui peut mener à baisser sa garde sur les machines supposées « protégées par le pare-feu » (section 6 de notre RFC). Enfin, la section 1.1 du RFC rappelle que le NAT n'est pas un vrai pare-feu.

Le problème des pare-feux est d'autant plus aigu que la réponse classique à la question de l'utilité d'un pare-feu est « bloquer le trafic non souhaité », mais que cette réponse ne dit pas « non souhaité par qui ». Par l'utilisateur final ? Par son employeur ? Par l'administrateur réseau ? Par le développeur ? Leurs intérêts sont souvent en conflit et on ne peut pas espérer un consensus simple entre tous ces acteurs (le RFC reprend l'excellent terme de « tussle »). Pas étonnant qu'on voit souvent des « courses aux armements » entre ceux qui bloquent et ceux qui contournent le blocage.

Le pare-feu typique est souvent configuré par des règles allow/deny où on indique que tel type de trafic est autorisé ou interdit (section 2 du RFC). Voici par exemple des règles Shorewall où tout est interdit par défaut mais où on accepte quelques services :

# SMTP 
ACCEPT               net       fw       tcp      25
ACCEPT               net       fw       tcp      587
# IMAP, IMAPS et POPS
ACCEPT               net        fw      tcp      143
ACCEPT               net        fw      tcp      993
ACCEPT               net        fw      tcp      995
# Nagios Remote Execution (NRPE)
ACCEPT               net:192.0.2.187  fw  TCP     5767
# SNMP
ACCEPT               net:192.0.2.187,203.0.113.170   fw  udp   161

Pour un pare-feu sur une machine terminale, l'utilisateur n'a pas le droit de modifier ces règles sauf dans le cas (fréquent avec les machines personnelles) où l'utilisateur est en même temps administrateur système. Par contre, des applications peuvent configurer le pare-feu, pour les systèmes où celui-ci fournit une API aux applications, ou bien s'il existe un protocole standard de changement de la configuration du pare-feu comme UPnP ou son successeur potentiel PCP (RFC 6887). Ces règles sont classées par notre RFC en deux catégories : réduction de la surface d'attaque (empêcher l'application de faire des choses que le développeur ne voulait pas qu'elle fasse) et politique de sécurité (empêcher l'application de faire des choses que l'administrateur système ne voulait pas qu'elle fasse).

La section 3 décrit la première catégorie, la réduction de la surface d'attaque. On peut se demander si cette catégorie est utile : après tout, si on ne veut pas qu'une application, par exemple, détruise des fichiers en réponse à une requête venue par le réseau, le mieux est de ne pas mettre en œuvre cette fonction. On sera alors bien plus en sécurité. Sauf qu'on n'a pas toujours le luxe d'avoir uniquement des applications bien conçues et bien écrites. Souvent, le développeur a fait des impasses (ou, en termes plus savants, a pris de la dette technique) et réécrire l'application serait trop coûteux. C'est par exemple le cas de beaucoup d'applications Web programmées à la va-vite sans souci de sécurité, en PHP ou VB.NET. Coller un pare-feu devant l'application est en général moins coûteux et plus efficace que de se plonger dans le plat de spaghettis.

Il y a aussi des cas où la dette prise par le développeur (reporter le problème à plus tard...) était justifiée par un manque de support de la part du système. Par exemple, une règle aussi simple et courante que « communication uniquement avec le lien local » est difficile à mettre en œuvre de façon portable, sur Unix, dans tous les langages de programmation. (Notez au passage que n'autoriser que les adresses IP source du même réseau que celui du serveur n'est pas suffisant, en raison de la possibilité d'usurpation d'adresse. Pour UDP, il faudrait aussi tester le TTL, cf. RFC 5082.) Il y a aussi le cas où la machine se déplace d'un réseau sûr à un réseau non-sûr. Le développeur est donc tenté de déléguer la mise en œuvre de cette politique à un pare-feu.

Même chose avec des politiques complexes, comme de ne communiquer que sur les liens les moins coûteux, sachant que l'application n'a pas forcément connaissance des coûts (sur Android, cette politique est en général purement binaire : on considère que la 3G est coûteuse et le Wifi gratuit, ce qui n'est pas toujours vrai.) Avoir une API permettant à l'application d'exprimer ses souhaits, que le système d'exploitation traduirait en règles de pare-feu concrètes, simplifierait beaucoup les choses.

Quelles sont donc les solutions pour l'utilisateur, qui se retrouve avec une application « imparfaite », pleine de failles de sécurité ? D'abord, réparer le logiciel en corrigeant ces failles. Les partisans des pare-feux feront évidemment remarquer qu'il est plus rapide et plus efficace d'utiliser un pare-feu : déboguer un logiciel mal écrit est long et coûteux et on n'est jamais sûr d'avoir bouché tous les trous de sécurité. Il y a d'ailleurs des cas où réparer le logiciel est quasi-impossible : pas les sources pour le faire ou bien pas de mécanisme de mise à jour pour les engins qui font tourner ce logiciel. En général, un changement des règles du pare-feu peut être déployé plus facilement qu'un patch.

Autre idée, ne pas utiliser le logiciel. Solution brutale mais qui résoud tout. Notez que l'idée n'est pas si violente qu'elle en a l'air : certains systèmes font tourner par défaut des logiciels qui ne devraient pas tourner. Arrêter ce logiciel inutile est une solution réaliste.

Enfin, il y a le pare-feu, qui va protéger contre le logiciel mal fait. Le logiciel tournera toujours, consommant des ressources (personnellement, je ne trouve pas l'argument très convaincant : si le logiciel est juste en attente d'événements, il ne consomme pas grand'chose). Et, surtout, si un utilisateur tente de se servir du logiciel, il aura des surprises, car beaucoup de programmes ne sont pas conçus de manière robuste : ils réagissent mal quand, par exemple, certains services réseau ne leur sont pas accessibles. Parfois, cela aggrave la consommation de ressources (par exemple, le logiciel, n'ayant pas compris qu'il était bloqué par le pare-feu, réessaie en boucle). Autre problème avec les pare-feux : ils sont souvent surbloquants. Par exemple, ils bloquent tout ce qu'il ne connaissent pas et gênent donc considérablement l'innovation. Certains analysent les paquets de manière très sommaire (oubliant par exemple certaines fonctions avancées du protocole) et prennent donc de mauvaises décisions. C'est ainsi qu'ICMP est souvent bloqué, menant à des problèmes de découverte de la MTU du chemin (RFC 4890 et RFC 2979).

Deuxième catégorie de règles dans un pare-feu, celles qui visent à faire respecter une politique de sécurité. L'administrateur a décidé qu'on ne regarderait pas YouTube, les règles bloquent YouTube. Cette catégorie peut en théorie être mise en œuvre à trois endroits. D'abord, dans les applications elles-mêmes. Ça ne passe évidemment pas à l'échelle : il faudrait ajouter du code à toutes les applications (faites un peu une liste du nombre de clients HTTP sur votre machine, par exemple ; rappelez vous qu'il n'y a pas que les navigateurs Web) et les configurer. En pratique, il y aura toujours une application oubliée. Cette solution n'est donc pas réaliste.

Deuxième endroit possible, le pare-feu. Outre les problèmes discutés plus haut (application qui réagit mal au blocage de certains services), cela mène facilement à une course aux armements : comme les désirs de l'utilisateur final et de l'administrateur du pare-feu ne coïncident pas forcément, les applications, à la demande des utilisateurs, vont évoluer pour contourner le filtrage, qui va devenir de plus en plus intrusif et ainsi de suite jusqu'à ce que tout le monde, pris dans cette course, oublie le but originel de la politique de sécurité. Comme le disait la section 2.1 du RFC 4924, le filtrage sans le consentement (au moins passif) de l'utilisateur est difficile, et va mener à des solutions compliquées, coûteuses et fragiles, violant largement les bons principes d'architecture réseau (par exemple en faisant tout passer au-dessus de HTTP, seul protocole autorisé).

Reste le troisième endroit, qui a plutôt les faveurs du RFC : faire des politiques de sécurité un service auquel s'inscrivent les applications. L'application utilise une bibliothèque (comme les TCP wrappers) ou bien parle un protocole réseau (comme PCP, RFC 6887), décrit ce qu'elle veut, et apprend si c'est autorisé ou pas, de manière propre. Cela nécessite évidemment des standards (comme PCP, qui est pour l'instant très peu déployé, et qui est de toute façon sans doute insuffisant pour gérer toutes les politiques possibles) car on n'imagine pas chaque application ajouter du code pour l'API de chaque marque de pare-feu.

Cette technique est très originale par rapport à l'approche habituelle sur les pare-feux mais c'est celle recommandée par notre RFC. Pour le résumer en une phrase, on fait trop travailler les pare-feux et pas assez les applications.


Téléchargez le RFC 7288


L'article seul

RFC 7250: Using Raw Public Keys in Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)

Date de publication du RFC : Juin 2014
Auteur(s) du RFC : P. Wouters (Red Hat), H. Tschofenig, J. Gilmore, S. Weiler (SPARTA), T. Kivinen (AuthenTec)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 1 juillet 2014


Le système de sécurité TLS, normalisé dans le RFC 5246 est souvent confondu avec la norme de certificats X.509. On lit souvent des choses comme « le serveur TLS envoie alors un certificat X.509 au client », voire, pire, on appelle ces certificats « certificats TLS » (ou, encore plus inexact, « certificats SSL »). Dans la réalité, les deux systèmes, TLS et X.509, sont indépendants. X.509 peut servir à autre chose qu'à TLS (et c'est pourquoi parler de « certificats SSL » est une grosse erreur), et TLS peut utiliser d'autres techniques d'authentification que X.509. Une de ces techniques était déjà spécifiée dans le RFC 6091, l'utilisation de clés PGP. Une autre vient d'être normalisée dans ce RFC, l'utilisation de « clés brutes » (raw keys), sans certificat autour. Comme une clé seule ne peut pas être authentifiée, ces clés brutes n'ont de sens que combinées avec un mécanisme d'authentification externe, comme DANE.

Rappelons en effet qu'un certificat, c'est une clé publique (champ SubjectPublicKeyInfo dans X.509) plus un certain nombre de métadonnées (comme la date d'expiration) et une signature par une autorité qui certifie la clé. Pour la session TLS, la seule partie indispensable est la clé (dans un certificat « auto-signé », seule la clé a un sens, le reste du certificat ne pouvant pas être vérifié). Mais, en pratique, le chiffrement sans l'authentification est limité : il ne protège que contre un attaquant purement passif. Dès que l'attaquant est actif (pensons par exemple à un hotspot Wifi tentant... mais tenu par l'attaquant), il peut rediriger le client TLS vers un serveur intermédiaire qui va faire croire au client qu'il est le vrai serveur, avant de retransmettre vers le serveur authentique. Pour contrer cette attaque de l'homme du milieu, on authentifie le serveur. Sur l'Internet, la méthode la plus courante est l'utilisation de certificats X.509, ou plus précisement de leur profil PKIX (RFC 5280). Cette méthode est d'une fiabilité très douteuse : des centaines d'organisations dans le monde peuvent émettre un faux certificat pour gmail.com et il ne peut donc pas y avoir de confiance rationnelle dans ce système (cf. « New Tricks for Defeating SSL in Practice »).

Il existe d'autres méthodes d'authentification que X.509, permettant de faire un lien entre le serveur qu'on veut contacter, et l'entité qui nous envoie une clé publique en TLS :

  • Les certificats ou clés publiés dans le DNS et sécurisés par DNSSEC, à savoir la technique DANE du RFC 6698,
  • Les certificats ou clés publiés dans un annuaire LDAP,
  • Les certificats ou clés stockés en dur chez le client. Cela manque de souplesse (il est difficile de les changer) et ce n'est donc pas très recommandé pour l'Internet public mais c'est envisagé pour des déploiements plus fermés comme ceux du protocole CoAP (RFC 7252), où les objets peuvent sécuriser la communication avec DTLS. (Cette troisième méthode est sans doute la seule possible pour l'authentification du client TLS par le serveur, cf. section 4.3.)

La section 6 de notre RFC revient en détail sur l'authentification de ces clés brutes. Sans cette authentification, une session TLS utilisant ces clés est très vulnérable à l'homme du milieu.

La section 3 décrit les détails de format des clés brutes. Il faut deux extensions au protocole TLS, client_certificate_type et server_certificate_type (enregistrées dans le registre des extensions), qui permettent d'indiquer (lors de la négociation TLS) séparément le type de certificat utilisé par le serveur et par le client. Par exemple, pour le serveur, cela s'écrit ainsi :

 
struct {
           select(ClientOrServerExtension) {
               case client:
                 CertificateType server_certificate_types<1..2^8-1>;
               case server:
                 CertificateType server_certificate_type;
           }
   } ServerCertTypeExtension;

Lorsque ces extensions TLS sont utilisées, la structure Certificate est modifiée ainsi :

   

struct {
       select(certificate_type){

           // certificate type defined in this document.
           case RawPublicKey:
             opaque ASN.1_subjectPublicKeyInfo<1..2^24-1>;

           // X.509 certificate defined in RFC 5246
           case X.509:
             ASN.1Cert certificate_list<0..2^24-1>;

           // Additional certificate type based on TLS
           //   Certificate Type Registry 
           //   http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#tls-extensiontype-values-3
           // for instance PGP keys
       };
   } Certificate;

(On notera que le RFC 6091 utilisait un autre mécanisme, l'extension cert_type, numéro 9, mais qui n'a pas été réutilisée ici, le RFC 6091 n'ayant pas le statut « Chemin des Normes » et le mécanisme cert_type ne permettant pas des types différents pour le client et le serveur.) Le champ subjectPublicKeyInfo contient un encodage DER de la clé et d'un identificateur d'algorithme (un OID comme 1.2.840.10045.2.1 pour ECDSA) :

     SubjectPublicKeyInfo  ::=  SEQUENCE  {
           algorithm               AlgorithmIdentifier,
           subjectPublicKey        BIT STRING  }

L'omission d'une grande partie du certificat permet de se contenter d'un analyseur ASN.1 plus réduit. Mais les clés brutes ne sont malheureusement pas réellement brutes, c'est quand même une structure décrite en ASN.1 et il faut donc un analyseur minimal.

La section 4 décrit les modifications à la négociation initiale de TLS (les autres parties de la communication ne sont pas modifiées par ce RFC). Le client ajoute une ou deux des extensions TLS (la plupart du temps, en TLS, le client ne s'authentifie pas, et on n'aura donc que l'extension server_certificate_type). La ou les extensions indiquent les types de certificat reconnus par le pair TLS (si l'extension est absente, seul X.509 est reconu). Ainsi, un serveur nouveau, intégrant les clés brutes, sait tout de suite si un client pourrait les traiter (si le client n'a pas mis server_certificate_type, ou bien si son server_certificate_type n'inclut pas les clés brutes, ce n'est pas la peine d'en envoyer). Si c'est le client qui est nouveau et le serveur ancien, la réponse du serveur ne contiendra pas les extensions et le client saura donc qu'il ne faut pas s'attendre aux clés brutes. Si le serveur reconnait l'extension, mais aucun des types de certificats indiqués, il termine la session. À noter que ces clés brutes marchent avec tous les algorithmes de cryptographie déjà reconnus.

La section 5 du RFC fournit des exemples de sessions TLS avec cette nouvelle possibilité. Par exemple, ce dialogue montre un client qui gère les clés brutes du serveur (et uniquement celles-ci, ce n'est donc pas un navigateur Web typique) et un serveur qui accepte d'envoyer sa clé ainsi. Le client n'est pas authentifié (il n'envoie pas sa clé ou son certificat et ne se sert donc pas de client_certificate_type) :


client_hello,
server_certificate_type=(RawPublicKey) // [1]
                       ->
                       <- server_hello,
                          server_certificate_type=(RawPublicKey), // [2]
                          certificate, // [3]
                          server_key_exchange,
                          server_hello_done

client_key_exchange,
change_cipher_spec,
finished               ->

                       <- change_cipher_spec,
                          finished

Application Data       <-------> Application Data

Ce cas d'un client ne comprenant que les clés brutes est un cas qui peut se produire pour l'Internet des objets, par exemple avec CoAP. Mais un client différent, par exemple un navigateur Web, va accepter d'autres types de certificats. Le dialogue pourrait ressembler à :


client_hello,
server_certificate_type=(X.509, RawPublicKey)
client_certificate_type=(RawPublicKey) // [1]
                         ->
                         <-  server_hello,
                             server_certificate_type=(X.509)//[2]
                             certificate, // [3]
                             client_certificate_type=(RawPublicKey)//[4]
                             certificate_request, // [5]
                             server_key_exchange,
                             server_hello_done
certificate, // [6]
client_key_exchange,
change_cipher_spec,
finished                  ->

                          <- change_cipher_spec,
                             finished

Application Data        <------->     Application Data

Ici, le serveur reconnait la nouvelle extension mais ne sait envoyer que des certificats X.509 (ce qui est donc fait par la suite). Le client, lui, s'authentifie en envoyant une clé brute (même si ce message est marqué certificate dans le dialogue).

Il existe apparemment déjà plusieurs mises en œuvre de TLS qui utilisent ces extensions.


Téléchargez le RFC 7250


L'article seul

RFC 7284: The Profile URI Registry

Date de publication du RFC : Juin 2014
Auteur(s) du RFC : M. Lanthaler
Pour information
Première rédaction de cet article le 1 juillet 2014


Le RFC 6906 définit la notion de profil, une variante (en général restrictive) d'un format. Les profils du RFC 6906 sont identifiés par un URI et ce nouveau RFC crée un registre de ces URI, afin de faciliter la tâche des développeurs.

Les conditions d'inscription dans le registre sont spécifiées dans la section 2 : il faut indiquer l'URI qui sert d'identificateur, un nom court pour le profil, la description et un lien vers une documentation stable du profil, accessible publiquement. Ensuite (section 4), c'est du premier arrivé, premier servi (politique décrite dans la section 4.1 du RFC 5226). La section 3 donne un exemple en utilisant un profil bidon, http://example.com/profiles/example.

Le registre est désormais en ligne et comprend au début cinq profils :

  • urn:example:profile-uri, prévu pour être utilisé dans les exemples, documentations, etc,
  • Dublin Core http://dublincore.org/documents/2008/08/04/dc-html/, un moyen pour mettre les méta-données en HTML,
  • http://www.w3.org/ns/json-ld#expanded, http://www.w3.org/ns/json-ld#compacted et http://www.w3.org/ns/json-ld#flattened, des profils du monde JSON décrits dans une norme du W3C.

Téléchargez le RFC 7284


L'article seul

RFC 7282: On Consensus and Humming in the IETF

Date de publication du RFC : Juin 2014
Auteur(s) du RFC : P. Resnick (Qualcomm)
Pour information
Première rédaction de cet article le 1 juillet 2014


L'IETF, organisme de normalisation des protocoles de l'Internet, fonctionne normalement par « consensus ». Il n'y a pas de vote formel, pas de majorité qui décide, on discute jusqu'à ce que presque tout le monde soit d'accord. À cause du « presque », on parle de « consensus approximatif » (rough consensus). Ça, c'est la théorie. En pratique, les évolutions récentes font que de plus en plus de décisions sont prises suite à des opérations qui ressemblent pas mal à des votes majoritaires, justement ce que les règles de l'IETF voulaient éviter. D'où ce RFC d'opinion, écrit pour défendre le principe du consensus approximatif, et pour appeler à se méfier de ces dérives.

Ce RFC a le statut « pour information » et représente le point de vue de son auteur : ce n'est pas un document officiel. En outre, il ne définit pas de nouvelles règles, il explique juste le fonctionnement idéal. Pour résumer ce RFC, revenons à la théorie. Le fonctionnement normal de l'IETF a été traduit par une percutante phrase de Dave Clark (dans « A Cloudy Crystal Ball - Visions of the Future »), que tous les participants à l'IETF connaissent par cœur : « We reject kings, presidents and voting. We believe in rough consensus and running code. ». Les décisions à l'IETF ne peuvent pas être prises par un seul individu (roi ou président), mais elles ne doivent pas non plus être prises par un vote, pour les raisons expliquées plus loin. Au contraire, elles doivent s'appuyer sur le consensus approximatif, lui-même nourri d'expérience concrète (le « running code », par opposition à des organisations comme l'ISO qui ont développé des usines à gaz impossibles à programmer). La pratique doit toujours l'emporter : le code qui marche a raison.

Notez que le consensus est approximatif : avoir un consensus tout court (tout le monde est d'accord ou, au moins, se tait) serait l'idéal mais ce n'est pas réaliste. Demander un consensus complet serait donner un droit de veto à n'importe quel individu négatif qui s'oppose à tout (l'IETF, comme toute organisation humaine, a quelques trolls de ce calibre...).

Comme il n'y a pas de vote, l'IETF a développé une autre pratique inhabituelle, le mmmmmmmm (humming). Lorsqu'on veut avoir une idée de l'opinion des gens, lors d'une réunion physique, on demande à chacun de se manifester par ce bruit produit en gardant la bouche fermée (ce qui assure un certain secret, contrairement aux mains levées). En faisant des mmmmmmmmm plus ou moins forts, on peut même avoir un vote non-binaire. Ce rituel est souvent pratiqué pendant les réunions IETF.

Mais tous les participants à l'IETF ne comprennent pas forcément ces subtilités, surtout lorsqu'ils viennent d'arriver. « Pourquoi un mmmmmm et pas un vote normal ? » ou « Il y avait une majorité de mmmmmm en faveur de la proposition B, pourquoi continue-t-on de discuter ? » Même quand le groupe de travail est arrivé à un résultat, bien des participants ne comprennent pas comment il a été obtenu et pensent que le processus a produit un mauvais résultat. Paradoxe : il n'y a pas de consensus à l'IETF sur l'importance du consensus. Ou plutôt, il y en a un en parole (la phrase de Clark est très souvent citée) mais pas dans la réalité du travail.

Donc, premier point expliqué par ce RFC, en section 2, l'important n'est pas que tout le monde soit d'accord, mais qu'il n'y ait pas trop de désaccord. Imaginons un groupe qui doit trancher entre deux formats pour un protocole (mettons JSON et XML, pour prendre un exemple réel...). Il y a des gens pour et des gens contre chacune des deux propositions. Cela veut-il dire qu'il n'y aura jamais de consensus ? Sauf que tout travail d'ingéniérie (et la création d'une norme technique en est un) nécessite des compromis. Si on demande un accord de tous, on ne l'aura jamais (surtout avec le goût des ingénieurs pour les discussions sur la couleur du garage à vélos). En revanche, si on demande qu'il n'y ait pas d'objections bloquantes, on peut arriver à un résultat. Les partisans de XML ne vont pas tout à coup préférer JSON. Par contre, ils peuvent considérer que JSON, sans être leur solution préférée, est acceptable, qu'il n'a pas de défaut qui mettrait vraiment en péril le nouveau protocole. Il ne faut donc pas demander aux participants « êtes-vous heureux avec ce choix ? » (il y aura forcément des râleurs) mais plutôt « ce choix a t-il un défaut rédhibitoire (et, si vous répondez oui, argumentez) ? » Bien sûr, il faut quand même qu'il y ait un certain nombre de gens qui soutiennent activement la proposition. Une solution technique qui ne rencontrerait aucune objection, mais pas non plus de soutien actif ne serait sans doute pas intéressante !

Autre point où il faut entrer dans les nuances, la différence entre compromis et compromission. Le mot « compromis » (en français, comme en anglais avec « compromise ») est utilisé dans deux sens différents. Le compromis est une bonne chose : en ingéniérie, on a rarement des solutions idéales. Il faut en général arbitrer et, par exemple, décider de sacrifier du temps CPU pour consommer moins de ressources réseau ou le contraire, ou bien mettre de la redondance pour assurer l'intégrité alors qu'elle va diminuer les performances, ou encore préférer un format texte qui facilite le déboguage à un format binaire plus efficace mais plus dur à traiter par les humains. Ce genre de compromis est un travail normal et essentiel de l'ingénieur, et l'IETF le fait.

En revanche, les compromissions sont néfastes : une compromission est quand on tient compte des opinions et pas des principes d'ingéniérie. Par exemple qu'on accepte telle option, qui semble inutile, voire dangereuse, parce qu'une partie du groupe de travail trépigne et y tient absolument. On ajoute alors l'option pour obtenir leur accord (ou leur non-désaccord), faisant ainsi une compromission (ou un « design by committee », où le résultat n'enthousiasme personne, à force d'avoir intégré plein de demandes contradictoires). Ce genre de compromission (qu'il faudrait plutôt appeler capitulation) n'a pas réglé les problèmes techniques. Pire, on peut voir ce que le RFC désigne sous le nom de « horse trading » (comportement de maquignon ?) « Je ne suis pas d'accord avec ta proposition sur l'algorithme de traitement de la congestion, tu n'es pas d'accord avec ma proposition sur le format des données, mettons-nous d'accord pour chacun soutenir la proposition de l'autre. » Cela permet d'arriver à un accord mais au prix de la qualité : les objections contre les deux propositions ont été ignorées, alors qu'elles étaient peut-être sérieuses techniquement et devaient être traitées comme telles. Ce comportement donne l'apparence du consensus alors qu'il est de la pure magouille.

Deuxième point important, en section 3, le fait qu'il faut traiter toutes les questions mais pas forcément les résoudre. Parfois, une objection technique va être résolue à la satisfaction de tous : celui qui l'a soulevée est convaincu que son objection était erronée, ou bien le groupe de travail a été convaincu qu'elle était sérieuse et a modifié le protocole en cours de discussion. Mais, parfois, le groupe de travail ne change pas de direction et celui qui a soulevé l'objection est frustré. Est-ce un échec du consensus ? Non, car il y a toujours « consensus approximatif ». Celui-ci est une solution de repli : l'idéal est le consensus complet mais, comme le dit le conte « Le meunier, son fils et l'âne », on ne peut pas toujours plaire à tout le monde et, de toute façon, il y a des gens qui ne sont jamais contents. La notion de consensus approximatif est faite pour ces cas-là : l'objection n'a pas été ignorée, elle a été sérieusement étudiée et le groupe de travail a délibérement décidé de continuer dans la voie originale. Compte-tenu de la forte personnalité de pas mal de participants à l'IETF, le consensus seulement approximatif, qui devrait être une exception à la règle du consensus, est souvent la norme.

Notez que ce consensus approximatif vu comme un repli lorsque le consensus idéal n'est pas atteint est une description nouvelle. Le RFC 1603 voyait plutôt le consensus approximatif comme exprimant le « point de vue dominant » du groupe de travail IETF. Cette vision quantitative (exprimée de manière encore plus directe dans son successeur, le RFC 2418) n'est pas partagée par ce nouveau RFC 7282, qui met l'accent sur le fait que les 0,1 % qui sont en désaccord ont peut-être raison (comme le disait un de mes enseignants à la fac, « la science n'est pas une démocratie »).

Et le mmmmmmm ? La section 4 revient sur son rôle. D'abord, pourquoi ne vote-t-on pas, à l'IETF (certainement la première question que posent tous les nouveaux venus) ? D'abord, l'IETF n'a pas de membres : il n'existe aucun processus formel d'adhésion. On s'inscrit à la liste de diffusion d'un groupe de travail et, hop, on est un participant à l'IETF. Impossible donc, de compter le corps électoral. Ensuite, le vote peut être une mauvaise chose : comme indiqué plus haut (même si l'expression n'est pas dans le RFC), la technique n'est pas démocratique. 51 % ou même 99 % d'un groupe de travail peut toujours voter X, c'est peut-être quand même Y qui est justifié techniquement. Le vote pourrait être un moyen de glisser sous le tapis les problèmes gênants, qui doivent au contraire être traités en pleine lumière. Une des raisons de l'utilisation du mmmmmmm est donc, précisément par le caractère folkorique de la procédure, de rappeler qu'on ne vote pas, à l'IETF (par comparaison avec, disons, un vote à main levée).

Le mmmmmmm sert donc à jauger la situation et à ordonner la discussion. Par exemple, s'il y a beaucoup plus de mmmmmmm objectant contre X que contre Y, partir des objections contre Y peut permettre d'optimiser le temps de discussion (sans pour autant considérer que Y a « gagné »).

Malgré cela, un effet pervers s'est développé, avec des gens considérant le mmmmmmm comme une sorte de vote anonyme. Ce n'est pas son but, le mmmmmmm devrait être utilisé au début (pour avoir une idée des points de vue en présence) pas à la fin pour clore le bec aux minoritaires. Le mmmmmmm n'est pas une méthode magique, et, s'il est mal utilisé, il a souvent les mêmes inconvénients que le vote classique (par exemple une extrême sensibilité à la rédaction des questions, ou même simplement à leur ordre).

Donc, le but, c'est le consensus ? Pas du tout. La section 5 insiste sur le fait que le consensus est un moyen et pas une fin. Le consensus approximatif est un moyen d'obtenir la meilleure décision technique possible, en s'assurant qu'aucune objection, aucun problème, n'a été négligé. Au contraire, l'expérience des SDO qui votent a largement montré que cela mène à des mauvaises décisions, notamment suite à des compromissions pour obtenir une majorité, et suite au mépris des remarques parfaitement valides des minoritaires.

Alors, évidemment, la méthode IETF prend du temps et est souvent exaspérante, même pour les plus patients des participants. Et il ne faut donc pas se faire d'illusions : les magouilles politiciennes existent aussi à l'IETF. Simplement, passer à un système plus rapide comme le vote permettrait d'avoir plus vite des normes de moins bonne qualité.

La section 6 revient sur la notion de majorité et sur l'approche quantitative, qu'appuyait le RFC 2418 (« Note that 51% of the working group does not qualify as "rough consensus" and 99% is better than rough. ») S'il y a 100 participants dans un groupe qui trouvent la proposition en cours excellente, et la soutiennent activement, et 5 participants qui sont en désaccord, prédisant, par exemple, « ce protocole va créer de la congestion et n'a aucun mécanisme de réaction en cas de congestion » et que personne ne répond de manière argumentée à ce point, alors, il n'y a pas de consensus, même pas de consensus approximatif. Une objection a été ignorée, pas traitée, et pas rejetée. Le faible nombre d'opposants n'y change rien, on ne peut pas parler d'un consensus, même pas approximatif. (Le RFC note que c'est pareil si la ou les personnes qui ont soulevé l'objection ne sont pas des participants réguliers, et qu'ils n'insistent pas après avoir fait leurs remarques ; les remarques en question doivent quand même être traitées.)

En sens inverse, et même si cela peut sembler anti-intuitif, on peut avoir 5 personnes qui soutiennent un projet et 100 qui sont contre, et qu'il y ait quand même un consensus en faveur du projet (section 7). Comment est-ce possible ? Le RFC décrit une telle situation (et des cas analogues se sont effectivement produits) : un groupe de travail a peu de participants actifs. Ils sont arrivés à un consensus malgré les objections de l'un d'entre eux, objection qui a été étudiée et rejetée. Le « dissident » fait appel à une centaine de personnes, parfois des participants à d'autres groupes de travail mais souvent des employés de la même société que lui, qui ne connaissent rien au sujet mais font ce qu'on leur dit. Ils envoient tous un message sur la liste de diffusion, disant qu'ils soutiennent l'objection. Une bonne façon de traiter ces soutiens vides (sans contenu nouveau) est de leur demander « Nous avons déjà discuté de ce point et abouti à un consensus. Avez-vous des éléments techniques nouveaux qui justifieraient de rouvrir la discussion ? » En général, les commerciaux et marketeux qui avaient été recrutés pour faire masse dans la discussion ne répondent pas... Ils croyaient que l'IETF fonctionnait sur un système de vote et sont surpris de découvrir qu'on leur demande d'argumenter. Bien sûr, ce cas est un peu extrême mais la même méthode peut servir dans des cas moins dramatiques. Le principe du consensus sert à traiter une des plus grosses failles des systèmes de vote : le bourrage des urnes par des ignorants.


Téléchargez le RFC 7282


L'article seul

Exposé « Tous à poil » à Pas Sage en Seine

Première rédaction de cet article le 27 juin 2014
Dernière mise à jour le 30 juin 2014


Le 26 juin, j'ai eu le plaisir de livrer quelques réflexions sur l'utilisation de la transparence pour la sécurité des protocoles réseaux, à l'évènement « Pas Sage en Seine » à Paris, l'évènement qui rassemble tous les regards critiques et novateurs sur le numérique, l'Internet et bien d'autres choses. Sous le titre de « Tous à poil » (titre choisi pour des raisons de SEO), je parlais de bitcoin, de Namecoin, d'Usenet et même de BGP.

Voici les supports de cet exposé :

Parmi les exemples de systèmes de sécurité fonctionnant sur la transparence, j'ai oublié de mentionner le Stamper. J'aurais dû. Pour les autres sujets que je mentionnais :


L'article seul

RFC 7276: An Overview of Operations, Administration, and Maintenance (OAM) Tools

Date de publication du RFC : Juin 2014
Auteur(s) du RFC : T. Mizrahi (Marvell), N. Sprecher (Nokia Solutions and Networks), E. Bellagamba (Ericsson), Y. Weingarten
Pour information
Réalisé dans le cadre du groupe de travail IETF opsawg
Première rédaction de cet article le 25 juin 2014


Ce RFC est entièrement consacré à la gestion de réseaux. Quels sont les outils existants dans les réseaux TCP/IP, notamment l'Internet ? Ce RFC est destiné aux opérateurs réseau, aux fournisseurs d'équipements et de logiciels réseau, et aux gens de la normalisation, pour s'assurer qu'ils ont une base d'information commune sur les outils existants.

La gestion de réseaux est ici désignée par le sigle OAM qui signifie Operations, Administration, and Maintenance. OAM est défini dans le RFC 6291. Les outils de l'OAM sont tous les dispositifs qui permettent de détecter un problème et de l'isoler, ou bien de mesurer les performances. (Les outils permettant de configurer les équipements réseaux ne sont pas traités ici.) Ce nouveau RFC couvre les outils utilisés dans des contextes très divers, d'IP à MPLS en passant par TRILL mais je ne vais parler que des outils IP, ceux que je connais.

Ce RFC décrit les outils, logiciels mettant en œuvre un protocole particulier. Une liste des protocoles normalisés par l'IETF pour l'OAM est dans le RFC 6632. Par exemple, OWAMP (RFC 4656) est un protocole, le programme distribué par Internet 2 est un outil mettant en œuvre ce protocole.

Tous les réseaux sont bien sûr gérés, et tous ont évidemment des dispositifs d'OAM, plus ou moins perfectionnés. On a donc :

  • O (pour Operations) : tout ce qui contribue à ce que le réseau reste « up & running » en détectant les problèmes (et en les résolvant) avant même que l'utilisateur ne s'en aperçoive.
  • A (pour Administration) : garder à jour la liste des ressources et surveiller leur utilisation.
  • M (pour Maintenance) : permettre les réparations et les évolutions (mise à jour du logiciel du routeur, par exemple).

Parfois, ces dispositifs d'OAM ont été ajoutés après, en utilisant (souvent de manière créative) les fonctions disponibles. De nos jours, on tend à prévoir l'OAM dès le début, comme ce fut le cas pour l'ATM.

Notre RFC commence (section 2) par des rappels de concept et de terminologie (outre le RFC 6291, déjà cité, le RFC 7087 définit les termes spécifiques à MPLS). D'abord, données vs. contrôle : on distingue souvent dans un équipement réseau le sous-système des données (data plane ou user plane) et le sous-système de contrôle (control plane). Le premier désigne toutes les fonctions qui contribuent à transmettre les paquets (et donc les données) à l'équipement suivant. Le second, le sous-système de contrôle, comprend les fonctions qui permettent d'établir dynamiquement les informations dont le sous-système des données va avoir besoin. Par exemple, dans un routeur, le sous-système des données est composé des fonctions qui font passer le paquet d'une interface d'entrée à une interface de sortie (rôle de forwarding), alors que le sous-système de contrôle (rôle de routing) comprend notamment les fonctions de calcul de la table de routage (les mises en œuvre d'OSPF, de BGP, etc), table de routage qu'utilisera le sous-système des données lors de la transmission. Si le routeur est une machine Unix, le sous-système de données est mis en œuvre dans le noyau, alors que celui de contrôle l'est par un démon séparé, genre Quagga ou BIRD. La distinction entre sous-systèmes de contrôle et de données n'est pas seulement conceptuelle, ni limitée au logiciel. Dans un routeur Juniper ou Cisco moyen ou haut de gamme, c'est même du matériel différent (ASIC pour les données, CPU généraliste pour le contrôle). L'OAM, tel qu'utilisé ici, mesure et teste le sous-système de données, mais il peut utiliser des informations fournies par le sous-système de contrôle, et être piloté par lui. (On voit parfois, par exemple dans le RFC 6123, un troisième sous-système, le sous-système de gestion, le management plane. De toute façon, la différence entre ces sous-systèmes n'est pas forcément claire et nette dans tous les cas.)

Encore un peu de terminologie : les outils peuvent être « à la demande » ou bien « proactifs ». Les premiers sont déclenchés lorsqu'il y a un problème (c'est l'administrateur réseaux à qui on dit « le serveur ne marche pas » et qui lance ping tout de suite), les seconds tournent en permanence, par exemple pour détecter et signaler les pannes.

Quelle sont les fonctions précises d'un logiciel d'OAM ? Notre RFC cite le test de la connectivité, la découverte du chemin, la localisation d'un problème, et des mesures liées aux performances comme la mesure du taux de perte de paquets, ou comme la mesure des délais d'acheminement.

On entend parfois parler de « tests de continuité » et de « tests de connectivité ». Ces termes viennent de MPLS (RFC 5860) et désignent, dans le premier cas, un test que les paquets peuvent passer et, dans le second, que deux systèmes sont effectivement connectés, et par le chemin attendu (IP, étant sans connexion, n'a guère besoin de ces tests de connectivité).

En parlant de connexion, le monde TCP/IP comprend des protocoles avec connexion et des protocoles sans. L'OAM concerne ces deux groupes et les outils présentés dans ce RFC couvrent les deux. Un test utilisant un protocole sans connexion (ping est l'exemple archétypal) peut emprunter un autre chemin, d'autres ressources (et donc donner un autre résultat), qu'une utilisation réelle du réseau. Au contraire, en testant un protocole avec connexion (TCP, par exemple), on utilise les mêmes ressources que les vraies données. Notez aussi que des protocoles sans connexion peuvent être testés par des protocoles avec connexion, le RFC donnant l'exemple d'un test d'IP fait avec BFD (RFC 5880).

Enfin, dernier point de terminologie, les mots utilisés pour les problèmes. Il n'existe pas une norme unique et acceptée pour ces mots. Suivant la norme ITU-T G.806, notre RFC fait une différence entre défaillance (fault), qui est l'incapacité à effectuer une tâche, comme de livrer un paquet, interruption (defect), qui est un arrêt temporaire d'un service et panne (failure). Cette dernière, au contraire de l'interruption, n'est pas un accident ponctuel et elle peut durer longtemps.

Bien, après tous ces préliminaires, les outils eux-mêmes, présentés en section 4 du RFC. À tout seigneur, tout honneur, on va évidemment commence par l'outil emblématique de l'OAM, ping. Il envoie un paquet ICMP de type « demande d'écho » et la réception d'un paquet ICMP de réponse indiquera que la connectivité est complète. Il donnera également quelques indicateurs de performance comme le RTT, affiché après time= :

% ping -c 3 www.ietf.org
PING www.ietf.org (4.31.198.44) 56(84) bytes of data.
64 bytes from mail.ietf.org (4.31.198.44): icmp_req=1 ttl=44 time=227 ms
64 bytes from mail.ietf.org (4.31.198.44): icmp_req=2 ttl=44 time=222 ms
64 bytes from mail.ietf.org (4.31.198.44): icmp_req=3 ttl=44 time=223 ms

--- www.ietf.org ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 222.689/224.685/227.533/2.067 ms

Certaines versions de ping peuvent faire des tests avec UDP plutôt qu'ICMP. ping donne un point de vue unilatéral, celui du côté qui initie la connexion. Si on veut le point de vue du pair, il faut que celui-ci fasse un test de son côté (par exemple, s'il y a un pare-feu à état entre les deux machines, un des tests peut échouer même si l'autre réussit). Malheureusement, il arrive que ces paquets ICMP soient filtrés (par exemple, suite à l'incompétence de l'administrateur qui croit que l'attaque mal nommée ping of death a un rapport avec ping alors qu'elle n'en a pas).

Après ping, l'outil d'OAM le plus connu et le plus utilisé est sans doute traceroute. Comme ping, il n'y a pas de texte sacré de référence le décrivant. À l'époque, on codait d'abord, on documentait ensuite (parfois). Les textes les plus canoniques sur traceroute semblent être les RFC 1470 et RFC 2151.

traceroute sert à découvrir le chemin pris par les paquets entre l'initiateur de la requête et une cible. Il envoie un paquet UDP à destination du port 33434 en mettant dans l'en-tête IP un TTL inhabituel, de 1. Vu avec tcpdump, cela donne (notez le ttl 1) :

22:10:20.369307 IP (tos 0x0, ttl 1, id 19164, offset 0, flags [none], proto UDP (17), length 60)
    192.168.2.1.44024 > 213.154.224.1.33434: UDP, length 32

Le premier routeur rencontré va donc considérer ce paquet comme trop ancien, le jeter, et envoyer un paquet ICMP de type Time exceeded à la source, qui aura ainsi l'adresse IP du premier routeur :

22:17:46.359084 IP (tos 0xc0, ttl 64, id 64893, offset 0, flags [none], proto ICMP (1), length 88)
    192.168.2.254 > 192.168.2.1: ICMP time exceeded in-transit, length 68
	IP (tos 0x0, ttl 1, id 19239, offset 0, flags [none], proto UDP (17), length 60)
    192.168.2.1.34315 > 213.154.224.1.33434: UDP, length 32

Ensuite, traceroute itère : il incrémente le numéro de port et le TTL et trouve ainsi le deuxième routeur. Le jeu s'arrête lorsque traceroute reçoit un paquet ICMP Port unreachable lorsque le paquet UDP atteint sa cible et que celle-ci, n'ayant pas de service sur ce port, répondra négativement :

22:19:20.648790 IP (tos 0x0, ttl 53, id 11453, offset 0, flags [none], proto ICMP (1), length 56)
    213.136.31.102 > 192.168.2.1: ICMP 213.154.224.1 udp port 33445 unreachable, length 36
	IP (tos 0x0, ttl 1, id 19325, offset 0, flags [none], proto UDP (17), length 60)
    192.168.2.1.57471 > 213.154.224.1.33445: UDP, length 32

Utilisant toutes ces informations, traceroute peut alors afficher le chemin :

% traceroute -q 1 www.nlnetlabs.nl
traceroute to www.nlnetlabs.nl (213.154.224.1), 30 hops max, 60 byte packets
 1  freebox (192.168.2.254)  13.646 ms
 2  88.189.152.254 (88.189.152.254)  37.136 ms
 3  78.254.1.62 (78.254.1.62)  37.133 ms
 4  rke75-1-v900.intf.nra.proxad.net (78.254.255.42)  46.845 ms
 5  cev75-1-v902.intf.nra.proxad.net (78.254.255.46)  39.030 ms
 6  p16-6k-1-po12.intf.nra.proxad.net (78.254.255.50)  43.658 ms
 7  bzn-crs16-1-be1024.intf.routers.proxad.net (212.27.56.149)  43.662 ms
 8  bzn-crs16-1-be1106.intf.routers.proxad.net (212.27.59.101)  43.637 ms
 9  londres-6k-1-po101.intf.routers.proxad.net (212.27.51.186)  55.973 ms
10  *
11  amsix-501.xe-0-0-0.jun1.bit-1.network.bit.nl (195.69.144.200)  60.693 ms
12  nlnetlabs-bit-gw.nlnetlabs.nl (213.136.31.102)  62.287 ms

(On note que le routeur n° 10 a refusé de répondre, ce qui arrive, ou que sa réponse s'est perdue, ce qui arrive aussi.)

Sur une machine Unix d'aujourd'hui, il existe souvent plusieurs mises en œuvre différentes de traceroute. traceroute est une idée, pas un programme unique. Par exemple, Paris traceroute fait varier l'en-tête IP pour essayer de découvrir plusieurs chemins lorsqu'il y a de l'ECMP. Des traceroutes ont des options pour utiliser ICMP et pas UDP (c'est le cas des sondes Atlas). Il existe des extensions à ICMP pour rendre traceroute plus utile, comme celles du RFC 4950 ou RFC 5837.

Attention aux limites de traceroute : il ne montre que le chemin aller. Le routage sur l'Internet pouvant être asymétrique, rien ne dit que le chemin de retour sera le même.

Après, le RFC passe à des outils moins connus du grand public. D'abord, les outils BFD. Normalisé dans le RFC 5880, BFD permet de tester la connectivité dans le contexte d'IP (RFC 5881) mais aussi de MPLS (RFC 5884) et de bien d'autres. Il fonctionne en mode connecté : les deux machines établissent une session BFD. Ensuite, elles échangent des paquets BFD, soit périodiquement, soit à la demande. La bonne arrivée de ces paquets indique que la connexion avec le pair fonctionne. Il existe aussi un mode où BFD envoie des paquets « demande d'écho » et attend une réponse.

Il existe ensuite plusieurs outils d'OAM pour le monde MPLS, le plus important étant LSP-Ping, décrit dans le RFC 4379. S'il assure des fonctions analogues à ping, comme son nom l'indique, il en fait aussi qui sont équivalentes à traceroute. Répondre à une demande d'écho est moins trivial en MPLS qu'en IP. Par exemple, un chemin (un LSP, Label Switched Path) peut n'être établi que dans une seule direction, rendant impossible de répondre en MPLS à un LSP-Ping.

Les autres outils MPLS sont décrits en détail dans les sections 4.4 et 4.5 de notre RFC. Sinon, la problématique générale de l'OAM en environnement MPLS est dans les RFC 4377 et RFC 4378. (L'OAM de MPLS a d'ailleurs suscité un affrontement entre l'IETF et l'UIT, le point de vue de l'IETF étant exposé dans le RFC 5704.)

ping sur un réseau IP a plusieurs limites : il mesure un aller-retour (alors que les chemins peuvent être différents, avec des caractéristiques très différentes), et il n'est pas défini avec une grande précision donc la sémantique du RTT, par exemple, n'est pas rigoureuse. L'IETF, via son groupe de travail IPPM, a deux protocoles qui traitent ces limites, OWAMP et TWAMP. Outre des définitions rigoureuses de métriques comme le délai d'acheminement aller-simple (RFC 2679), ou comme la connectivité (RFC 2678), IPPM a produit ces deux protocoles qui établissent une session de contrôle entre deux points, sur TCP, avant de lancer une mesure précise avec UDP. OWAMP (RFC 4656) mesure un aller-simple alors que TWAMP (RFC 5357) mesure un aller-retour (comme ping).

À noter que, pour mesurer les performances, il existe bien d'autres outils comme les outils OAM d'Ethernet.

Enfin, la section 4 se termine par une section sur TRILL (RFC 6325). Trop récent, ce système n'a pas encore beaucoup d'outils OAM. Il existe un cahier des charges pour ces outils, le RFC 6905, qui demande des outils pour tester la connectivité, découvrir le chemin suivant et mesurer des indicateurs comme le temps d'acheminement d'un paquet.

La section 5 du RFC résume tous les outils décrits, sous forme d'un tableau donnant le nom des outils, un résumé de leur fonctionnement, et le protocole auquel ils s'appliquent (IP, MPLS, TRILL, etc). Pratique pour les lecteurs pressés.

Il reste enfin la classique section de sécurité, la section 6. Un système d'OAM soulève évidemment des enjeux de sécurité. Par exemple, un attaquant qui contrôle l'OAM pourrait créer l'illusion de pannes non-existantes, forçant les équipes du réseau à perdre du temps. Ou il pourrait empêcher la détection de pannes réelles, ce qui aggraverait le déni de service.

Certains des outils d'OAM présentés dans ce RFC ont des fonctions de sécurité, empêchant par exemple la modification des données par un tiers. BFD a un mécanisme d'authentification. OWAMP et TWAMP peuvent authentifier (via un HMAC) et/ou chiffrer leurs communications (en AES). Ceci dit, la confidentialité n'est pas en général considérée comme un problème pour l'OAM, seules l'authentification et l'intégrité le sont. D'autres outils n'ont aucune sécurité (traceroute peut facilement être trompé, comme l'avait montré The Pirate Bay).

Les outils d'OAM peuvent aussi être utilisés pour la reconnaissance : par exemple, l'option -g de fping ou la possibilité d'indiquer un préfixe IP (et pas juste une adresse) à nmap, permettent de découvrir les machines existant sur le réseau.

L'annexe A termine le RFC en énumérant une (longue) liste de documents de normalisation sur l'OAM, y compris des documents non-IETF.


Téléchargez le RFC 7276


L'article seul

RFC 7269: NAT64 Deployment Options and Experience

Date de publication du RFC : Juin 2014
Auteur(s) du RFC : G. Chen, Z. Cao (China Mobile), C. Xie (China Telecom), D. Binet (France Telecom-Orange)
Pour information
Réalisé dans le cadre du groupe de travail IETF v6ops
Première rédaction de cet article le 20 juin 2014


L'épuisement des adresses IPv4 étant allé plus vite que le déploiement d'IPv6 (qui est freiné par la passivité de nombreux acteurs), il y a de plus en plus souvent le problème de connecter des réseaux modernes, entièrement en IPv6, à des machines anciennes restées en IPv4. La solution de l'IETF à ce problème est le déploiement de NAT64, normalisé dans le RFC 6144. Ce nouveau RFC documente l'expérience concrète de trois gros opérateurs avec NAT64. Qu'est-ce qui a marché, qu'est-ce qui a raté ?

Ces réseaux entièrement IPv6 ont l'avantage de ne pas avoir de problème de manque d'adresses IP (même les préfixes du RFC 1918 peuvent être insuffisants pour un grand réseau) et de permettre d'utiliser un plan d'adressage propre dès le début (pas de contorsions pour faire durer les adresses) et une architecture unique (un seul protocole à gérer). Pour les réseaux mobiles, par exemple, cela signifie un seul contexte PDP, ce qui simplifie les choses. Mais, comme vu plus haut, leur inconvénient est qu'ils ne peuvent pas accéder aux services qui restent archaïquement en IPv4 seul comme Twitter ou impots.gouv.fr. Et, même si les professionnels sérieux ont compris depuis longtemps l'importance de migrer vers IPv6, les résistances des plus attardés vont sans doute durer longtemps et on peut penser que des réseaux uniquement IPv4 seront encore en fonctionnement pendant de longues années. Au lieu du plan de transition envisagé au début (« tout le monde en IPv4 » -> « double-pile - IPv4 et IPv6 - déployée progressivement » -> « tout le monde en IPv6 »), il faut maintenant travailler dans le cadre d'un nouveau plan, « tout le monde en IPv4 » -> « certains réseaux en v4 seul et certains en v6 seul » -> « tout le monde en IPv6 ». L'étape intermédiaire a été décrite dans les RFC 6145 et RFC 6146 et son composant le plus connu est nommé NAT64. NAT64 effectue une traduction d'adresses de IPv6 vers IPv4 en sortie du réseau moderne vers le réseau archaïque et l'inverse lorsque la réponse arrive.

NAT64 a donc une partie des inconvénients du NAT IPv4 traditionnel (NAT44), comme le montre le compte-rendu d'expérience du RFC 6586 mais il parait incontournable, sauf si le déploiement d'IPv6 s'accélérait subitement dans l'Internet, ce qu'on ne voit pas venir.

30 % des opérateurs, selon un sondage signalé dans le RFC 6036 prévoient de déployer NAT64 ou un système équivalent. NAT64 est également systématiquement présent lors des réunions des organismes de normalisation ou de gestion des ressources réseau, comme l'IETF ou le RIPE. À chaque réunion, un des réseaux WiFi est « IPv6 seulement » et bénéficie d'une passerelle NAT64 vers l'extérieur. De nombreux experts peuvent ainsi tester en vrai les technologies modernes. Bref, NAT64 est désormais une technique importante et il est donc nécessaire d'étudier ses conséquences pratiques. C'est le rôle de ce nouveau RFC, qui ne décrit pas un nouveau protocole mais offre simplement un retour d'expérience.

À noter que NAT64 peut être déployé en deux endroits :

  • Côté FAI, ce qu'on appelle le NAT64-CGN (pour Carrier-Grade NAT), sous forme d'un gros traducteur NAT dans le réseau du FAI,
  • Ou bien côté fournisseur de services/contenus, ce qu'on appelle le NAT64-FE (pour Front End), sous forme d'un traducteur NAT dans le data center, devant les serveurs.

Le premier cas est celui du FAI tout en IPv6 mais qui veut permettre à ses clients d'accéder à des services IPv4. Le second est celui du fournisseur de services ou de contenus dont l'infrastructure est malheureusement entièrement ou partiellement en IPv4 mais qui veut fournir un accès IPv6, en ne gérant qu'un traducteur, au lieu de mettre à jour tous ses systèmes. (Le RFC ne discute pas la question de savoir si c'est une idée intelligente ou pas...)

À noter que le cas d'un NAT64 chez l'utilisateur (le célèbre M. Michu), dans son CPE, n'est pas mentionné. Un NAT64 dans le CPE imposerait un réseau du FAI qui soit en IPv4 jusqu'au client, justement ce qu'on voudrait éviter. Le RFC recommande plutôt de mettre les traducteurs NAT64 près de la sortie du FAI, de façon à permettre à celui-ci de conserver un réseau propre, entièrement IPv6 (section 3.1.3). Certes, cela va concentrer une bonne partie du trafic dans ces traducteurs mais NAT64 est fondé sur l'idée que la proportion de trafic IPv4 va diminuer avec le temps : les traducteurs n'auront pas besoin de grossir. Aujourd'hui, 43 des cent plus gros sites Web (selon Alexa) ont déjà un AAAA et court-circuitent donc complètement le NAT64.

Autre problème avec la centralisation, le risque de SPOF. Si le CPE de M. Michu plante, cela n'affecte que M. Michu. Si un gros traducteur NAT64, gérant des milliers de clients, plante, cela va faire mal. Il faut donc soigner la disponibilité (voir section 4).

Centraliser dans un petit nombre de traducteurs près de la sortie évite également d'avoir plusieurs équipements NAT64, avec les problèmes de traçabilité que cela poserait, par exemple s'ils ont des formats de journaux différents.

NAT64 nécessite un résolveur DNS spécial, utilisant DNS64 (RFC 6147). En effet, les clients, n'ayant qu'IPv6, ne demanderont que des enregistrements AAAA (adresses IPv6) dans le DNS. Le résolveur DNS64 doit donc en synthétiser si le site original (par exemple twitter.com) n'en a pas. Dans certains cas, par exemple une page Web où les liens contiennent des adresses IPv4 littérales (comme <a href="http://192.0.2.80/">), la solution 464xlat du RFC 6877 permet de se passer de DNS64. Mais, dans la plupart des déploiements NAT64, DNS64 sera indispensable.

NAT64 va probablement coexister avec NAT44 (le NAT majoritaire aujourd'hui), les réseaux locaux chez l'utilisateur gardant un certain nombre de machines IPv4. Si le RFC 6724 donne par défaut la priorité à IPv6, quelque soit le type de connectivité IPv6, les algorithmes comme celui des globes oculaires heureux (RFC 6555) peuvent mener à préférer IPv6 ou IPv4 et même à en changer d'un moment à l'autre, en fonction de leurs performances respectives. Ces changements ne seront pas forcément bons pour l'expérience utilisateur.

Pour le NAT64-FE (juste en face des serveurs), le RFC 6883 donnait quelques indications, qui peuvent s'appliquer à NAT64. Ici, DNS64 n'est pas forcément nécessaire, les adresses IPv6 servant à adresser les serveurs sont en nombre limité et peuvent être mises dans le DNS normal.

La section 4 de notre RFC revient sur l'exigence de haute disponibilité, essentielle si on veut que le passage de IPv4+IPv6 à IPv6+NAT64 ne diminue pas la fiabilité du service. Je me souviens d'une réunion RIPE où il n'y avait qu'un seul résolveur DNS64 et, un matin, il avait décidé de faire grève. DNS64 étant indispensable à NAT64, les clients étaient fort marris. Mais la redondance des résolveurs DNS est un problème connu, et relativement simple puisqu'ils n'ont pas d'état. Le RFC se focalise surtout sur la haute disponibilité des traducteurs NAT64 car eux peuvent avoir un état (comme les traducteurs NAT44 d'aujourd'hui). Il y a trois types de haute disponibilité : le remplaçant froid, qui est un traducteur NAT64 inactif en temps normal, et qui ne se synchronise pas avec le traducteur actif. Il est typiquement activé manuellement quand le traducteur actif tombe en panne. Comme il n'a pas de copie de l'état (la table des sessions en cours, aussi nommée BIB pour Binding Information Base), toutes les connexions casseront et devront être réétablies. Le second type est le remplaçant tiède. Il ne synchronise pas les sessions mais il remplace automatiquement le traducteur NAT64 principal, par exemple grâce à VRRP (RFC 5798). Au cours des tests faits par les auteurs du RFC, il fallait une minute pour que VRRP bascule et que les trente millions de sessions de la BIB soient établies à nouveau. Enfin, il y a le remplaçant chaud, qui synchronise en permanence l'état des sessions. Cette fois, la bascule vers le remplaçant est indétectable par les clients. Pour s'apercevoir rapidement de la panne du traducteur principal, le test a utilisé BFD (RFC 5880) en combinaison avec VRRP. Cette fois, la bascule des trente millions de sessions n'a pris que trente-cinq millisecondes, soit une quasi-continuité de service. Évidemment, cela nécessite du logiciel plus complexe dans les traducteurs, surtout vu l'ampleur de la tâche : avec 200 000 utilisateurs, il faut créer et détruire environ 800 000 sessions par seconde ! Un lien à 10 Gb/s entre les traducteurs n'est pas de trop, pour transporter les données nécessaires pendant les pics de trafic.

Alors, remplaçant froid, tiède ou chaud ? Notre RFC ne tranche pas définitivement, disant que cela dépend des applications. (L'annexe A du RFC contient des tests spécifiques à certaines applications.) Environ 90 % du trafic pendant les tests était dû à HTTP. Ce protocole n'a pas besoin de continuité de service, chaque requête HTTP étant indépendante. Le traducteur chaud n'est donc pas indispensable si HTTP continue à dominer (au passage, c'est pour une raison analogue que la mobilité IP n'a jamais été massivement utilisée). En revanche, le streaming souffrirait davantage en cas de coupure des sessions. (Curieusement, le RFC cite aussi le pair-à-pair comme intolérant aux coupures de session, alors que les clients BitTorrent, par exemple, s'en tirent très bien et réétablissent automatiquement des sessions.)

Le trafic d'un traducteur NAT64-CGN peut être intense et, même en l'absence de pannes, il est raisonnable de prévoir plusieurs traducteurs, afin de répartir la charge. Attention, si on fait du NAT64 avec état (le plus courant), il faut s'assurer que les paquets d'une session donnée arrivent toujours au même traducteur. Une façon de distribuer les requêtes entre les traducteurs, pour le NAT64-CGN, est de demander au serveur DNS64 de synthétiser des préfixes différents selon le client, et d'envoyer les différents préfixes à différents traducteurs, en utilisant le classique routage IP. Pour le NAT64-FE, les techniques classiques des répartiteurs de charge (condensation de certains paramètres puis répartition en fonction du condensat) conviennent.

La section 5 du RFC se penche ensuite sur le problème de l'adresse IP source. À partir du moment où on fait du NAT (quel que soit le NAT utilisé), l'adresse IP source vue par le destinataire n'est pas celle de l'émetteur mais celle du traducteur NAT. Cela entraine tout un tas de problèmes. D'abord, la traçabilité. Un serveur voit un comportement illégal ou asocial (par exemple, pour un serveur HTTP, des requêtes identiques coûteuses répétées, pour réaliser une attaque par déni de service). Son administrateur note l'adresse IP du coupable. Qu'en fait-il ? Pour pouvoir remonter de cette adresse IP au client individuel, il faut garder les journaux du traducteur NAT (et aussi, le RFC l'oublie, que le serveur note l'heure exacte et le port, comme rappelé par le RFC 6302). Les auteurs du RFC ont tenté l'expérience avec 200 000 clients humains pendant 60 jours. Les informations sur les sessions étaient transmises par syslog (RFC 5424) à une station d'archivage. Avec toutes les informations conservées (heure, protocole de transport, adresse IPv6 source originelle - et son port, adresse IPv4 source traduite - et son port), soit 125 octets, les 72 000 sessions par seconde ont produit pas moins de 29 téra-octets de données. La seule activité de journalisation, pour faire plaisir au CSA, nécessite donc une infrastructure dédiée (machines, disques et liens) et coûteuse.

Le système pourrait toutefois être amélioré. Au lieu de noter le port pour chaque session, on pourrait allouer une plage de ports à chaque client et réduire ainsi le volume à journaliser (seule l'allocation de la plage serait notée ; ceci dit, cela compliquera sérieusement le logiciel de recherche, qui aura moins de données à fouiller mais davantage de calculs à faire). On peut même statiquement allouer des plages de port à chaque client, supprimant ainsi une grande partie de la journalisation. Mais attention, ces méthodes d'optimisation ont deux gros défauts : elles diminuent l'efficacité du multiplexage (certains client vont tomber à court de ports alors que d'autres en auront des inutilisés) et elle est contraire aux recommandations de sécurité du RFC 6056, qui prescrit des ports sources difficiles à deviner, pour compliquer la tâche d'un attaquant.

Autre problème lié à la traduction d'adresses, la géolocalisation. Un serveur qui géolocalise en se fondant sur l'adresse IP source trouvera la position du traducteur, pas celle de l'utilisateur. Parfois, cela peut être gênant. Le RFC 6967 cite quelques solutions possibles, mais aucune n'est parfaite : on peut par exemple se servir d'informations applicatives comme l'en-tête Forwarded: de HTTP (RFC 7239), ce qui ne marche évidemment qu'avec ce protocole.

La section 6 revient vers les problèmes de M. Michu, heureux utilisateur, sans le savoir, d'un traducteur NAT64. Quelle va être la qualité de son vécu d'utilisateur ? Ou, dit plus trivialement, est-ce que ça va marcher ou planter ? Certaines applications fonctionnent facilement et simplement à travers n'importe quel NAT, comme HTTP ou SSH. D'autres nécessitent un relais, une ALG. Les traducteurs NAT64 fournissent en général une ALG pour FTP (RFC 6384). Même pour les protocoles qui marchent « tout seuls » à travers NAT64, une ALG peut être utile pour la traçabilité, par exemple insérer des en-têtes Via: dans une requête HTTP ou Received: dans un message transmis en SMTP.

En testant des applications réelles, comme l'avait fait le RFC 6586, les auteurs de ce RFC ont trouvé que plusieurs continuent à planter, ce qui plaiderait pour l'ajout à NAT64 des fonctions 464xlat du RFC 6877. Ainsi, SIP n'a pas réussi à appeler depuis la machine IPv6 pure vers un softphone en IPv4 pur (les adresses IP sont transmises dans la signalisation, et ne sont pas traduites par NAT64). Il faut donc, comme recommandé par le RFC 6157, que les clients SIP utilisent ICE (RFC 5245), qui a l'avantage de traiter également le problème des pare-feux.

Pour IPsec, le bilan est également mitigé : comme prévu, AH n'a pas fonctionné (puisque son but est d'empêcher toute modification des en-têtes, donc des adresses IP source et destination), mais ESP non plus, les paquets du protocole de couche 4 50, le protocole d'ESP, n'étant pas traduits. Utiliser ESP sur UDP, comme décrit dans le RFC 3947, aurait probablement résolu ce problème.

Le tableau de la section 6.1 résume les applications testées et les résultats :

  • Web : pas de problème, sauf les rares cas où un URL inclut une adresse IPv4 littérale,
  • Messagerie instantanée : la plupart des services testés ont échoué (mon expérience personnelle est que XMPP et IRC fonctionnent très bien à travers NAT64, donc je suppose qu'il s'agissait de protocoles fermés et de logiciels privateurs),
  • Jeux : les jeux non-Web ont presque tous échoué,
  • SIP : échec,
  • IPsec : échec (vor ci-dessus),
  • Partage de fichiers en pair-à-pair : échec, par exemple avec eMule,
  • Courrier électronique : pas de problème,
  • FTP : pas de problème.

L'annexe A décrit en outre les délais acceptables pour ces applications, lorsque le traducteur NAT64 tombe en panne et est remplacé. Les jeux interactifs, par exemple, ne supportent pas les longues interruptions.

Comme un traducteur NAT64 gène certains services, il serait utile de fournir aux clients un moyen de contrôler, par exemple, l'ouverture de ports entrants, les ports sortants affectés, etc. Il existe un protocole standard pour cela, PCP (RFC 6887), qui serait un ajout très utile aux traducteurs NAT64 mais, pour l'instant, aucun n'en dispose encore.

La section 7 du RFC se penche ensuite sur les problèmes de MTU. Certes, il n'y a pas de tunnel avec NAT64, mais il peut y avoir des liens IPv4 dont la MTU est inférieure à 1 280 octets, le minimum des liens IPv6. Il n'y a pas de moyen simple, en NAT64-CGN, de gérer ce problème.

Bon, et la sécurité ? La section 9 étudie la question. NAT64 fait du suivi des sessions TCP et est donc normalement à l'abri de pas mal de types d'attaques par déni de service. On peut renforcer sa sécurité en déployant RPF (RFC 3704), pour empêcher les usurpations d'adresses IPv6.

Pour NAT64-FE, une attaque possible serait de remplir la table des sessions en commençant plein de sessions vers les serveurs situés derrière le traducteur NAT64-FE. Celui-ci doit donc avoir un mécanisme de limitation de trafic.

Ah, et puis il faut rappeler qu'un résolveur DNS64, étant un résolveur menteur (pour la bonne cause, certes, mais menteur tout de même), s'entend mal avec DNSSEC. Le RFC 6147 contient de nombreuses notes à ce sujet.

Ce RFC est issu d'un processus qui a commencé par un exposé à l'IETF. Il y a eu quelques objections de principe (un certain nombre de participants à l'IETF estiment que toute traduction est une erreur, NAT64 comme les autres).


Téléchargez le RFC 7269


L'article seul

Version 7 d'Unicode

Première rédaction de cet article le 19 juin 2014


Le 16 juin a vu la sortie d'une nouvelle version du jeu de caractères Unicode, la 7.0. On peut trouver une description des principaux changements en http://www.unicode.org/versions/Unicode7.0.0/ 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  
--------
 113021

Plus de 113 000 caractères. Lesquels ont été apportés par la version 7 ?


ucd=> SELECT version,count(version) FROM Characters GROUP BY version ORDER BY version;
 version | count 
---------+-------
...
 6.0     |  2088
 6.1     |   732
 6.2     |     1
 6.3     |     5
 7.0     |  2834

2 834 nouveaux. Lesquels ?

ucd=> SELECT To_U(codepoint) AS Codepoint, name FROM Characters WHERE version='7.0';
 codepoint |                                    name                                    
-----------+----------------------------------------------------------------------------
...
 U+2B5E    | BENT ARROW POINTING DOWNWARDS THEN NORTH EAST
...
 U+10350   | OLD PERMIC LETTER AN
...
 U+10500   | ELBASAN LETTER A
...
 U+10600   | LINEAR A SIGN AB001
...
 U+10880   | NABATAEAN LETTER FINAL ALEPH
...
 U+1236F   | CUNEIFORM SIGN KAP ELAMITE
...
 U+1F57D   | RIGHT HAND TELEPHONE RECEIVER
...
 U+1F6F2   | DIESEL LOCOMOTIVE
...

Comme on le voit, c'est varié. On trouve vingt-trois nouveaux alphabets, ayant souvent cessés d'être utilisés, et intégrés à Unicode surtout pour les recherches historiques, comme par exemple le vieux permien, l'elbasan, le nabatéen ou le Linéaire A, toujours pas déchiffré (ce qui explique les noms des caractères, qui sont des chiffres sans sémantique). Il y a aussi l'ajout de plus de mille nouveaux caractères cunéiformes.

La partie la plus spectaculaire, mais pas forcément la plus utile, est l'ajout de nombreux symboles, comme les innombrables flèches (comme la U+2B5E ci-dessus), ou la locomotive Diesel U+1F6F2... À ceux qui disent « digital » pour numérique (comme les innombrables « agences digitales » qui font de la « stratégie digitale »), on pourra désormais répondre avec l'image du caractère U+1F595, « REVERSED HAND WITH MIDDLE FINGER EXTENDED »... Pour mieux apprécier ces nouveaux symboles (avec des représentations graphiques), vous pouvez regarder la liste de ceux de la série U+1F300 et suivants... Si vous avez les bonnes polices de caractères, voici les caractères pris en exemple plus haut : ⭞ 𐍐 𐔀 𐘀 𐢀 𒍯 🕽 🛲 🖕 ... Une autre solution est de se servir d'UniView qui a été mis à jour avec les données de la version 7. Quant aux noms officiels en français de tous ces caractères, ils sont en ligne.


L'article seul

RFC 7211: Operations Model for Router Keying

Date de publication du RFC : Juin 2014
Auteur(s) du RFC : S. Hartman (Painless Security), D. Zhang (Huawei)
Pour information
Réalisé dans le cadre du groupe de travail IETF karp
Première rédaction de cet article le 19 juin 2014


Il y a un gros travail en cours à l'IETF pour améliorer la sécurité du routage sur l'Internet. L'un des aspects de ce travail concerne la gestion des clés par les routeurs. Bien des solutions de sécurité nécessitent que les routeurs disposent de clés secrètes. Le groupe de travail KARP travaille sur les problèmes de gestion de ces clés (cf. RFC 6518). Ce nouveau RFC du groupe décrit les problèmes opérationnels et les pratiques actuelles de gestion des clés dans les routeurs.

KARP a déjà produit plusieurs RFC. Celui-ci se veut particulièrement terre-à-terre. Il existe des tas de techniques pour sécuriser la communication entre routeurs. Mais comment sont-elles effectivement déployées ? Pour le savoir, il faut disposer d'un modèle décrivant les opérations de gestion de clés. C'est d'autant plus crucial que la sécurisation du routage présente quelques défis spécifiques. Par exemple, comme il faut que le routage fonctionne pour pouvoir contacter d'autres machines du réseau, les solutions fondées sur un serveur d'authentification central ne conviennent pas.

D'abord, le modèle de configuration d'un routeur, en section 3. La configuration consiste à indiquer au routeur les clés qu'il utilisera pour s'authentifier, ainsi que divers paramètres. Cela peut se faire manuellement ou automatiquement. La configuration automatique étant encore un sujet d'étude, notre RFC se concentre sur la configuration manuelle, via une structure abstraite, la table des clés. Ce n'est déjà pas évident car certains protocoles nécessitent une clé commune à tous les routeurs, et d'autres pas. Il faut donc pouvoir configurer les clés par interface réseau et par pair. Prenons l'exemple d'OSPF (RFC 2328) : sur tout lien réseau, tous les routeurs doivent utiliser la même clé. En revanche, d'un lien à l'autre, un même routeur peut se trouver à utiliser plusieurs clés différentes. Mais ce n'est pas obligatoire et, dans certaines organisations, on aura peut-être une clé pour chaque zone (area) OSPF, avec seuls les ABR (Area Border Router) utilisant plusieurs clés. Bien sûr, avec une table de clés OSPF où les clés sont rangées par lien, on peut réaliser la politique « une clé par zone » en mettant la même clé pour tous les liens de la zone. Mais, si ce n'est pas vérifié par le système de gestion de la table de clés, il y a un risque de désynchronisation, par exemple parce qu'on change la clé d'un lien en oubliant les autres liens de la zone. Il serait donc souhaitable d'avoir un mécanisme d'héritage (« la clé du lien dépend de la zone ») permettant d'éviter cette désynchronisation accidentelle.

L'intégrité de la table des clés par rapport aux règles techniques des protocoles de routage est évidemment essentielle. Le routeur doit donc s'assurer que les algorithmes de cryptographie indiqués sont compatibles avec le protocole de routage, que la fonction de dérivation l'est également, etc. La table peut être modifiée via une quantité de méthodes (système de gestion de configuration, interface Web, CLI, etc) du moment que son intégrité est vérifiée.

Afin d'éviter les « clés éternelles », jamais changées (même lorsqu'un employé qui les connait quitte l'organisation), il faut aussi que le table prévoit un système d'expiration, pour pouvoir indiquer « cette clé est valable jusqu'au 15 avril 2014 » et qu'elle soit automatiquement marquée comme expirée après cette date.

Il existe des tas de façons d'utiliser des clés en cryptographie. La plus simple (et sans doute la plus courante aujourd'hui) est que les clés configurées dans le routeur soient partagées, et utilisées telles quelles dans le protocole de routage. Mais on peut aussi passer par une fonction de dérivation, qui va combiner la clé partagée avec des paramètres d'une session particulière, rendant ainsi la tâche plus difficile pour l'attaquant (même s'il a la clé, il ne pourra pas forcément s'insérer dans une session en cours). C'est par exemple ce que fait le protocole AO du RFC 5925 (dans sa section 5.2). Ou bien on peut se servir des clés préconfigurées pour s'authentifier le temps de générer des clés de session, par exemple avec Diffie-Hellman. Les clés sont parfois identifiées par un nombre court, parfois pas identifiées du tout et un routeur peut avoir à en essayer plusieurs avant de tomber sur la bonne.

Enfin, il y a le cas où les routeurs utilisent de la cryptographie asymétrique. La clé privée de chaque routeur ne quittera alors jamais le routeur, ce qui augmente la sécurité. (Ceci dit, cela empêchera de remplacer rapidement un routeur, ce qui est une opération souhaitable, et qui justifie donc une gestion centrale des clés.) L'authentification des autres routeurs pourra se faire via une PKI (ce qui peut être compliqué à mettre en place), ou bien en ajoutant la clé publique de chaque routeur à tous ses pairs (ce qui n'est pas pratique si on ajoute souvent de nouveaux routeurs). Pour identifier les clés, notre RFC recommande de se servir du RFC 4572 (section 5). Ensuite, les autorisations de tel ou tel routeur pair peuvent se faire en indiquant les clés (ce qui a l'inconvénient qu'il faut les refaire si un routeur change de clé) ou en indiquant une identité du routeur (ce qui nécessite, pour cette indirection supplémentaire, un moyen sécurisé de passer de l'identité du routeur à sa ou ses clés). Avec une PKI, le problème ne se posera évidemment pas.

On l'a vu, un problème spécifique à la sécurisation du routage est qu'il faut que le mécanisme marche sans dépendre du réseau puisque, avant que les routeurs soient authentifiés et échangent les routes, on n'aura peut-être pas de réseau. Cela limite sérieusement le rôle que peuvent jouer des serveurs centraux, genre RADIUS (RFC 2865). Pas question par exemple que le seul moyen d'obtenir les clés des pairs soit de demander à un serveur central. Cela ne veut pas dire que ces serveurs centraux soient inutiles. Ils peuvent aider à des opérations comme la diffusion de nouvelles clés en cas de préparation à un remplacement de clés ou comme la distribution de la clé d'un nouveau pair. Simplement, le réseau doit pouvoir démarrer et fonctionner sans eux.

La section 6 de notre RFC est consacrée au rôle de l'administrateur humain dans la gestion des clés. Deux exemples typiques où il est indispensable, l'arrivée d'un nouveau pair (il faut décider si on le laisse entrer ou pas) et la réparation en cas de problème. Si le réseau fonctionne déjà, l'arrivée d'un nouveau pair peut être simplifiée car il peut suffire d'inscrire ce routeur dans une base de données et toutes les informations nécessaires peuvent être propagées aux autres routeurs en utilisant le réseau. Le cas d'un réseau tout nouveau démarrant à froid est évidemment plus compliqué.

Naturellement, le nouveau routeur doit être proprement configuré sur le plan de la sécurité ; il ne servirait à rien de protéger les clés utilisées par les protocoles de routage si on pouvait se connecter en SSH sur le routeur avec login: admin password: admin !

Et les pannes ? C'est que la sécurité apporte ses propres causes de panne. Vouloir sécuriser un réseau peut l'empêcher de fonctionner. Ainsi, l'expiration d'un certificat entraîne son refus par les autres routeurs et le retrait du routeur au certificat trop vieux... Même si le RFC n'en parle pas, une des raisons du faible déploiement des solutions de sécurisation du routage est la crainte d'avoir davantage de pannes. Le déploiement de toute solution de sécurité nécessitera des pratiques plus rigoureuses, par exemple une supervision des dates d'expiration des certificats.

Et une fois qu'on a décidé de déployer une jolie sécurité toute neuve avec une meilleure gestion des lettres de créance des routeurs, comment on fait ? La section 7 fait remarquer que le passage du système actuel au nouveau ne va pas forcément être sans douleur. Si on conçoit un réseau tout neuf, on peut le faire proprement dès le début. Mais si on a un réseau existant et qu'on veut y introduire de nouvelles pratiques ? Et sans tout casser ? Aujourd'hui, si des clés comme celles d'OSPF ou bien celles utilisées entre routeurs BGP sont très rarement changées, c'est souvent par peur que ce changement coupe le routage. Pour deux routeurs BGP appartenant à des organisations différentes, le changement du mot de passe MD5 (RFC 2385) doit être soigneusement coordonné et il faut que le pair respecte rigoureusement la procédure qui a été définie, notamment le moment du changement. Comme on s'en doute, c'est rarement fait.

Prenons l'exemple du nouveau mécanisme AO (RFC 5925) qui remplace l'authentification MD5. Il n'existe pas de mécanisme automatique pour savoir si le pair BGP utilise MD5 ou AO. Il faut donc un changement coordonné (« le 11 mars à 13h37 UTC, on passe à AO »). Si le logiciel utilisé permet de grouper les configurations en traitant tout un ensemble de pairs de la même façon, il faut en prime faire cela pour tous les pairs à la fois. Vu cette difficulté, il n'est pas étonnant que peu de sessions BGP soient aujourd'hui protégées par AO.

Enfin, le rappelle la section 8, beaucoup de mécanismes de sécurité dépendent d'une horloge à l'heure. Il est donc crucial de s'assurer que l'heure sur les routeurs est correctement synchronisée.


Téléchargez le RFC 7211


L'article seul

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

Syndication : Flux Atom avec seulement les résumés et Flux Atom avec tout le contenu