Voilà une expression un peu effrayante, « la négation d'existence
authentifiée ». C'est en fait un terme utilisé dans le
C'est que
DNSSEC fonctionne en
Tous les exemples du RFC sont faits avec une petite zone d'exemple,
example.org. SOA ( ... )
NS a.example.org.
a.example.org. A 192.0.2.1
TXT "a record"
d.example.org. A 192.0.2.1
TXT "d record"
Chargeons-la dans NSD pour voir et pouvoir
reproduire les tests (le serveur va écouter sur le
> DiG 9.9.3-rpz2+rl.13214.22-P2-Ubuntu-1:9.9.3.dfsg.P2-4ubuntu1.1 <<>> @::1 -p 9053 TXT a.example.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46585
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;a.example.org. IN TXT
;; ANSWER SECTION:
a.example.org. 3600 IN TXT "a record"
;; AUTHORITY SECTION:
example.org. 3600 IN NS a.example.org.
;; ADDITIONAL SECTION:
a.example.org. 3600 IN A 192.0.2.1
;; Query time: 1 msec
;; SERVER: ::1#9053(::1)
;; WHEN: Mon Jan 20 18:46:25 CET 2014
;; MSG SIZE rcvd: 93
]]>
On a bien le code de retour
> DiG 9.9.3-rpz2+rl.13214.22-P2-Ubuntu-1:9.9.3.dfsg.P2-4ubuntu1.1 <<>> @::1 -p 9053 TXT b.example.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 10555
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;b.example.org. IN TXT
;; AUTHORITY SECTION:
example.org. 3600 IN SOA a.example.org. root.example.org. (
2014012000 ; serial
604800 ; refresh (1 week)
86400 ; retry (1 day)
2419200 ; expire (4 weeks)
86400 ; minimum (1 day)
)
;; Query time: 0 msec
;; SERVER: ::1#9053(::1)
;; WHEN: Mon Jan 20 18:48:18 CET 2014
;; MSG SIZE rcvd: 85
]]>
On a bien un
Il y a un autre cas où la négation d'existence est nécessaire :
lorsque le nom existe mais qu'il n'y a pas de données de ce type. Par
exemple, si je demande une adresse
> DiG 9.9.3-rpz2+rl.13214.22-P2-Ubuntu-1:9.9.3.dfsg.P2-4ubuntu1.1 <<>> @::1 -p 9053 AAAA a.example.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55053
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;a.example.org. IN AAAA
;; AUTHORITY SECTION:
example.org. 3600 IN SOA a.example.org. root.example.org. (
2014012000 ; serial
604800 ; refresh (1 week)
86400 ; retry (1 day)
2419200 ; expire (4 weeks)
86400 ; minimum (1 day)
)
;; Query time: 0 msec
;; SERVER: ::1#9053(::1)
;; WHEN: Thu Jan 23 10:07:06 CET 2014
;; MSG SIZE rcvd: 83
]]>
Ici,
Maintenant, passons en
La solution adoptée par DNSSEC a été de
« signer les trous ». Un enregistrement spécial définit un intervalle
entre deux noms, signifiant « il n'y a rien entre ces deux noms ». Et
cet enregistrement est signé et peut donc être vérifié. Au début, cet
enregistrement spécial se nommait le
Le
[On crée une clé]
% dnssec-keygen -a RSASHA256 -f KSK -b 4096 example.org
Generating key pair..................................................................................................................................................................................................................................++ .............................................++
Kexample.org.+008+37300
[On édite le fichier de zone pour mettre "$INCLUDE
Kexample.org.+008+37300.key"]
[On signe]
% dnssec-signzone -z example.org
dnssec-signzone: warning: example.org:3: no TTL specified; using SOA MINTTL instead
Verifying the zone using the following algorithms: RSASHA256.
Zone fully signed:
Algorithm: RSASHA256: KSKs: 1 active, 0 stand-by, 0 revoked
ZSKs: 0 active, 0 stand-by, 0 revoked
example.org.signed
Examinons le fichier
% grep NSEC example.org.signed
example.org. 86400 IN NSEC a.example.org. NS SOA RRSIG NSEC DNSKEY
example.org. 86400 IN RRSIG NSEC 8 2 86400 20140222083601 20140123083601 37300 example.org. J9kDD...
a.example.org. 86400 IN NSEC d.example.org. A TXT RRSIG NSEC
a.example.org. 86400 IN RRSIG NSEC 8 3 86400 20140222083601 20140123083601 37300 example.org. IVc/rrR...
d.example.org. 86400 IN NSEC example.org. A TXT RRSIG NSEC
d.example.org. 86400 IN RRSIG NSEC 8 3 86400 20140222083601 20140123083601 37300 example.org. b/DHgE...
(J'ai un peu triché, j'ai mis des noms absolus partout avec
Chargeons cette zone signée et interrogeons-la pour un nom
inexistant :
> DiG 9.9.3-rpz2+rl.13214.22-P2-Ubuntu-1:9.9.3.dfsg.P2-4ubuntu1.1 <<>> @::1 -p 9053 A b.example.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 16186
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;b.example.org. IN A
;; AUTHORITY SECTION:
a.example.org. 86400 IN NSEC d.example.org. A TXT RRSIG NSEC
a.example.org. 86400 IN RRSIG NSEC 8 3 86400 (
20140222083601 20140123083601 37300 example.org.
IVc/rr...
example.org. 86400 IN NSEC a.example.org. NS SOA RRSIG NSEC DNSKEY
example.org. 86400 IN RRSIG NSEC 8 2 86400 (
20140222083601 20140123083601 37300 example.org.
J9kDDpefX...
example.org. 86400 IN SOA a.example.org. root.example.org. (
2014012000 ; serial
604800 ; refresh (1 week)
86400 ; retry (1 day)
2419200 ; expire (4 weeks)
86400 ; minimum (1 day)
)
example.org. 86400 IN RRSIG SOA 8 2 86400 (
20140222083601 20140123083601 37300 example.org.
QcFJIEpCs...
;; Query time: 0 msec
;; SERVER: ::1#9053(::1)
;; WHEN: Thu Jan 23 10:45:38 CET 2014
;; MSG SIZE rcvd: 1821
]]>
On obtient logiquement un
Et avec un nom existant mais un type de données qui ne l'est pas :
> DiG 9.9.3-rpz2+rl.13214.22-P2-Ubuntu-1:9.9.3.dfsg.P2-4ubuntu1.1 <<>> @::1 -p 9053 AAAA a.example.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 15504
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;a.example.org. IN AAAA
;; AUTHORITY SECTION:
example.org. 86400 IN SOA a.example.org. root.example.org. (
2014012000 ; serial
604800 ; refresh (1 week)
86400 ; retry (1 day)
2419200 ; expire (4 weeks)
86400 ; minimum (1 day)
)
example.org. 86400 IN RRSIG SOA 8 2 86400 (
20140222083601 20140123083601 37300 example.org.
QcFJIE...
a.example.org. 86400 IN NSEC d.example.org. A TXT RRSIG NSEC
a.example.org. 86400 IN RRSIG NSEC 8 3 86400 (
20140222083601 20140123083601 37300 example.org.
IVc/rrRD5...
;; Query time: 0 msec
;; SERVER: ::1#9053(::1)
;; WHEN: Thu Jan 23 10:56:28 CET 2014
;; MSG SIZE rcvd: 1228
]]>
Ls
Bon, tout cela est très joli mais les
% dig @::1 -p 9053 A $RANDOM.example.org
...
example.org. 86400 IN NSEC a.example.org.
[Donc, le nom suivant est a.example.org. On continue avec un type de
données rare.]
% dig @::1 -p 9053 NAPTR a.example.org
...
a.example.org. 86400 IN NSEC d.example.org. ...
[On a le nom suivant, d.example.org, plus qu'à continuer]
Ce
Le second inconvénient des
La solution à ces deux inconvénients est
Par contre,
Il est donc temps de passer à
Donc, le principe de NSEC3 : lorsqu'on signe, on met un
enregistrement NSEC3 pour le
Pour tester
% dnssec-signzone -3 $(echo $RANDOM$RANDOM | sha256sum | cut -d' ' -f1) -H 2 \
-z example.org
dnssec-signzone: warning: example.org:3: no TTL specified; using SOA MINTTL instead
Verifying the zone using the following algorithms: RSASHA256.
Zone fully signed:
Algorithm: RSASHA256: KSKs: 1 active, 0 stand-by, 0 revoked
ZSKs: 0 active, 0 stand-by, 0 revoked
example.org.signed
Et, lorsque je charge cette zone et que je demande un nom
non-existant, je récupère des
> DiG 9.9.3-rpz2+rl.13214.22-P2-Ubuntu-1:9.9.3.dfsg.P2-4ubuntu1.1 <<>> @::1 -p 9053 A b.example.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 7591
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 8, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;b.example.org. IN A
;; AUTHORITY SECTION:
ui6pc9ajfb1e6ge0grul67qnckig9bck.example.org. 86400 IN NSEC3 1 0 2 DD438FBA32EC3FFA4B1849EF2F41F64A83A17D220D22F57BC9903300A861BFE9 (
ASPD8T7IP6MGQ09OPQQP3KMH9D7VVODA
A TXT RRSIG )
ui6pc9ajfb1e6ge0grul67qnckig9bck.example.org. 86400 IN RRSIG NSEC3 8 3 86400 (
20140223065356 20140124065356 37300 example.org.
PIb9Y8T...
l6m3op8qm1vr3t47jnm6dbl6s4qm2bl8.example.org. 86400 IN NSEC3 1 0 2 DD438FBA32EC3FFA4B1849EF2F41F64A83A17D220D22F57BC9903300A861BFE9 (
UI6PC9AJFB1E6GE0GRUL67QNCKIG9BCK
NS SOA RRSIG DNSKEY NSEC3PARAM )
l6m3op8qm1vr3t47jnm6dbl6s4qm2bl8.example.org. 86400 IN RRSIG NSEC3 8 3 86400 (
20140223065356 20140124065356 37300 example.org.
WKf8OkW...
aspd8t7ip6mgq09opqqp3kmh9d7vvoda.example.org. 86400 IN NSEC3 1 0 2 DD438FBA32EC3FFA4B1849EF2F41F64A83A17D220D22F57BC9903300A861BFE9 (
L6M3OP8QM1VR3T47JNM6DBL6S4QM2BL8
A TXT RRSIG )
aspd8t7ip6mgq09opqqp3kmh9d7vvoda.example.org. 86400 IN RRSIG NSEC3 8 3 86400 (
20140223065356 20140124065356 37300 example.org.
YrR7/0vxWT...
[SOA omis...]
;; Query time: 0 msec
;; SERVER: ::1#9053(::1)
;; WHEN: Fri Jan 24 08:57:43 CET 2014
;; MSG SIZE rcvd: 2639
]]>
À quel nom correspondent ces condensats ? On va utiliser le programme
% dig +short @::1 -p 9053 NSEC3PARAM example.org
1 0 2 DD438FBA32EC3FFA4B1849EF2F41F64A83A17D220D22F57BC9903300A861BFE9
...
On voit donc le nombre d'itérations à
faire et la valeur du sel (ce n'est pas le même sel que dans les
exemples du RFC, ne vous étonnez donc pas si les valeurs sont
différentes ; celles du RFC sont résumées dans l'annexe C). Testons avec l'apex :
% nsec3hash DD438FBA32EC3FFA4B1849EF2F41F64A83A17D220D22F57BC9903300A861BFE9 1 2 example.org
L6M3OP8QM1VR3T47JNM6DBL6S4QM2BL8 (salt=DD438FBA32EC3FFA4B1849EF2F41F64A83A17D220D22F57BC9903300A861BFE9, hash=1, iterations=2)
Et pareil pour les autres noms qu'on connait. On peut donc trouver le
l6m3op8qm1vr3t47jnm6dbl6s4qm2bl8.example.org. 86400 IN NSEC3 1 0 2 DD438FBA32EC3FFA4B1849EF2F41F64A83A17D220D22F57BC9903300A861BFE9 (
UI6PC9AJFB1E6GE0GRUL67QNCKIG9BCK
NS SOA RRSIG DNSKEY NSEC3PARAM )
Avec à la fin la liste des types qui existent pour ce nom. Et avec
dans la valeur le condensat suivant,
Ici, on n'a pas utilisé l'
>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 38993
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 8, ADDITIONAL: 1
...
9GVSHUTCOU5FH6SKL75VBHIJQ6KLB8HR.fr. 5346 IN NSEC3 1 1 1 BE174A99 9GVTDB9F17RKAOJKIK7GIH3EHS89GGJ6 NS SOA TXT NAPTR RRSIG DNSKEY NSEC3PARAM
...
]]>
Le deuxième chiffre, qui est à 1 et plus à 0 comme dans
l'exemple précédent, montre que
NSEC3 peut encore peut-être paraître simple comme cela mais la norme est très complexe et on y découvre encore des cas bizarres, pas ou mal prévus comme l'erratum 3441.
Maintenant, le problème difficile, les jokers (section
5.3). Ajoutons dans notre zone d'exemple :
*.example.org. TXT "wildcard record"
Si un client DNS demande le
> DiG 9.9.3-rpz2+rl.13214.22-P2-Ubuntu-1:9.9.3.dfsg.P2-4ubuntu1.1 <<>> -p 9053 @::1 TXT z.example.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49955
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 2, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;z.example.org. IN TXT
;; ANSWER SECTION:
z.example.org. 86400 IN TXT "wildcard record"
z.example.org. 86400 IN RRSIG TXT 8 2 86400 (
20140227062210 20140128062210 37300 example.org.
G8THJnlRf...
;; AUTHORITY SECTION:
d.example.org. 86400 IN NSEC example.org. A TXT RRSIG NSEC
d.example.org. 86400 IN RRSIG NSEC 8 3 86400 (
20140227062210 20140128062210 37300 example.org.
JoewWY...
;; Query time: 0 msec
;; SERVER: ::1#9053(::1)
;; WHEN: Tue Jan 28 08:23:49 CET 2014
;; MSG SIZE rcvd: 1215
]]>
On reçoit une réponse. Comment savoir qu'elle vient d'un joker ? C'est
grâce à un champ discret de la signature, dont je n'ai pas encore
parlé, le champ
L'enregistrement
Et avec
> DiG 9.9.3-rpz2+rl.13214.22-P2-Ubuntu-1:9.9.3.dfsg.P2-4ubuntu1.1 <<>> -p 9053 @::1 TXT z.example.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10172
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 2, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;z.example.org. IN TXT
;; ANSWER SECTION:
z.example.org. 86400 IN TXT "wildcard record"
z.example.org. 86400 IN RRSIG TXT 8 2 86400 (
20140223162550 20140124162550 37300 example.org.
i69nAYi...
;; AUTHORITY SECTION:
l6m3op8qm1vr3t47jnm6dbl6s4qm2bl8.example.org. 86400 IN NSEC3 1 0 2 DD438FBA32EC3FFA4B1849EF2F41F64A83A17D220D22F57BC9903300A861BFE9 (
UI6PC9AJFB1E6GE0GRUL67QNCKIG9BCK
NS SOA RRSIG DNSKEY NSEC3PARAM )
l6m3op8qm1vr3t47jnm6dbl6s4qm2bl8.example.org. 86400 IN RRSIG NSEC3 8 3 86400 (
20140223162550 20140124162550 37300 example.org.
ZbfNK6xD++...
;; Query time: 0 msec
;; SERVER: ::1#9053(::1)
;; WHEN: Tue Jan 28 08:15:15 CET 2014
;; MSG SIZE rcvd: 1292
]]>
L'enregistrement
Je vous épargne (et à moi aussi), la section 5.4 sur les
Plus utile (mais pas facile) est la section 5.5, sur la notion de
« boîte la plus proche » (
> DiG 9.9.3-rpz2+rl.13214.22-P2-Ubuntu-1:9.9.3.dfsg.P2-4ubuntu1.1 <<>> @::1 -p 9053 A x.2.example.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 52476
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 8, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;x.2.example.org. IN A
;; AUTHORITY SECTION:
ui6pc9ajfb1e6ge0grul67qnckig9bck.example.org. 86400 IN NSEC3 1 0 2 DD438FBA32EC3FFA4B1849EF2F41F64A83A17D220D22F57BC9903300A861BFE9 (
ASPD8T7IP6MGQ09OPQQP3KMH9D7VVODA
A TXT RRSIG )
ui6pc9ajfb1e6ge0grul67qnckig9bck.example.org. 86400 IN RRSIG NSEC3 8 3 86400 (
20140227063446 20140128063446 37300 example.org.
eqpl3Vp...
...
l6m3op8qm1vr3t47jnm6dbl6s4qm2bl8.example.org. 86400 IN NSEC3 1 0 2 DD438FBA32EC3FFA4B1849EF2F41F64A83A17D220D22F57BC9903300A861BFE9 (
UI6PC9AJFB1E6GE0GRUL67QNCKIG9BCK
NS SOA RRSIG DNSKEY NSEC3PARAM )
l6m3op8qm1vr3t47jnm6dbl6s4qm2bl8.example.org. 86400 IN RRSIG NSEC3 8 3 86400 (
20140227063446 20140128063446 37300 example.org.
GaK0HrQ...
je5k991emd1g7f9p37a3ajr96jrl0fvi.example.org. 86400 IN NSEC3 1 0 2 DD438FBA32EC3FFA4B1849EF2F41F64A83A17D220D22F57BC9903300A861BFE9 (
L6M3OP8QM1VR3T47JNM6DBL6S4QM2BL8
TXT RRSIG )
je5k991emd1g7f9p37a3ajr96jrl0fvi.example.org. 86400 IN RRSIG NSEC3 8 3 86400 (
20140227063446 20140128063446 37300 example.org.
U5zFverPJT...
;; Query time: 0 msec
;; SERVER: ::1#9053(::1)
;; WHEN: Tue Feb 04 18:10:08 CET 2014
;; MSG SIZE rcvd: 2641
]]>
Si vous voulez suivre les explications qui suivent, vous devez
calculer les condensats des noms. Les voici déjà calculés (avec mon
sel ; avec le sel du RFC, voir l'annexe C qui résume les valeurs) :
Noms dans la zone :
L6M3OP8QM1VR3T47JNM6DBL6S4QM2BL8 : apex
UI6PC9AJFB1E6GE0GRUL67QNCKIG9BCK : a.example.org
ASPD8T7IP6MGQ09OPQQP3KMH9D7VVODA : d.example.org
JE5K991EMD1G7F9P37A3AJR96JRL0FVI : *.example.org
Noms dans la requête :
C9PVNA4KK7P5CH0L8C5RFDDI377RMJTO : x.2.example.org
4TPLI2PU4PVISAQ3OK4BNE06FL1ESDKJ : 2.example.org
TIIAIPBQRUI8LQOQVUOTC1RGS4VR3BA5 : z.example.org
Ici, il a fallu pas moins de trois
Sinon, si vous aimez lire le code source, le RFC recommande celui d'
Si votre cerveau n'est pas complètement frit après ce RFC, l'annexe
A présente un mécanisme pour utiliser des