Introduction

Le système DNS permet de faire l'association entre un nom humainement comprésensible et une adresse IP (et vice-versa). Le fonctionnement du DNS a été défini et implémenté dans les années 80, à une époque où la sécurité sur internet n'était pas encore une préoccupation.

De ce fait, par design, le système DNS, par son protocole de base, est vulnérable, en ce sens qu'il est basé sur la confiance. Nos ordinateurs posent des questions au serveur DNS, reçoivent des réponses, mais n'ont pas les moyens de s'assurer que la réponse qu'ils reçoivent est légitime. l'extension DNSSEC, définie dès 1999 par la RFC 2535 et complété par les RFC 4033, RFC 4034 et RFC 4035, permet de combler ce manque en introduisant une chaine de signature numérique jusqu'à la signature des enregistrements d'une zone DNS par son propriétaire, chaine de singature qui pourra être vérifiée par quiconque aura besoin de l'information.

Nous allons donc voir comment s'assurer que notre resolveur vérifie les signatures DNSSEC et si non, comment le mettre en place, puis comment signer sa zone dns et la gérer.

Cet article s'appuie sur bind 9.9.5 sous ubuntu LTS 14.04.

S'assurer que son serveur DNS vérifie les signatures DNSSEC

La première des choses consiste à vérifier que le serveur DNS que votre poste utilise vérifie bien les signatures DNSSEC.

Nous allons donc effectuer une requête positive et une requête négative pour s'assurer à la fois que les informations correctement signées sont vérifiées comme tel et que les informations mal signées conduisent à un échec (SERVFAIL), échec qui ne se présente pas quand on désactive la vérification de signature DNSSEC :

$ dig sigok.verteiltesysteme.net

; <<>> DiG 9.9.5-3-Ubuntu <<>> sigok.verteiltesysteme.net
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57950
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 5

[...]

$ dig sigfail.verteiltesysteme.net

; <<>> DiG 9.9.5-3-Ubuntu <<>> sigfail.verteiltesysteme.net
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 8675
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

[...]

$ dig +cd sigfail.verteiltesysteme.net

; <<>> DiG 9.9.5-3-Ubuntu <<>> +cd sigfail.verteiltesysteme.net
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55863
;; flags: qr rd ra cd; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 5

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;sigfail.verteiltesysteme.net.  IN  A

;; ANSWER SECTION:
sigfail.verteiltesysteme.net. 60 IN A   134.91.78.139

[...]

Dans le premier cas de figure, avec une signature correcte, le flag AD (Authenticated Data) dans la réponse indique qu'une signature dnssec a été vérifiée correctement.

Dans le second cas, le serveur DNS a refusé de nous répondre (SERVFAIL) parce que la signature est incorrecte.

Enfin, dans le troisième cas, nous avons demandé à notre résolveur de ne pas vérifier les signatures DNSSEC (CD = Check Disabled) et nous obtenons bien une réponse, c'est donc bien une erreur DNSSEC qui provoque le SERVFAIL dans le deuxième cas de figure. La présence du flag CD dans la réponse indique que la vérification DNSSEC a été désactivée.

Mode livre dont vous êtes le héro :

Si vous obtenez le même résultat, bravo, vous utilisez un résolveur DNS qui vérifie les signatures DNSSEC, vous pouvez passer directement à la mise en place d'une zone signée.

Dans le cas contraire, vous aurez une réponse à la deuxième requête, cela signifie que votre résolveur ne vérifie pas les signatures DNSSEC. Passez au chapitre suivant qui vous expliquera comment configurer un bind récursif pour vérifier les signatures DNSSEC.

Configuration d'un bind récursif pour vérifier les signatures DNS

En supposant que vous ayez déjà un bind récursif pour la résolution de nom, activer la validation DNSSEC est trivial.

Il suffit de s'assurer que la clé de la root zone '.' est bien incluse dans votre distribution de bind (généralement dans /etc/bind/bind.keys) et d'ajouter dans le fichier named.conf (/etc/bind/named.conf.options sous ubuntu) les 2 directives suivantes :

        dnssec-enable yes;
        dnssec-validation auto;

Une relance de bind est nécessaire pour la prise en compte de ces directives :

