Architecture DNS sécurisée

GNU/Linux Magazine n° 154 | novembre 2012 | Fabien Dupont
Creative Commons
  • Actuellement 0 sur 5 étoiles
0
Merci d'avoir participé !
Vous avez déjà noté cette page, vous ne pouvez la noter qu'une fois !
Votre note a été changée, merci de votre participation !
Pierre angulaire de l'infrastructure réseau, le service DNS est la cible de nombreuses attaques. Certains veulent le faire taire (déni de service), d'autres le faire mentir (empoisonnement de cache) ou s'appuyer sur lui pour attaquer d'autres serveurs DNS (récursivité mal configurée). Lorsque vous mettez en place ce service, vous avez à votre disposition plusieurs mécanismes qui limitent les risques face aux méchants pirates de l'Internet. Cet article vous guidera dans l'installation d'une architecture DNS multiniveau et implémentant DNSSEC.

Cet article a été construit comme une progression logique, chaque étape apportant ses fonctionnalités et complexifiant encore un peu ce service qui peut paraître si simple. D'ailleurs, nous fêterons l'année prochaine les 30 ans du DNS (ça ne nous (me ?) rajeunit pas). La page Wikipédia dédiée au DNS [0.1] vous fournira des informations sur le principe de fonctionnement du DNS et des bases pour appréhender la suite de cet article.

Nous initierons le processus en installant un serveur DNS faisant office de résolveur récursif pour les machines de notre réseau. Comme il n'héberge pas de zone en propre, son rôle est de répondre aux requêtes des machines internes en interrogeant les serveurs externes, à la manière d'un proxy. Cela réduit le nombre de machines accédant à Internet pour les requêtes DNS et offre un mécanisme de cache, ce qui limite la consommation de bande passante (oui, je sais, votre opérateur vous fournit de la fibre et cela ne vous fait ni chaud ni froid de consommer quelques octets par seconde pour la résolution de noms).

Dans un deuxième temps, nous hébergerons nos propres zones (directe et inverse), ce qui est quand même l'un des buts recherchés lorsque l'on installe un serveur DNS sur son réseau. La zone directe est associée au sous-domaine lab.evenit.info et la zone inverse est associée au sous-réseau 192.168.0.0/24. Notre serveur fera alors autorité pour notre domaine et devra répondre aux requêtes issues d'Internet, en plus des machines internes.

