Je suis Charlie

Autres trucs

Accueil

Seulement les RFC

Seulement les fiches de lecture

Mon livre « Cyberstructure »

Ève

Déboguer les zones DNS signées avec DNSSEC

Première rédaction de cet article le 26 novembre 2008
Dernière mise à jour le 10 juin 2009


La sécurité est un aller et retour permanent entre trop et pas assez. Trop de sécurité et on ne peut plus rien faire, pas assez et les ennuis nous tombent dessus. Le DNS n'échappe pas à cette dialectique fondamentale. Pour se protéger de l'empoisonnement des résolveurs DNS, on pense à déployer DNSSEC. Oui, mais qui va déboguer les innombrables problèmes que cela va causer et comment ?

La vulnérabilité du DNS aux empoisonnements avec des données fausses est bien connue depuis longtemps (voir par exemple le RFC 3833). Elle est particulièrement prise en compte depuis la révélation de la faille Kaminsky. Pour faire face à cette vulnérabilité, un certain nombre d'experts prônent le déploiement d'une technique de signature cryptographique, DNSSEC, normalisée dans les RFC 4033, RFC 4034 et RFC 4035.

Il existe une longue expérience de déploiement de la cryptographie sur l'Internet et l'une des leçons apprises est que des ennuis se produisent inévitablement. Bogues dans les logiciels et erreurs de procédures de la part des humains font que la cryptographie fonctionne souvent comme une serrure : elle gêne les méchants mais elle peut aussi bloquer les gentils. Normalement, le DNS est très résistant aux erreurs de configuration : il faut vraiment le faire exprès pour rendre une zone complètement inatteignable. Avec DNSSEC, on perd cette robustesse : une erreur et la zone, quoique atteignable, ne sera pas validée et les résolveurs refuseront de transmettre les données. Il est donc crucial d'apprendre à déboguer DNSSEC.

Commençons par un cas banal à l'heure actuelle. On a un résolveur DNS qui valide avec DNSSEC (dans BIND, cela se fait avec l'option dnssec-validatation yes;) et, un matin, une zone DNS n'est plus accessible. Le résolveur, interrogé avec dig dit juste Server failure :


% dig +dnssec SOA example.net

; <<>> DiG 9.5.0-P2 <<>> +dnssec @dnssec SOA example.net
; (1 server found)
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 31180
                                       ^^^^^^^^
                                       Ici
...

Si on a accès aux journaux de ce résolveur et s'il est configuré en mode suffisamment bavard, on pourra y trouver des détails sur la cause du problème. Mais il faut noter que les résolveurs Unbound et (surtout) BIND ont en commun que les messages ainsi journalisés sont peu compréhensibles, et nécessitent souvent la lecture des RFC pour être décodés.

À l'autre extrémité du spectre de la complexité, une solution entièrement cliquodromesque est le vérificateur du registre de .se, http://dnscheck.iis.se/. Si la zone est disponible publiquement, il peut l'analyser en détail et produire des diagnostics compréhensibles par exemple :

DNSSEC signature expired: RRSIG(example.net/IN/DNSKEY/8850)
Expired signatures will be ignored by validating resolvers.

Cet outil est également distribué, sous une licence libre, pour ceux qui veulent le faire tourner chez eux. Un autre excellent outil de vérification du DNS, Zonecheck, peut faire des tests DNSSEC depuis sa version 3.

Et entre les deux ? Si on est prêt à utiliser quelques outils Unix mais qu'on n'a pas accès au journal du résolveur, ou bien qu'on ne comprend rien à ses messages ? Mark Andrews, responsable de la version actuelle de BIND, aime dire qu'on peut « déboguer DNSSEC uniquement avec dig et date ». Est-ce vrai ?

Voyons ce qu'on peut obtenir avec dig. Par défaut, la plupart des résolveurs validateurs ne distribuent aucune donnée lorsque la signature est invalide. Cela rend difficile le débogage aussi faut-il demander gentiment au résolveur d'envoyer quand même les données invalides, avec l'option +cd :