$ service bind9 restart

Tester comme indiqué au chapitre précédent et voilà !

Il est à noter que la plupart des relais DNS des box de connexion à internet (et des serveurs DNS de nos FAI) ne permettent pas la validation dnssec c'est pourquoi il est plus sûr de disposer de son propre caching-recursive-name-server plutôt que de s'appuyer sur sa box.

Si vous n'utilisez pas bind mais Unbound ou un DNS microsoft, vous trouverez des éléments de configuration dans ce document.

Mise en place d'une zone signée

Prérequis

Pour héberger une zone DNS signée, il vous faut :

  • Un nom de domaine utilisant un TLD gérant DNSSEC. Cela peut se vérifier en utilisant la commande dig ds tld (par exemple dig ds com pour le TLD .com) qui rendra des enregistrements DS si tld gère dnssec.

  • Un serveur DNS, qui sait gérer des zones signées. Nous utiliserons ici bind. Depuis sa version 9.9, bind sait gérer seul tout le cycle de signature. C'est la méthode la plus simple et la moins risquée, c'est donc celle que nous utiliserons. Ainsi, nous n'aurons pas à nous préoccuper de l'expiration des enregistrements signés, à penser à signer la zone à chaque modification, etc...

  • Un registrar qui vous permet de publier les enregistrements DS. Vérifiez le avant de commencer, si votre registrar ne gère pas dnssec, vous ne pourrez pas aller jusqu'au bout de la mise en place de la chaine de confiance.

Une zone basique pour commencer

Supposons que votre dns héberge déjà une zone. Pour l'exemple de cet article, nous partirons d'une zone basique example.tld avec une entrée test.example.tld dont l'adresse IPv4 est 1.2.3.4. Evidemment, le reste ne fonctionnera que s'il s'agit d'un vrai nom de domaine enregistré et connu sur internet.

$TTL 86400
@       IN      SOA     ns1.example.tld. hostmaster.example.tld. (
                        2014052901      ; serial number of this zone file
                        8H              ; slave refresh (1 day)
                        30M             ; slave retry time in case of a problem (2 hours)
                        4W              ; slave expiration time (4 weeks)
                        1H )            ; minimum caching time in case of failed lookups (1 hour)

        IN      NS      ns1.example.tld.
test    IN  A   1.2.3.4

Définie dans la configuration de bind de la façon suivante :

zone "example.tld" {
    type master;
    file "/etc/bind/db.example.tld";
};

Nous pouvons tester que l'entrée test.example.tld fonctionne bien :

root@vm-xubuntu:/etc/bind# dig @serveur_dns test.example.tld

; <<>> DiG 9.9.5-3-Ubuntu <<>> @serveur_dns test.example.tld
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21659
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;test.example.tld.      IN  A

;; ANSWER SECTION:
test.example.tld.   30  IN  A   1.2.3.4

;; AUTHORITY SECTION:
example.tld.        30  IN  NS  ns1.example.tld.

;; Query time: 32 msec
;; SERVER: xxxxx
;; WHEN: Thu May 29 22:32:34 CEST 2014
;; MSG SIZE  rcvd: 98

Bien, maintenant que nous avons une zone qui fonctionne, nous allons pouvoir la signer.

Génération des clés

Il vous faudra 2 clés, l'une dite KSK (Key Signing Key), l'autre appelée ZSK (Zone Signing Key). La première sert à signer la seconde, la seconde sert à signer les enregistrements de la zone.

Si vous avez plusieurs zones, vous aurez besoin d'une KSK, et d'une ZSK par zone à minima.

Commençons par créer un répertoire où nous stockerons les clés

$ mkdir -p /etc/bind/keys
$ chgrp bind /etc/bind/keys
$ chmod u+rwx,g+srx,o-rwx /etc/bind/keys
$ cd /etc/bind/keys

Les permissions utilisées permettent de s'assurer que bind pourra bien lire les clés.

Créons la KSK :

$ dnssec-keygen -a RSASHA256 -3 -f KSK -b 4096 example.tld
Generating key pair.....................................................................................++ ...............................++ 
Kexample.tld.+008+17107