La troisième étape consistera en l'éloignement d'Internet de notre serveur accessible en écriture (et donc, de tous les méchants qui s'y promènent). Pour cela, nous déploierons deux serveurs « esclaves » qui serviront les zones en lecture seule et seront les seuls à répondre aux requêtes. Notre serveur initial sera leur « maître » et, à ce titre, il n'acceptera de répondre qu'à eux, les notifiera en cas de modification des zones pour qu'ils mettent à jour leurs données. Il y aura deux esclaves pour assurer un mécanisme de résilience en cas de défaillance de l'un d'eux : ce serait dommage que notre domaine disparaisse d'Internet pour une bête panne matérielle.

Nous passerons ensuite à l'interconnexion avec les services DNS d'Active Directory (eh oui, nous n'oublions pas l’interopérabilité). Cela nous permettra d'aborder deux notions supplémentaires : la délégation de sous-domaines et l'implémentation d'un mécanisme de mise à jour dynamique des zones. Et cela, sans polluer nos zones existantes et en isolant une partie du domaine d'Internet (pseudo-mesure de sécurité). Nous aurons alors la première brique de notre infrastructure hétérogène.

Une fois tout en place, l'architecture DNS multiniveau que nous mettons en œuvre disposera de trois couches :

1. Le serveur ns.lab.evenit.info est le serveur maître de la zone lab.evenit.info. Il est inaccessible depuis Internet.

2. Les serveurs ns1.lab.evenit.info et ns2.lab.evenit.info sont des serveurs esclaves qui sont notifiés et autorisés à transférer les zones de nos (sous-)domaines. Ils sont accessibles en lecture depuis Internet et les machines internes, et font autorité pour les zones hébergées.

3. Le contrôleur de domaine Active Directory est autorisé à faire des mises à jour dynamiques de certaines zones et dispose donc de droits en écriture sur le serveur maître ; il n'est donc pas accessible depuis Internet.

Initialement, j'avais envisagé d'intégrer la mise en œuvre de DNSSEC dans cet article. Cependant, nous entrons dans un domaine plus complexe qui est facteur de risque. Cela fera donc l'objet d'un article séparé dans un prochain numéro.

Côté logiciel, j'ai choisi de baser les serveurs GNU/Linux sur CentOS 6 et de faire confiance à un classique, le célèbre Bind, dans sa version 9.8.2, qui est fourni dans les dépôts de la distribution CentOS 6. Cette version dispose de tous les raffinements dont nous avons besoin et les mises à jour en cas de faille sont plutôt rapidement disponibles.

1. Configuration réseau de notre serveur

Une fois notre serveur fraîchement installé, nous configurons son interface réseau pour un adressage statique : il est nécessaire que l'adresse IP ne change pas, puisque le DNS est un pilier de notre réseau et qu'il en garantit la stabilité et que cela pose problème pour la délégation de zone (voir section 4).

Nous récupérons l'adresse Ethernet de la carte réseau (vous noterez que le hostname est localhost puisque nous n'avons rien configuré) :

[root@localhost]# ip addr

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN

    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

    inet 127.0.0.1/8 scope host lo

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000

    link/ether 00:0c:29:04:d6:8c brd ff:ff:ff:ff:ff:ff

Nous créons une configuration en utilisant (temporairement) les résolveurs DNS publics fournis par Google. Vous pouvez/devriez utiliser les résolveurs de votre fournisseur d'accès à Internet (FAI), qui sont plus proches de votre serveur d'un point de vue réseau et qui répondront donc (normalement) plus vite.

[root@localhost]# cat > /etc/sysconfig/network-scripts/ifcfg-eth0 << EOF

DEVICE="eth0"

HWADDR="00:0c:29:04:d6:8c"

ONBOOT="yes"

BOOTPROTO="static"

IPADDR="192.168.0.10"

NETMASK="255.255.255.0"

NETWORK="192.168.0.0"

BROADCAST="192.168.0.255"

DOMAIN="lab.evenit.info"

DNS1="8.8.4.4"

DNS2="8.8.8.8"

EOF

[root@localhost]# service network restart

Nous indiquons ensuite le nom d'hôte de la machine, ns, et la route par défaut (ici, mon routeur ADSL).

[root@localhost]# cat > /etc/sysconfig/network << EOF

NETWORKING=yes

HOSTNAME=ns.lab.evenit.info

GATEWAY=192.168.0.1

EOF

Comme les cordonniers sont toujours les plus mal chaussés, notre serveur ne peut pas (encore) se fier à un serveur DNS pour associer son propre nom à une adresse. Nous le mettons au parfum « à l'ancienne » : dans /etc/hosts. C'est d'ailleurs une bonne pratique lorsque l'on est en adressage statique.

[root@localhost]# echo "192.168.0.10 ns ns.lab.evenit.info" >> /etc/hosts

Et pour nous assurer que tout ceci est pérenne, nous pouvons redémarrer le serveur et vérifier que nos paramètres sont toujours bien positionnés.

2. Installation basique de Bind

Comme indiqué en introduction, nous commençons par une configuration simpliste de Bind en tant que résolveur récursif des domaines externes pour les machines internes.

Les bonnes pratiques nous disent de chrooter notre serveur pour compliquer la vie d'un éventuel pirate. Nous installons donc le paquet bind-chroot avec yum :

[root@ns]# yum install bind-chroot bind bind-libs portreserver bind-utils

Pour nous simplifier la vie, lors de son démarrage, le service named monte en mode « bind » les fichiers de configuration dans l'environnement chrooté. Par exemple, le fichier /etc/named.conf est monté comme /var/named/chroot/etc/named.conf. Nous utilisons donc les fichiers de configuration génériques situés dans /etc.

La configuration par défaut, fournie avec le paquet, est un bon point de départ et implémente presque notre résolveur récursif à usage interne. Voici son contenu :

options {

        listen-on port 53 { 127.0.0.1; };

        listen-on-v6 port 53 { ::1; };

        directory       "/var/named";

        dump-file       "/var/named/data/cache_dump.db";

        statistics-file "/var/named/data/named_stats.txt";

        memstatistics-file "/var/named/data/named_mem_stats.txt";

        allow-query     { localhost; };

        recursion yes;

        dnssec-enable yes;

        dnssec-validation yes;

        dnssec-lookaside auto;

        /* Path to ISC DLV key */

        bindkeys-file "/etc/named.iscdlv.key";

        managed-keys-directory "/var/named/dynamic";

};

logging {

        channel default_debug {

                file "data/named.run";

                severity dynamic;

        };

};

zone "." IN {

        type hint;

        file "named.ca";

};

include "/etc/named.rfc1912.zones";

include "/etc/named.root.key";

Je l'explicite rapidement. Dans la section options, nous définissons les paramètres globaux du serveur, à savoir :

- listen-on et listen-on-v6 : les adresses et ports sur lesquels le serveur écoute,

- directory : racine de la configuration,

- dump-file, statistics-file, memstatistics-file : fichiers utilisés lors d'appels à RNDC,

- allow-query : nous autorisons notre serveur à s'interroger lui-même,

- recursion : nous autorisons les requêtes récursives,

- dnssec-enable : nous activons DNSSEC pour pouvoir vérifier les domaines externes,

- dnssec-validation : nous validons les enregistrements avec DNSSEC,

- dnssec-lookaside : nous indiquons les trust-anchors DNSSEC (fournis par Bind),

- bindkeys-file : fichier contenant les trust-anchors DNSSEC pour la validation en mode auto,

- managed-keys-directory : répertoire où stocker les clés DNSSEC gérées par Bind.

La section logging définit une gestion de log minimale : tout dans le fichier /var/named/data/named.run.

La zone racine est fournie pour information dans le fichier /var/named/named.ca et sa clé DNSSEC dans le fichier /etc/named.root.key. Les zones définies dans le RFC1912 [2.1] sont définies dans le fichier /etc/named.rfc1912.zones.

Le démarrage du service se fait sans souci :

[root@ns]# service named start

[root@ns]# chkconfig named on

Nous pouvons alors vérifier que le serveur est bien démarré et en écoute sur le port 53 :

[root@ns]# netstat -lnp

Connexions Internet actives (seulement serveurs)

Proto Recv-Q Send-Q Local Address    Foreign Address State   PID/Program name

[...]

tcp        0      0 127.0.0.1:53     0.0.0.0:*        LISTEN 1475/named

tcp        0      0 127.0.0.1:953    0.0.0.0:*        LISTEN 1475/named

udp        0      0 127.0.0.1:53     0.0.0.0:*                1475/named

[…]

Il écoute aussi sur le port 953, qui correspond à l'interface RNDC (Remote Nameserver Daemon Controller).

Nous pouvons aussi vérifier qu'il répond aux requêtes :

[root@ns ~]# dig @127.0.0.1 www.unixgarden.com +short

www.unixgarden.com.     600     IN      A       94.23.219.217

Pour observer le fonctionnement de la résolution de noms, vous pouvez utiliser l'option +trace de dig (à la place de +short) : le résolveur réalise une résolution sans passer par son cache, ce qui signifie qu'il interroge la racine, puis tous les serveurs intermédiaires jusqu'au domaine concerné.

Vous pouvez au passage voir l'ensemble des fichiers/répertoires montés en mode « bind » [2.2] (à ne pas confondre avec le nom de notre serveur DNS) :

[root@ns ~]# mount | grep bind

/etc/named on /var/named/chroot/etc/named type none (rw,bind)

/var/named on /var/named/chroot/var/named type none (rw,bind)

/etc/named.conf on /var/named/chroot/etc/named.conf type none (rw,bind)

/etc/named.rfc1912.zones on /var/named/chroot/etc/named.rfc1912.zones type none (rw,bind)

/etc/rndc.key on /var/named/chroot/etc/rndc.key type none (rw,bind)

/usr/lib64/bind on /var/named/chroot/usr/lib64/bind type none (rw,bind)

/etc/named.iscdlv.key on /var/named/chroot/etc/named.iscdlv.key type none (rw,bind)

/etc/named.root.key on /var/named/chroot/etc/named.root.key type none (rw,bind)

Note

Les deux documents suivants ont été sources d'inspiration pour cette section. Ils fournissent des détails plus exhaustifs sur certains points de configuration et je vous en conseille donc la lecture : http://www.madboa.com/geek/soho-bind/ ; http://fr.gentoo-wiki.com/wiki/Bind.

3. Ouverture du résolveur

Notre résolveur fonctionne, mais ne répond qu'à localhost. Nous allons donc maintenant le rendre accessible à l'ensemble des machines de notre réseau interne. Pour cela, nous commençons par créer une Access Control List (ACL) qui contient notre réseau privé, en ajoutant la ligne suivante à /etc/named.conf :

acl internal_nets { 192.168.0.0/24; };

Nous modifions les options globales comme suit :

listen-on port 53 { 127.0.0.1; 192.168.0.10; };

listen-on-v6 { none; };

allow-query { localhost; internal_nets; };

recursion yes;

allow-recursion { localhost; internal_nets; };

Pour résumer, notre serveur écoute sur son interface réseau accessible de nos machines, et seulement en IPv4. Il accepte les requêtes normales et récursives depuis les machines de notre réseau (identifiées par le nom de l'ACL correspondante).

Un redémarrage du service named permet de prendre en compte les changements :

[root@ns]# service named restart

Côté firewall, nous ouvrons les ports nécessaires, à savoir UDP/53 et TCP/53. Nous ajoutons donc les lignes suivantes au fichier /etc/sysconfig/iptables et redémarrons le service iptables :

-A INPUT -m state --state NEW -m tcp -p tcp --dport 53 -j ACCEPT

-A INPUT -p udp --dport 53 -j ACCEPT

Nous pouvons relancer notre test depuis un client sur le réseau pour confirmer que cela fonctionne :

[toto@client]$ dig @192.168.0.10 www.unixgarden.com +short

www.unixgarden.com.     600     IN      A       94.23.219.217

Tout fonctionne bien. Nous allons donc pouvoir passer aux choses sérieuses : héberger notre propre zone.

4. Création de nos propres zones

Comme indiqué en introduction, nous allons travailler à la mise en œuvre de la zone lab.evenit.info. Bon, en fait, il n'y a que moi qui puisse le faire, mais vous saurez facilement adapter ce qui suit à votre(vos) propre(s) zone(s).

Note

Vous aurez remarqué que le domaine sur lequel nous travaillons n'est pas un domaine de second niveau, à savoir un domaine juste en-dessous d'un domaine de premier niveau, aussi appelé TLD (Top-Level Domain). C'est une commodité pour ne pas impacter mon domaine de second niveau evenit.info.. J'ai donc réalisé une délégation de zone qui consiste à indiquer dans la zone parente les serveurs de noms pour la zone fille, ainsi que les enregistrements A (Address) correspondants.

Ces enregistrements A sont appelés « glue records ». Leur rôle est de servir de pointeur vers les serveurs de noms hébergeant les zones filles. Ainsi, les serveurs de la zone racine . ont connaissance des serveurs de la zone info., qui ont eux-mêmes connaissance des serveurs de la zone evenit.info., et ainsi de suite. Dans le cas de la zone lab.evenit.info., c'est le serveur de la zone evenit.info. que j'ai configuré pour répondre aux requêtes demandant les serveurs de noms (enregistrements de type NS (NameServer)) pour sa zone fille.

Si vous avez un peu fouillé dans l'arborescence, vous aurez pu constater la présence du répertoire /var/named/slaves qui n'est accessible qu'à named, en lecture-écriture (770). Ce répertoire permet de ranger les fichiers de zones que notre serveur reçoit quand il est esclave pour des zones. Par contre, par défaut, il n'y a rien de tel pour les zones pour lesquelles notre serveur est maître ; c'est pourtant bien pratique quand vous devez gérer de nombreuses zones. Nous allons donc créer un répertoire masters à cet effet ; seul root peut y écrire et named y a accès en lecture.

[root@ns]# install -o root -g named -m 0750 -d /var/named/masters

La prochaine étape est la création du fichier de zone qui contient toutes les informations sur notre (sous-)domaine. Il se compose a minima d'un enregistrement de type SOA (Start Of Authority) qui indique le serveur propriétaire de la zone, l'adresse de contact pour le domaine, un numéro de série (la section 2.2 du RFC 1912 recommande AAAAMMJJnn, car la date sous ce format est forcément un nombre croissant) utilisé pour savoir si une zone a changé (une requête du SOA suffit), ainsi que les différents TTL (Time To Live) que je ne détaille pas. Ensuite, nous ajoutons un enregistrement de type NS indiquant le serveur de noms de notre zone, et le « glue record » associé (de type A). Cela donne le fichier suivant :

[root@ns]# cat > /var/named/masters/lab.evenit.info.zone << EOF

$TTL 1D

@ IN SOA ns.lab.evenit.info. hostmaster.lab.evenit.info. (

   2012090101 1D 1H 1W 3H )

 IN NS ns.lab.evenit.info.

ns IN A 192.168.0.10

EOF

[root@ns]# chown root:named /var/named/masters/lab.evenit.info.zone

[root@ns]# chmod 0640 /var/named/masters/lab.evenit.info.zone

Nous faisons la même chose pour notre zone inverse :

[root@ns]# cat > /var/named/masters/0.168.192.in-addr-arpa.zone << EOF

$TTL 1D

@ IN SOA ns.lab.evenit.info. hostmaster.lab.evenit.info. (

   2012090101 1D 1H 1W 3H )

 IN NS ns.lab.evenit.info.

10 IN PTR ns.lab.evenit.info.

EOF

[root@ns]# chown root:named /var/named/masters/0.168.192.in-addr-arpa.zone

[root@ns]# chmod 0640 /var/named/masters/0.168.192.in-addr-arpa.zone

Note

Vous aurez noté que dans notre SOA nous indiquons l'adresse de contact pour la zone : hostmaster.lab.evenit.info.. Celle-ci est automatiquement transformée en hostmaster@lab.evenit.info. Il est important que cette adresse existe. Pour l'instant, nous n'avons pas encore installé de serveur de messagerie, mais il faudra penser à créer un compte ou un alias hostmaster@lab.evenit.info.

Note

Dans l'absolu, notre configuration est considérée comme insuffisante par les outils d'analyse de zone, tel que l'excellent ZoneCheck [4.1] fourni par l'AFNIC [4.2]. En effet, il est fortement conseillé pour des raisons de résilience de disposer de plus d'un serveur de domaine et qu'ils ne soient pas tous hébergés sur le même domaine et le même réseau. Notre étude de cas corrigera l'unicité du serveur, mais pas la localisation réseau.

Nous indiquons à notre serveur que nous hébergeons ces deux zones, où se trouvent les données et qui peut les consulter (allow-query), les transférer (allow-transfer) ou les mettre à jour (allow-update) :

[root@ns]# cat >> /etc/named.conf << EOF

zone "lab.evenit.info" {

        type master;

        file "masters/lab.evenit.info.zone";

        allow-query { any; };

        allow-transfer { none; };

        allow-update { none; };

};

zone "0.168.192.in-addr.arpa" {

        type master;

        file "masters/0.168.192.in-addr.arpa.zone";

        allow-query { any; };

        allow-transfer { none; };

        allow-update { none; };

};

EOF

Un petit redémarrage de named...

[toto@ns]# service named restart

et nous pouvons tester depuis une autre machine de notre réseau :

[toto@client]$ dig @192.168.0.10 lab.evenit.info SOA +short

ns.lab.evenit.info. hostmaster.lab.evenit.info. 2012090501 86400 3600 604800 10800

[toto@client]$ dig @192.168.0.10 ns.lab.evenit.info +short

192.168.0.10

Tout fonctionne. Nous pouvons donc passer à l'isolation de notre serveur maître par la création des serveurs esclaves accessibles en lecture seule (et qui assureront une fonction (partielle) de résilience).

5. Deuxième étage de la fusée

Pour rappel, nous allons déployer deux nouveaux serveurs DNS répondant aux noms de ns1 et ns2, avec les adresses IP respectives 192.168.0.20 et 192.168.0.21. Ces deux serveurs seront des esclaves de notre serveur actuel, ns, et ne disposeront donc pas des fichiers de zones originaux : nous devons donc mettre en place le mécanisme de transfert de zone [5.1]. Ils seront aussi les seuls à répondre aux requêtes des clients, et récursivement uniquement pour les clients internes : nous devons donc changer les autorisations sur le serveur maître et déporter celles de query et recursion vers les esclaves.

Nous commençons par installer les nouveaux serveurs comme vu aux sections 1 et 2. Nous avons alors deux nouveaux serveurs qui savent résoudre en mode récursif pour eux-mêmes. Nous appliquons alors la configuration de la section 3 sur chacun d'eux, en prenant soin de changer les adresses IP d'écoute. Il ne nous reste qu'à :

1. déclarer nos serveurs comme esclaves pour les zones que nous hébergeons,

2. autoriser le transfert depuis notre serveur maître vers les serveurs esclaves,

3. supprimer les fonctionnalités de résolution sur le serveur maître.

Pour la suite, les commandes sont passées sur ns1 (192.168.0.20). Je vous laisse adapter à ns2.

Nous commençons par définir des ACL pour les serveurs de noms en fonction de leur niveau dans l'architecture. Sur le serveur maître, nous ajoutons la ligne suivante dans /etc/named.conf :

acl lvl2_ns { 192.168.0.20; 192.168.0.21; };

et sur les serveurs esclaves, la ligne suivante :

acl lvl1_ns { 192.168.0.10; };

Sur le serveur maître, nous indiquons pour chacune des zones hébergées que nous autorisons seulement les serveurs esclaves à faire des requêtes (allow-query), à transférer la zone (allow-transfer) et nous les notifions lorsque les zones sont modifiées pour qu'ils procèdent au transfert (also-notify). Pour la zone lab.evenit.info, cela donne les lignes ci-dessous ; je vous laisse le soin de dupliquer pour la zone inverse 0.168.192.in-addr.arpa :

zone "lab.evenit.info" {

 type master;

 file "masters/lab.evenit.info.zone";

 allow-query { lvl2_ns; };

 allow-transfer { lvl2_ns; };

 allow-update { none; };

 also-notify { 192.168.0.20; 192.168.0.21; };

};

Au niveau des options globales, nous restreignons les requêtes au serveur maître lui-même et désactivons la récursivité :

allow-query { localhost; };

recursion no;

Sur les serveurs esclaves, nous acceptons les requêtes et la récursivité depuis le réseau interne (cf. section 3) et indiquons que nos serveurs sont en mode esclave (type slave), qu'ils répliquent les zones du serveur maître (masters), seul autorisé à les notifier (allow-notify). Ils n'acceptent pas les transferts vers un autre serveur et ne notifient aucun serveur. Cela donne la configuration suivante pour la zone lab.evenit.info (similaire pour la zone inverse) :

zone "lab.evenit.info" {

 type slave;

 file "slaves/lab.evenit.info.zone";

 masters { 192.168.0.10; };

 allow-query { any; };

 allow-notify { lvl1_ns; };

 allow-transfer { none; };

 allow-update { none; };

 notify no;

};

Notre architecture DNS est maintenant en place. Il ne nous reste plus qu'à configurer nos clients pour qu'ils utilisent ns1 et ns2. Pour cela, je vous renvoie à la documentation de votre système d'exploitation.

6. Sécurisation des transferts de zone

Vous aurez remarqué (ou pas) que les ACL sont basées sur l'adresse IP des serveurs. C'est pratique, mais une machine se présentant avec la même adresse peut donc transférer nos zones sans que nous le voulions. Un mécanisme est disponible pour authentifier les échanges : TSIG, pour Transaction SIGnature. Celui-ci se base sur un secret partagé, qui prend la forme d'une clé au format HMAC-SHA256 (d'autres HMAC sont possibles) partagée entre deux serveurs pour s'authentifier mutuellement.

Dans notre cas, nous devons donc créer deux clés, une entre ns et ns1, et une entre ns et ns2 (comme ça, ns1 et ns2 ne peuvent pas s'authentifier mutuellement). Nous créons les clés sur le serveur maître, puis nous les copions sur les serveurs esclaves. Je vous montre ce que cela donne pour la clé entre ns et ns1.

Nous commençons par utiliser dnssec-keygen (fourni par le paquet bind) pour générer notre clé avec l'algorithme HMAC-SHA256, d'une taille de 256 bits pour un usage entre les hôtes ns et ns1 :

[root@ns]# dnssec-keygen -a HMAC-SHA256 -b 256 -n HOST ns-ns1.

Kns-ns1.+163+35933

La commande a généré les fichiers Kns-ns1.+163+35933.key et Kns-ns1.+163+35933.private (les noms seront différents chez vous). Seul le fichier Kns-ns1.+163+35933.private nous intéresse, car il contient la clé au format HMAC-SHA256, à la ligne commençant par Key:. Nous copions la chaîne de caractères associée et l'ajoutons dans le fichier de configuration /var/named/chroot/etc/ns-ns1.key :

[root@ns]# cat > /var/named/chroot/etc/ns-ns1.key << EOF

key "ns-ns1." {

 algorithm hmac-sha256;

 secret "j35Qn20KQnntBcAg7K8W2RlFnDX5ThJ1AkcBVM8Tsns=";

};

EOF

[root@ns]# chgrp named /var/named/chroot/etc/named/keys/ns-ns1.key

[root@ns]# chmod 0640 /var/named/chroot/etc/named/keys/ns-ns1.key

Nous copions le fichier de clé sur le serveur ns1 et modifions la configuration pour activer l'authentification par clé.

Sur le serveur maître, nous ajoutons une référence au fichier contenant la clé et nous la rattachons au serveur esclave :

include "/etc/ns-ns1.key";

server 192.168.0.20 { keys { ns-ns1.; }; };

Nous faisons la même chose pour le serveur ns2 et nous modifions l'ACL lvl2_ns :

acl lvl2_ns { key ns-ns1.; key ns-ns2.; };

Sur les serveurs esclaves, nous ajoutons la clé du serveur ns et modifions l'ACL lvl1_ns. L'exemple ci-dessous est ce qui se fait sur ns1.

acl lvl1_ns { key ns-ns1; };

Ce mécanisme de sécurisation est simple à mettre en œuvre, mais nous verrons que nous pouvons implémenter d'autres mécanismes plus tard.

Note

Une source d'inspiration pour cette section, directement dans la documentation de Bind : http://ftp.isc.org/isc/bind9/cur/9.8/doc/arm/Bv9ARM.ch04.html#tsig.

7. Activation des mises à jour dynamiques pour Active Directory

Au sein de notre environnement, nous souhaitons faire cohabiter des serveurs GNU/Linux au sein d'un domaine Active Directory. Or, Active Directory use (et abuse ?) du DNS pour identifier les serveurs hébergeant ses services. Pour cela, il se base sur des enregistrements de type SRV (Server) tels que _ldap._tcp.evenit.info qui indique quel(s) est(sont) le(s) serveur(s) LDAP joignable(s) en TCP sur le domaine. Ces enregistrements sont cantonnés à des sous-domaines : _msdcs, _sites, _udp, _tcp, DomainDnsZones et ForestDnsZones ; et Active Directory les met à jour dynamiquement à intervalles réguliers. Pour ne pas le contrarier, nous allons héberger ces zones sur nos serveurs DNS, mais allons laisser Active Directory les mettre à jour via le mécanisme de Dynamic Update [7.1].

Nous commençons par créer une nouvelle ACL qui référence l'ensemble de nos contrôleurs de domaine. Pour cet exemple, la liste est très courte, mais en production, vous en aurez au moins deux pour la résilience.

acl ad_dcs { 192.168.0.30; };

Nous déclarons les zones dans le fichier /etc/named.conf du serveur maître, ns. Le changement principal est l'option allow-updates pour laquelle nous indiquons que les serveurs de l'ACL ad_dcs sont autorisés à mettre à jour la zone. Vous noterez que pour les zones _msdcs et ForestDnsZones, nous avons une option supplémentaire : check-names ignore ; cela vient du fait qu'Active Directory essaie d'ajouter des enregistrements qui ne respectent pas le format décrit dans les RFC. Comme il est le seul à les utiliser, nous ne sommes pas trop regardant et le laissons faire.

zone "_msdcs.lab.evenit.info" {

    type master;

    file "masters/_msdcs.lab.evenit.info.zone";

    allow-query { lvl2_ns; ad_dcs; };

    allow-transfer { lvl2_ns; };

    allow-update { ad_dcs; };

    check-names ignore;

    also-notify { 192.168.0.20; 192.168.0.21; };

};

zone "_sites.lab.evenit.info" {

    type master;

    file "masters/_sites.lab.evenit.info.zone";

    allow-query { lvl2_ns; ad_dcs; };

    allow-transfer { lvl2_ns; };

    allow-update { ad_dcs; };

    also-notify { 192.168.0.20; 192.168.0.21; };

};

zone "_udp.lab.evenit.info" {

    type master;

    file "masters/_udp.lab.evenit.info.zone";

    allow-query { lvl2_ns; ad_dcs; };

    allow-transfer { lvl2_ns; };

    allow-update { ad_dcs; };

    also-notify { 192.168.0.20; 192.168.0.21; };

};

zone "_tcp.lab.evenit.info" {

    type master;

    file "masters/_tcp.lab.evenit.info.zone";

    allow-query { lvl2_ns; ad_dcs; };

    allow-transfer { lvl2_ns; };

    allow-update { ad_dcs; };

    also-notify { 192.168.0.20; 192.168.0.21; };

};

zone "DomainDnsZones.lab.evenit.info" {

    type master;

    file "masters/DomainDnsZones.lab.evenit.info.zone";

    allow-query { lvl2_ns; ad_dcs; };

    allow-transfer { lvl2_ns; };

    allow-update { ad_dcs; };

    also-notify { 192.168.0.20; 192.168.0.21; };

};

zone "ForestDnsZones.lab.evenit.info" {

    type master;

    file "masters/ForestDnsZones.lab.evenit.info.zone";

    allow-query { lvl2_ns; ad_dcs; };

    allow-transfer { lvl2_ns; };

    allow-update { ad_dcs; };

    check-names ignore;

    also-notify { 192.168.0.20; 192.168.0.21; };

};

Nous initialisons les fichiers de zones avec le strict minimum, puisque les enregistrements seront gérés par Active Directory. Les six fichiers sont identiques, les quelques lignes ci-dessous vous permettent de les mettre en place :

[root@ns]# cat /var/named/masters/_msdcs.lab.evenit.info.zone << EOF

$TTL 1D

@   IN SOA ns.lab.evenit.info. hostmaster.lab.evenit.info. (

     2012090101 1D 1H 1W 3H )

   IN NS ns1.lab.evenit.info.

   IN NS ns2.lab.evenit.info.

EOF

[root@ns]# chown root:named /var/named/masters/_msdcs.lab.evenit.info.zone

[root@ns]# chmod 0660 /var/named/masters/_msdcs.lab.evenit.info.zone

[root@ns]# for z in _sites _udp _tcp DomainDnsZones ForestDnsZones

> do

> cp -p /var/named/masters/_msdcs.lab.evenit.info.zone /var/named/masters/${z}.lab.evenit.info.zone

> done

Il y a quelques opérations à réaliser du côté des serveurs Microsoft Windows. Ils sont à la limite du périmètre dans ce magazine ; je me contenterai donc de vous indiquer les opérations à réaliser sans entrer dans le détail (juste des liens vers les commandes utiles) :

1. Promouvoir le serveur au rôle de contrôleur de domaine, avec redémarrage final [7.2].

2. Configurer le DNS sur l'interface réseau du contrôleur : ajouter les adresses IP de ns1 et ns2 [7.3].

3. Redémarrer pour que la configuration se propage pour les domaines hébergeant les enregistrements SRV. [7.4]

4. Désactiver la récursivité sur le DNS AD.

5. Activer le nettoyage automatique des enregistrements obsolètes.

Comme d'habitude, de nombreux redémarrages sont nécessaires, mais ils ne sont qu'une goutte d'eau dans l'océan de redémarrages qu'est la vie d'un serveur Windows.

Note

Pour cette section, j'ai trouvé l'inspiration sur deux sites : http://fengnet.com/book/DNS.and.BIND.5th.Edition/dns5-CHP-17-SECT-10.html et http://www.kuro5hin.org/story/2009/2/1/235152/2142. Le premier est le plus proche de ce que je documente.

8. Limitation avec les vues

Nous avons autorisé Active Directory à écrire dans certaines zones, mais nous ne souhaitons peut-être pas permettre à tout Internet d'accéder à ces enregistrements. Nous allons donc mettre en place sur les serveurs esclaves, ns1 et ns2, le mécanisme de vues de Bind qui permet de ne pas présenter les mêmes zones à tous les clients, en se basant sur leur adresse IP (vos routeur et/ou firewall feront le tri des clients pour limiter l'usurpation d'adresse IP).

Cette fonctionnalité est simple : il suffit de créer un conteneur de type view, de décrire les zones concernées, et de limiter l'accès par la directive match-clients. Comme les zones de notre domaine principal et les zones inversées doivent être vues aussi bien de l'intérieur que de l'extérieur, et que nous aimons factoriser l'effort, nous allons séparer les zones dans des fichiers que nous inclurons dans /etc/named.conf. Cela donne :

view "internal" {

 match-clients { internal_nets; };

 include "/etc/named.internal.conf" ;

 include "/etc/named.external.conf" ;

};

view "external" {

 match-clients { any; };

 include "/etc/named.external.conf" ;

};

9. Sécurisation des mises à jour avec GSS-TSIG

Les plus attentifs d'entre vous auront remarqué que notre ACL contenant les contrôleurs de domaines est basée sur l'adresse IP des serveurs, ce qui n'est toujours pas recommandé. Mais cela nous donne l'opportunité d'utiliser une autre méthode de sécurisation des échanges : GSS-TSIG. Le préfixe GSS nous ramène à GSS-API [9.1], dont l'implémentation la plus répandue est Kerberos. Et nous retrouvons l'une des fonctionnalités de base d'Active Directory : un Key Distribution Center (KDC) pour le protocole Kerberos.

Je ne vous fournis pas d'explications sur le fonctionnement de Kerberos : pour cela, je vous renvoie au n°143 de GNU/Linux Magazine France qui traite du sujet en profondeur. Par contre, je vous fournis les étapes nécessaires à la mise en œuvre de GSS-TSIG. Et en tout premier lieu, nous devons générer le fichier keytab pour le service DNS hébergé par le serveur ns.lab.evenit.info, puisque c'est lui qui dispose des fichiers de zones pour les sous-domaines gérés par Active Directory (_msdcs...).

C:\Users\Administrator> net computer \\ns /add

C:\Users\Administrator> ktpass -out dns_ns.lab.evenit.info.keytab ^

  -princ DNS/ns.lab.evenit.info@LAB.EVENIT.INFO ^

 -ptype KRB5_NT_PRINCIPAL ^

 -mapuser LAB\ns ^

 -crypto all -DesOnly ^

 +rndPass

J'explicite les commandes Windows exécutées ci-dessus. La première crée un compte de type Ordinateur nommé « ns ». La seconde génère un mot de passe pour le principal DNS/ns.lab.evenit.info@LAB.EVENIT.INFO (-princ) :

- en l'associant au compte d'ordinateur précédemment créé LAB\ns (-mapuser),

- en ne se limitant pas aux algorithmes basés sur DES (-DesOnly),

- en créant un mot de passe aléatoire (+rndPass),

- en écrivant le résultat dans le fichier dns_ns.lab.evenit.info.keytab.

Nous copions le fichier keytab sur le serveur ns (j'utilise WinSCP [9.2]) et l'installons dans l'arborescence chrootée de Bind : ce fichier n'est pas monté par le script de démarrage, car il est spécifique à l'application et doit donc n'apparaître que dans l'arborescence dédiée à Bind.

[root@ns]# install -o root -g named -m 0640 /root/dns_ns.lab.evenit.info.keytab /var/named/chroot/etc/krb5.keytab

Nous configurons Kerberos via le fichier /var/named/chroot/etc/krb5.conf :

[logging]

default = FILE:/var/log/krb5libs.log

kdc = FILE:/var/log/krb5kdc.log

admin_server = FILE:/var/log/kadmind.log

[libdefaults]

default_realm = LAB.EVENIT.INFO

dns_lookup_realm = false

dns_lookup_kdc = true

ticket_lifetime = 24h

renew_lifetime = 7d

forwardable = true

[realms]

LAB.EVENIT.INFO = {

  kdc = _kerberos._tcp.lab.evenit.info

  admin_server = _kerberos._tcp.lab.evenit.info

}

[domain_realm]

.lab.evenit.info = LAB.EVENIT.INFO

lab.evenit.info = LAB.EVENIT.INFO

Nous y définissons le royaume par défaut à LAB.EVENIT.INFO. Nous indiquons les serveurs pour ce royaume, ainsi que l'association nom de domaine / royaume. Vous noterez au passage que les noms de serveurs sont des enregistrements de type SRV gérés par Active Directory et qu'il a créés à la section 7 ; cela impose de spécifier l'option dns_lookup_kdc = true, mais correspond toujours à un serveur puisque Active Directory met à jour cet enregistrement en fonction de la configuration du domaine.

Il ne nous reste plus qu'à configurer Bind et c'est presque le plus simple. Tout se passe dans le fichier /etc/named.conf. Dans un premier temps, nous indiquons à Bind quel est le fichier contenant la keytab à utiliser en ajoutant la ligne suivante dans la section options du fichier :

tkey-gssapi-keytab "/etc/krb5.keytab"

Puis, pour chaque zone que nous souhaitons qu'Active Directory mette à jour, nous ajoutons la ligne suivante dans la déclaration de zone, en supprimant la ligne allow-update qui est incompatible avec l'option update-policy (Bind vous le dira au démarrage). Cette option permet de spécifier des règles de mise à jour très fines. Dans notre cas, nous nous contentons d'autoriser le contrôleur de domaine (ou plus précisément le principal associé) à mettre à jour tous les enregistrements de la zone (ANY) et de ses éventuelles zones filles (zonesub).

update-policy { grant ADDC01\$@LAB.EVENIT.INFO zonesub ANY; };

Comme nous répétons les mêmes règles pour toutes les zones gérées par Active Directory (_msdcs...), nous pouvons factoriser en mettant ces règles dans un fichier annexe que nous incluons (directive include) et qui sera plus simple à maintenir.

Et pour terminer, il faut redémarrer le service named pour activer cette nouvelle configuration, et le contrôleur de domaine pour nous assurer que les zones sont bien mises à jour. Et nous y sommes.

Note

Cette section a été la plus difficile à mettre en œuvre et surtout à valider. Mes sources d'inspiration ont été : http://www.netlinxinc.com/netlinx-blog/45-dns/136-how-to-implement-gss-tsig-on-isc-bind.html et http://jpmens.net/2012/06/29/dynamic-dns-updates-using-gss-tsig-and-kerberos/.

10. Récapitulatif de la configuration

Mon dévoué relecteur m'a suggéré de reprendre en fin d'article la configuration du serveur maître. L'idée me paraissant bonne, je l'ai reprise et vous fournis ci-dessous ledit fichier : /etc/named.conf.

include "/etc/ns-ns1.key";

include "/etc/ns-ns2.key";

server 192.168.0.20 { keys { ns-ns1.; }; };

server 192.168.0.21 { keys { ns-ns2.; }; };

acl internal_nets { 192.168.0.0/24; };

acl lvl2_ns { key ns-ns1.; key ns-ns2.; };

acl ad_dcs { 192.168.0.30; };

options {

 listen-on port 53 { 127.0.0.1; 192.168.0.10; };

 listen-on-v6 port 53 { none; };

 directory "/var/named";

 dump-file "/var/named/data/cache_dump.db";

 statisctics-file "/var/named/data/named_stats.txt";

 memstatistics-file "/var/named/data/named_mem_stats.txt";

 allow-query { localhost; };

 recursion yes;

 allow-recursion { ad_dcs; };

 tkey-gssapi-keytab "/etc/krb5.keytab";

 dnssec-enable no;

 dnssec-validation no;

 dnssec-lookaside auto;

 bindkeys-file "/etc/named.iscdlv.key";

 managed-keys-directory "/var/named/dynamic";

};

logging {

        channel default_debug {

                file "data/named.run";

                severity dynamic;

        };

};

zone "." IN {

        type hint;

        file "named.ca";

};

include "/etc/named.rfc1912.zones";

include "/etc/named.root.key";

zone "lab.evenit.info" {

 type master;

 file "masters/lab.evenit.info.zone";

 allow-query { lvl2_ns; ad_dcs; };

 allow-transfer { lvl2_ns; };

 allow-update { none; };

 also-notify { 192.168.0.20; 192.168.0.21; };

};

zone "0.168.192.in-addr.arpa" {

 type master;

 file "masters/0.168.192.in-addr-arpa.zone";

 allow-query { lvl2_ns; ad_dcs; };

 allow-transfer { lvl2_ns; };

 allow-update { none; };

 also-notify { 192.168.0.20; 192.168.0.21; };

};

zone "_msdcs.lab.evenit.info" {

 type master;

 file "masters/_msdcs.lab.evenit.info.zone";

 allow-query { lvl2_ns; ad_dcs; };

 allow-transfer { lvl2_ns; };

 update-policy { grant ADDC01\$@LAB.EVENIT.INFO zonesub ANY; };

 check-names ignore;

 also-notify { 192.168.0.20; 192.168.0.21; };

};

zone "_sites.lab.evenit.info" {

 type master;

 file "masters/_sites.lab.evenit.info.zone";

 allow-query { lvl2_ns; ad_dcs; };

 allow-transfer { lvl2_ns; };

 update-policy { grant ADDC01\$@LAB.EVENIT.INFO zonesub ANY; };

 check-names ignore;

 also-notify { 192.168.0.20; 192.168.0.21; };

};

zone "_tcp.lab.evenit.info" {

 type master;

 file "masters/_tcp.lab.evenit.info.zone";

 allow-query { lvl2_ns; ad_dcs; };

 allow-transfer { lvl2_ns; };

 update-policy { grant ADDC01\$@LAB.EVENIT.INFO zonesub ANY; };

 check-names ignore;

 also-notify { 192.168.0.20; 192.168.0.21; };

};

zone "_udp.lab.evenit.info" {

 type master;

 file "masters/_udp.lab.evenit.info.zone";

 allow-query { lvl2_ns; ad_dcs; };

 allow-transfer { lvl2_ns; };

 update-policy { grant ADDC01\$@LAB.EVENIT.INFO zonesub ANY; };

 check-names ignore;

 also-notify { 192.168.0.20; 192.168.0.21; };

};

zone "DomainDnsZones.lab.evenit.info" {

 type master;

 file "masters/DomainDnsZones.lab.evenit.info.zone";

 allow-query { lvl2_ns; ad_dcs; };

 allow-transfer { lvl2_ns; };

 update-policy { grant ADDC01\$@LAB.EVENIT.INFO zonesub ANY; };

 check-names ignore;

 also-notify { 192.168.0.20; 192.168.0.21; };

};

zone "ForestDnsZones.lab.evenit.info" {

 type master;

 file "masters/ForestDnsZones.lab.evenit.info.zone";

 allow-query { lvl2_ns; ad_dcs; };

 allow-transfer { lvl2_ns; };

 update-policy { grant ADDC01\$@LAB.EVENIT.INFO zonesub ANY; };

 check-names ignore;

 also-notify { 192.168.0.20; 192.168.0.21; };

};

Conclusion

Nous voici arrivés au terme de cet article. Vous aurez pu (re)découvrir les grandes lignes de la configuration d'un serveur DNS basé sur Bind. L'infrastructure déployée est assez gourmande en ressources, puisque nous utilisons trois serveurs dédiés à cette fonction. Si vous ne disposez pas d'autant de serveurs, vous réussirez à réduire à un serveur (pour la redondance, voyez avec un autre lecteur qui a les mêmes limitations pour croiser vos domaines). L'intégration avec Active Directory et Kerberos devrait vous permettre de travailler en bonne intelligence avec vos collègues administrateurs de domaines Active Directory, sans négliger la sécurité.

Je tiens à remercier Sébastien Bonnegent, ingénieur système à l'INSA de Rouen, pour son regard avisé lors de la relecture et pour son amitié. Et un remerciement spécial à ma compagne, qui accepte sans (trop) se plaindre les heures que j'ai passées sur mon ordinateur à la rédaction de cet article.

Bibliographie

[0.1] http://fr.wikipedia.org/wiki/Domain_Name_System

[2.1] RFC 1912 : http://www.ietf.org/rfc/rfc1912.txt

[2.2] http://docs.1h.com/Bind_mounts

[4.1] http://www.afnic.fr/fr/produits-et-services/services/zonecheck/

[4.2] http://www.afnic.fr/fr/

[5.1] http://fr.wikipedia.org/wiki/Transfert_de_zone_DNS

[7.1] http://www.ietf.org/rfc/rfc2136.txt

[7.2] dcpromo : http://technet.microsoft.com/fr-fr/library/cc732887(v=ws.10).aspx

[7.3] dnscmd : http://technet.microsoft.com/en-us/library/cc756116(v=ws.10).aspx

[7.4] shutdown : http://technet.microsoft.com/en-us/library/cc732503(v=WS.10).aspx

[9.1] http://en.wikipedia.org/wiki/Generic_Security_Services_Application_Program_Interface

[9.2] http://winscp.net

Tags : Architecture, DNS