% dig +dnssec +cd SOA example.net
...
;; ANSWER SECTION:
example.net.            86400   IN      SOA     ns1.example.net. hostmaster.example.net. 2008102101 3600 900 3600000 900
example.net.            86400   IN      RRSIG   SOA 5 2 86400 20081120064157 20081021064157 8850 example.net. QWzZD+N2Y3qCoosWI7T/x3Rlqr06bZXyZhDv6h/L7PxxPXedV63gTz+b q5Rw8p/46rqkBa+YTwAH/050iZMfrKDQALh+0aLI1CJpRaiAUPyXN/hf Dc5kapJmgIW9eSD17hmbKz8Qp29WriqefPTToll4MC1TtBJm1M7QSE2e Y6Q=

Le premier enregistrement, le SOA est ce que nous avions demandé, le second, RRSIG est la signature cryptographique. La majorité est du binaire, que je ne chercherai pas à lire mais certaines données sont en clair, notamment le key ID (ici 8850) et les dates de validité de la signature.

En effet, pour empêcher un attaquant de « rejouer » de vieux enregistrements, les RRSIG ont une date de début et une date de fin de validité. (La période de validité est de un mois, par défaut, si on signe avec l'outil dnssec-signzone de BIND.) dig affiche cette date dans un format quasiment lisible, décrit par les section 3.2 et 3.1.5 du RFC 4034 : YYYYMMDDHHmmSS en UTC. Ici, la date de fin de validité est 20081120064157, donc le 20 novembre 2008 à 06h41 UTC. Comme nous sommes le 26 novembre, pas besoin de chercher plus loin : la signature a expiré. (Pour afficher la date du jour au format identique à celui de dig, sur un Unix qui a GNU date comme Debian, on peut faire date -u +%Y%m%d%H%M%S.)

En effet, puisque les signatures ont une durée de vie limitée, il faut re-signer la zone périodiquement. Les futures version de BIND le feront automatiquement. Mais, en attendant, il faut mettre dans son crontab quelque chose comme :

dnssec-signzone -N increment example.net

À noter qu'il existe aussi des appliances DNSSEC comme celle de Secure64 qui, normalement, vous dispensent de cette tâche.

Mais, pour l'instant, il faut bien dire que la signature expirée est la cause la plus fréquente de problèmes DNSSEC. Tellement qu'elle vaut la peine d'une surveillance spécifique. Par exemple, le script check-sig vérifie un nom de domaine et affiche un message d'erreur si sa signature est expirée ou bien si elle le sera bientôt (sept jours par défaut) :

% check-sig example.net
% 

% check-sig SOA example.net
Name example.net has an expired signature (20081120064157)
%

% check-sig example.org
Name example.org has a signature that will expire in less than 7 days (20081201224442)
%

Il peut donc être mis dans une crontab pour donner l'alarme lorsqu'une signature risque d'expirer. (Merci à Erwan David, Thomas Parmelan, Pierre Beyssac, Phil Regnauld et tlhackque pour sa mise au point.)

Et la cause suivante, en nombre d'erreurs ? Probablement les incohérences dans la délégation. DNSSEC repose, comme le DNS, sur un modèle hiérarchique. Une racine délègue à des zones qui délèguent à des sous-zones et ainsi de suite. Chacune de ces délégations, matérialisées par un enregistrement DS (RFC 4034, section 5), est évidemment signée. La principale différence avec le DNS est que la racine de signature peut être différente de la racine tout court, grâce à DLV (RFC 5074, technique désormais abandonnée). Ainsi, à l'heure actuelle, la plupart des zones signées sont délégués depuis le registre DLV de l'ISC, ce qui n'est plus le cas depuis. (Cf. RFC 8749.)

Or, des problèmes peuvent survenir lors des délégations puisque elles impliquent deux organisations différentes. Par exemple, un administrateur de zone décide de signer car c'est à la mode puis arrête de le faire mais oublie qu'un enregistrement DLV pointe vers lui (et donc garantit que la zone devrait être signée). Ou bien l'administrateur modifie sa clé et oublie de prévenir la zone au-dessus. De telles contradictions entre la zone parente et la zone fille sont fréquentes dans le DNS d'aujourd'hui mais, avec DNSSEC, elles ne pardonnent pas.

Supposons donc que la zone example.net ne fonctionne pas : nous ne récupérons que le Server Failure. Regardons sa clé (l'option +multi rend l'affichage des clés plus agréable) :

% dig +dnssec +cd +multi DNSKEY example.net
...
;; ANSWER SECTION:
example.net.              366 IN DNSKEY 257 3 5 (
                                AwEAAc4x/KbNECb+dpDDBSvyxfTlvUxXyC3EAqCnXDp4
                                +IxjfmwCm1QfB/VIMfqQ2bSsEu51BPk/38dBG01COvE5
                                tYit/Ux8gIuDgZiJx+ldZ9OAJ3Lnm7v/q5+gy2LSzW46
                                p6U45WHmGnDZ4c3uhzcfooXmQsW4UmIw+zDc2ePADy3M
                                bkr3Srll3XDny1OHoW6Ch4o8qC+ezzRDSEnhrtpon+r9
                                4sqXF50k6OLaqCRB3q9iaGUgviTVfZWJIlvZOwvxxpbH
                                SDd6QThM/CZBzcx/8JHAwP7MJcUQYS8XvBwRdaAfVDuE
                                FjUj6IF+vgn8PI1ipQUrF8L0OAHf1dHBou1XjuE=
                                ) ; key id = 17398

Sa clé est la 17398. Est-elle bien chez le parent ? On demande à celui-ci :

% dig @addr-server-parent +dnssec +cd +multi DS example.net.
...
;; ANSWER SECTION:
example.net.              1800 IN DS 6732 5 1 (
                                BBDDDD272C4D81EF941C722CEF79A848B543502D )

Oui, il y a bien un enregistrement DS mais pour une autre clé, la 6732. La chaîne de confiance est cassée là. Sans doute un changement de clé effectué sans prévenir le parent.

Attention, il est courant d'avoir plusieurs clés et il faut donc les regarder toutes.

Avec DLV, même principe. Ma zone sources.org est signée et enregistrée dans DLV à l'ISC, ce qu'on peut voir avec :

% dig @ns-ext.isc.org +multi DLV sources.org.dlv.isc.org
...
;; ANSWER SECTION:
sources.org.dlv.isc.org. 3600 IN DLV 22107 5 2 (
                                AF12A23DFBCDB5609DCEC2C2FBD3CD65AEEFE49CBE07
                                51C65C71C983986B7DE5 )
sources.org.dlv.isc.org. 3600 IN DLV 14347 3 1 (
                                31FF6986A07DAC3642C18606FC992F6ED403A873 )
sources.org.dlv.isc.org. 3600 IN DLV 14347 3 2 (
                                0D5D5B209264BBA5EDAEC9B95843901073BF27F01702
                                B144FFC1389D747DAB7F )
sources.org.dlv.isc.org. 3600 IN DLV 22107 5 1 (
                                EA78D532118A6C2B3C95447A7E520FF3B16FE775 )

Il y a deux clés, les 14347 et 22107, utilisant deux algorithmes différents, d'où les quatre DLV. Ici, tout va donc bien.

Et si la recherche d'une clé dans la zone ne donnait rien ? C'est que la zone n'est pas signée. Actuellement, c'est l'écrasante majorité. Sauf s'il existe une délégation DNSSEC depuis le parent (la zone serait alors invalide), ces zones ne doivent pas être refusées, sauf extrême paranoïa. Si elles déclenchent un Server Failure, c'est qu'il y a une raison non-DNSSEC à cela.

On peut tester ces techniques avec la zone test.dnssec-tools.org où se trouvent de nombreux enregistrements DNSSEC cassés volontairement. Par exemple, futuredate-A.newzsk-ns.test.dnssec-tools.org a une date des signatures valide seulement dans le futur... Même chose avec un autre domaine cassé exprès, dnssec-failed.org.

Bien sûr, un million d'autres choses peuvent tomber en panne. Par exemple, DNSSEC dépend d'EDNS0, puisque les réponses DNSSEC sont typiquement plus grosses. L'expérience prouve qu'un certain nombre de pare-feux bogués interceptent les paquets avec EDNS0 ou bien qu'un certain nombre d'administrateurs incompétents configurent leur pare-feu pour rejeter les paquets DNS de taille supérieur à 512 octets. Il faut donc également être prêt à déboguer la configuration réseau.

On trouve de nombreuses indications pratiques sur le débogage de DNSSEC dans le DNSSEC HOWTO, a tutorial in disguise ou bien dans les transparents DNSSEC in 6 minutes. Merci à Mohsen Souissi pour le débogage du texte et à Mark Andrews pour ses exemples de déboguage sur la liste bind-users.

Version PDF de cette page (mais vous pouvez aussi imprimer depuis votre navigateur, il y a une feuille de style prévue pour cela)

Source XML de cette page (cette page est distribuée sous les termes de la licence GFDL)