La génération de la clé peut être particulièrement longue si votre machine n'a pas assez d'entropie. Si tel est le cas, générez de l'entropie (accès disques, frappes au clavier, ou installation du démon haveged)

Ensuite, générons une ZSK :

$ dnssec-keygen -a RSASHA256 -3 -b 2048 example.tld
Generating key pair.......+++ .......+++ 
Kexample.tld.+008+25266

On utilise une taille de clé de 2048 bits minimum car en dessous, nous ne pourrons pas utiliser NSEC3 et serons limité à NSEC c'est moins bien. (on me signale dans l'oreillette1 que NSEC3 fonctionne aussi avec des clés de 1024 bits).

$ ls
Kexample.tld.+008+17107.key       Kexample.tld.+008+25266.key
Kexample.tld.+008+17107.private   Kexample.tld.+008+63666.private

Nos deux clés sont créées, la KSK porte la référence (key ID) 17107 et la ZSK porte la référence 25266. Si vous ne savez plus quelle est la clé KSK de la clé ZSK, regardez le contenu du fichier .key, c'est indiqué dedans !

Configuration de Bind pour signer la zone

Ensuite, il faut configurer bind (la partie options dans /etc/bind/named.conf.options et la partie concernant la zone dans /etc/bind/named.conf.local) :

options {
    directory "/etc/bind";
    key-directory "/etc/bind/keys";
    recursion no;
    dnssec-enable yes;
};

zone "example.tld" in {
    inline-signing yes;
    auto-dnssec maintain;
    update-policy local;
    type master;
    file "dnssec.fr";
    ...
}

La configuration proposée ici est la plus simple : bind se charge de tout tout seul, nous n'avons qu'à maintenir un fichier de zone non signé.

La directive key-directory permet d'indiquer à bind où il doit aller chercher les clés. Cette directive peut être mise dans la section zone. C'est particulièrement utile si on utilise un répertoire différent pour stocker les clés de chaque domain.

Il faut recharger bind pour prise en compte de ces modifications

$ rndc reload

On teste ensuite que bind renvoit bien des enregistrements signées

$ dig +dnssec test.example.tld

; <<>> DiG 9.9.5-3-Ubuntu <<>> +dnssec test.example.tld
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42791
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 2, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;test.example.tld.      IN  A

;; ANSWER SECTION:
test.example.tld.   30  IN  A   1.2.3.4
test.example.tld.   30  IN  RRSIG   A 8 3 30 20140628235217 20140529231424 16140 example.tld. M+tIdL9wvT3s0rmvVYk8Rpe/+mDPeow75KQ3cMWi8THuP/yD+nRj01hU HZZzzCF4asrq+VRhwGE1rjLxdR2FPs9X7qF6IeCFrVNo2opxkSSsNxoq KJTGA60dbudJOShIlqvllgu6SNaes2WiUaD7s4O71UckAnG0nbVeT8Nw mEtQ2aJ6BvUONOWVAwwMNhhI8oPfZR5fVu79conWTTn/++8Y4EFD5P0F ozfdVszrrDM2tBsvv7DtX9arGqsXt0COlOz0DU8LTWJ8OVXTjku5Aetf HPpkHzhJcjX7+3CeOPfc16bNWbn2d2fs9pGj1Y9z4jNvewnnIwimcYbn 0lBbkA==

;; AUTHORITY SECTION:
example.tld.        30  IN  NS  ns1.example.tld.
example.tld         30  IN  RRSIG   NS 8 2 30 20140628235217 20140529231424 16140 example.tld. K+Gi5ETPhM0pwmf4FyuL2ADaRJvkdteU0FWnrh+HLZl7mrynA4mxk6xQ IxiX3DGLpNatSax+3UzVL8itBuNzuASRXblo07dcZ88b/lJTN0XBSzcR Erjz57VYwh06W57LuiWi/YdvYO74COWGWfStmOvQ84x0wSs0VGDJnLgS Ly67lCBrLPf48AK2bQUUMvgDWhpHMr1kjgAYZZb+m/lLDMpHPU6xbAns xmkwySBQhLnNWxy9u6gzdFzn0zW5yQ/lzfoX1r5bbC6vMjaL0RMUE7Pa jeseofK021PmWB4xJM0c/Bx60OBher5bwceC9Zkr7xQhhqcKR60jlVJJ Q7ivRA==

;; Query time: 6 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Fri May 30 02:18:36 CEST 2014
;; MSG SIZE  rcvd: 700

...

Nous voyons bien ici les enregistrements RRSIG, bind fait bien son travail de signature.

Bind a d'ailleurs créé un nouveau fichier de zone suffixé par .signed qui contient la version signée et qui peut être lu avec la commande :

$ named-compilezone -D -f raw -o - example.tld db.example.tld.signed

Utiliser NSEC3 plutôt que NSEC

Pour l'instant, notre zone est signée en utilisant NSEC (par défaut). Nous pouvons le vérifier par la présence d'engistrements de type NSEC retournés par la commande précédente

Pour utiliser NSEC3, nous devons procurer à bind le sel à utiliser. Commençons par générer le sel :

$ dd if=/dev/random bs=1 count=10 | md5sum | cut -c1-8
10+0 enregistrements lus
10+0 enregistrements écrits
10 octets (10 B) copiés, 0,00278809 s, 3,6 kB/s
49d135ce

Nous allons donc utiliser le sel 49d135ce

$ rndc signing -nsec3param 1 0 10 49d135ce example.tld
request queued

et voilà. Nous avons désormais des enregistrements NSEC3 dans le fichier de zone.

Publication dans la zone parente

Pour que la chaine de signature DNSSEC soit complète, il reste à publier l'enregistrement DS (condensat de la KSK) au niveau du TLD. Ces engegistrements seront eux-même signés par la clé du TLD.

La première des choses consiste à générer ces enregistrements DS à partir de la KSK :

$ dnssec-dsfromkey Kexample.tld.+008+17107.key
example.tld. IN DS 17107 8 1 E89E85FFD0377CBC50BBD36F3275B460FD6B5A75
example.tld. IN DS 17107 8 2 08E46F22CE866971DC41A4A770A35E6EF3FA659EBB9FEC5DA81B716BD68F7B82

Maintenant que nous avons ces 2 enregistrements, il nous faut les déclarer.

L'interface de votre registrar devrait vous permettre de déclarer ces enregistrements. Si tel n'est pas le cas, il ne vous reste plus qu'à changer de registrar.

S'il ne vous permet pas de copier/coller la ligne entière mais présente des champs à remplir, voici les informations contenues dans ces enregistrements :

  • 17107 : Il s'agit de la Key ID (identifiant de clé)
  • 8 : ceci est l'algorithme de chiffrement. Ici 8 = RSHSHA256. On rencontre aussi parfois 5 = RSASHA1
  • 1 ou 2 : le type de condensat : 1 = SHA1, 2 = SHA256
  • la longue suite hexadécimale : il s'agit du condensat de la clé

Vérifier le bon fonctionnement

Une fois tout ceci effectué, cela devrait normalement être fonctionnel. Vous pouvez le vérifier sur le debugger DNSSEC verisign. Si vous avez des points verts partout, bravo, votre domaine est maintenant signé par DNSSEC.

L'utilitaire ZoneCheck dispose également d'une option pour tester la configuration DNSSEC d'un domaine (entre autres choses)

L'interrogation d'un autre serveur dns récursif prenant en charge la vérification DNSSEC présentera le flag AD (Authenticated Data), votre domaine est maintenant signé grâce à DNSSEC.

Maintenance d'une zone signée

Modification du contenu de la zone

Lorsque vous voulez désormais modifier une zone signée automatiquement par bind, il vous faut freezer la zone, faire vos modifications, puis libérer la zone, ce qui provoquera un rechargement et une resignature par bind. Cela ne vous dispense évidemment pas de mettre à jour le serial de la zone.

$ rndc freeze example.tld

[...Éditer le fichier de zone...]

$ rndc thaw example.tld
A zone reload and thaw was started.
Check the logs to see the result.

Supervision

Il est important de superviser que les opérations de resignature régulières des zones fonctionnent correctement, car en cas d'expiration des enregistrements RRSIG, votre domaine ne serait plus joignable.

Je vous renvoie pour cela à cet excellent article de stéphane Bortzmeyer qui détaille très clairement comment il a mis en place la supervision de l'expiration des enregistrements RRSIG à l'aide d'un script perl et d'icinga.

Rotation de clé

Les clés doivent être remplacées régulièrement. On parle de key-rollover. Plus souvent pour la ZSK, qui est plus petite et surtout qui n'est pas publiée dans les DNS de niveau supérieur (donc plus facile à remplacer).

Je vous conseille un remplacement des ZSK tous les 6 mois et un remplacement de la KSK tous les 2 ans. Ceci est bien entendu à ajuster en fonction de la criticité de votre domaine (et du temps dont vous disposez).

Une rotation de clé est toujours une opération minutieuse qu'il vaut mieux avoir répété avant sur un domaine de faible importance car les risques de se prendre les pieds dans le tapis sont grands, et cela pourrait rendre votre domaine inutilisable pendant quelques temps.

Pour la rotation de la ZSK, on commence par créer une nouvelle ZSK, puis on indique que la clé doit être retirée dans 2 jours et supprimée dans 6 jours (à ajuster en fonction des TTL) :

$ # Génération de la nouvelle ZSK
$ dnssec-keygen -a RSASHA256 -b 2048 example.tld
Generating key pair...............................................+++ ....................................................................................................................+++ 
Kexample.tld.+008+21999

$ # On indique que la clé doit être retirée dans 2 jours
$ dnssec-settime -I +2d Kexample.tld.+008+25266.key
dnssec-settime: warning: Permissions on the file ./Kexample.tld.+008+25266.private have changed from 0640 to 0600 as a result of this operation.
./Kexample.tld.+008+25266.key
./Kexample.tld.+008+25266.private

# On indique que la clé doit être supprimée dans 6 jours
$ dnssec-settime -D +6d Kexample.tld.+008+25266.key
./Kexample.tld.+008+25266.key
./Kexample.tld.+008+25266.private

$ # On ajuste les permissions pour que bind puisse toujours lire les clés
$ chmod g+r *

Bind fait normalement le nécessaire ensuite.

Pour le remplacement de la KSK, il y a 2 étapes de plus : d'une part il faut révoquer immédiatement la clé, d'autre part, il faut publier auprès de son registrar les enregistrements DS correspondant à la nouvelle KSK :

$ # Génération de la nouvelle KSK
$ dnssec-keygen -a RSASHA256 -f KSK -b 2048 example.tld
Generating key pair...+++ .............................................................................+++ 
Kexample.tld.+008+14614

$ # Révocation immédiate de la KSK
$ dnssec-settime -R +0 Kexample.tld.+008+17107.key
dnssec-settime: warning: Permissions on the file ./Kexample.tld.+008+17107.private have changed from 0640 to 0600 as a result of this operation.
./Kexample.tld.+008+17107.key
./Kexample.tld.+008+17107.private

$ # On indique que la clé doit être retirée dans 2 jours
$ dnssec-settime -I +2d Kexample.tld.+008+17107.key
./Kexample.tld.+008+17107.key
./Kexample.tld.+008+17107.private

# On indique que la clé doit être supprimée dans 6 jours
$ dnssec-settime -D +6d Kexample.tld.+008+17107.key
./Kexample.tld.+008+17107.key
./Kexample.tld.+008+17107.private

$ # On ajuste les permissions pour que bind puisse toujours lire les clés
$ chmod g+r *

$ # On génère les nouveaux enregistrement DS à publier chez notre registrar
$ dnssec-dsfromkey Kexample.tld.+008+14614.key 
example.tld. IN DS 14614 8 1 F14DCF5DA74A6A2727A5DF570C98BB2186CD241B
example.tld. IN DS 14614 8 2 B4CAF28F7BA14AA99E8C18DD87604ABBC1D18F4EE1C69D84E8BE33B7E439132F

On prendra évidemment bien soin de superviser le tout dans les jours qui suivent pour s'assurer que l'opération se passe bien.

Une fois les 6 jours passés, on pourra supprimer les enregistrements DS de l'ancienne clé, effacer les fichiers .key et .private correspondants. Faire un peu de ménage, quoi.

Références


Comments

comments powered by Disqus