26/11/2024

Sécuriser les flux DNS : TSIG et DNSSEC

I. Présentation

On vient de voir les différents principes permettant de sécuriser un peu plus le serveur de noms DNS. Mais, les flux échangés entre les clients et les serveurs DNS circulent toujours en clair sur le réseau. Le meilleur moyen de palier à ce problème est encore de chiffrer le trafic. Il existe alors deux alternatives :

  • L’utilisation de TSIG (Transaction SIGnature) permettant d’effectuer un transfert d’un serveur maître vers son esclave, uniquement après vérification du partage d’une clé secrète entre les deux protagonistes.
  • L’utilisation du protocole DNSSEC (DNS SECurity), permettant de signer cryptographiquement les zones, à l’aide d’une clé de zone.

Dans le premier cas, on renforce la méthode de transfert basée sur l’adresse IP standard. Ainsi, un pirate devra non seulement accéder à cette même adresse IP pour transférer la zone, mais, il devra aussi connaître la clé secrète.

Dans le second cas, on vérifie que les informations relatives à une zone spécifique provient d’un serveur de noms les ayant signées avec sa clé privée. Le destinataire de l’échange doit posséder la clé publique du serveur de noms, pour déchiffrer le trafic.

REMARQUE : depuis la version BINDv9 il existe une méthode d’authentification de message SIG(0) utilisant lui aussi un mécanisme  de bi-clé publique/privée.

II. Utilisation de TSIG

Le protocole de réseau TSIG est décrit dans la RFC2845 et est principalement utilisé par le système de résolution de noms, pour fournir une forme d’authentification concernant les mises à jour dynamiques des bases de données DNS. Ce mécanisme peut également être utilisé entre les serveurs, pour les requêtes d’échange.

Ce protocole utilise un secret partagé ainsi qu’une fonction de hachage unidirectionnelle pour apporter un certain niveau de sécurité via de la cryptographie et identifier chacune des extrémités d’une connexion, autorisant la mise à jour (ou la réponse à une demande de mise à jour DNS).

Les requêtes DNS sont, en règle générale, anonymes (sauf celles que l’on peut effectuer au travers du protocole DNSSec, que l’on verra plus loin). Aussi, les mises à jour doivent être authentifiées, car elles modifient la structure de nommage du réseau : qu’il s’agisse d’Internet ou d’un réseau privé ou d’entreprise.

IMPORTANT : afin d’activer TSIG, il faut alors partager un secret entre le client (ou le serveur DNS esclave), souhaitant effectuer la mise à jour, et le serveur DNS faisant autorité sur la zone. L’authentification reste à la charge du serveur DNS maître.

Etant donné que la demande de mise à jour peut se faire au travers d’un réseau non sécurisé, comme Internet, c’est la fonction de hachage unidirectionnelle qui va permettre d’empêcher un tiers malveillant de lire la clé secrète en écoutant le trafic circulant sur le réseau, entre le serveur DNS et son esclave (ou le client).

Généralement, l’utilisation d’un élément temporel (appelé timestamp), au sein du protocole, permet d’éviter une attaque par "rejeu". L’utilisation de TSIG nécessite systématiquement la synchronisation avec un serveur de temps NTP.

RAPPEL : une mise à jour, au sens DNS du terme, est un ensemble d’instructions destinées à un serveur DNS faisant autorité sur la zone concernée. Ceci est décrit dans la RFC2136. Cet échange contient alors l’entête, la zone à modifier et les prérequis devant être satisfaits ainsi que les enregistrements RR, devant ou pouvant être modifiés.

Par convention, le protocole TSIG ajoute à ce flux, un dernier enregistrement comprenant l’élément horaire (le fameux timestamp), une empreinte hachée de la requête, et le nom de la clé secrète partagée, utilisée pour signer ladite requête. On utilise globalement le programme nsupdate pour l’utilisation de TSIG afin de réaliser les modifications. Sinon, on utilise également l’outil dig (que l’on verra dans le troisième module de ce cours), pour permettre à TSIG d’effectuer les requêtes ou les transferts de zones authentifiées. L’enregistrement TSIG possède le même format que les enregistrements de requêtes concernant les mises à jour. La signification des champs le composant est décrit par la RFC1035 :

Dans l’immédiat, on ne possède pas encore le fameux secret partagé. Il faut donc l’initialiser. En partant de l’hypothèse que l’on a deux serveurs DNS : un maître (saturne.mydmn.org) et un esclave (jupiter.mydmn.org), i faut alors générer la paire de clés sur le serveur principal, dans le répertoire /var/named/chroot/named/etc/named/keys :

$ cd /var/named/chroot/named/etc/named
$ mkdir keys
$ chown –R named:named keys

La création de cette paire de clé peut se faire en exécutant la commande suivante:

$ dnssec-keygen –a HMAC-SHA512 –b 512 –n HOST tsig-saturne.mydmn.org-jupiter.mydmn.org

REMARQUE : cette commande génère deux fichiers: un avec l’extension .key, contenant la clé publique de l’échange et l’autre, avec l’extension .private. En fournissant un nom composé, en cas de nombreuses clés à administrer, ou de nombreuses zones à gérer, on saura toujours ainsi laquelle utiliser.

On peut ensuite modifier le fichier de configuration du serveur maître et y ajouter la rubrique concernant le secret partagé :

// * TSIG *
key tsig-saturne.mydmn.org-jupiter.mydmn.org. {
     algotithm hmac-sha512;
     secret   "aJW4gQ2dkH8xxCcnRHFM67fmvqn43tZoito0cPusYFUJC6FmGxMfDU2dtSBrMRw==";
} ;

La clé décrite par la ligne secret représente le contenu du fichier de clé publique, qu’il est possible de lister en exécutant la commande ci-dessous :

$ cat Ktsig*.key

IMPORTANT : si la notification de changement est portée au niveau de la section des zones du serveur secondaire, il faut aussi remplacer l’adresse du serveur primaire, par l’échange de clé TSIG. Ainsi, on doit donc modifier le fichier de configuration au niveau du paragraphe allow-tansfer pour y porter les lignes ci-dessous :

 allow-transfer {
     key tsig-saturne.mydmn.org-jupiter.mydmn.org.;
};

Il ne reste plus qu’à éditer le fichier de clé privée que l’on a généré ci-dessus pour y mentionner aussi le secret partagé et modifier le fichier named.conf du serveur secondaire (ou serveur esclave) pour disposer des lignes suivantes :

key tsig-saturne.mydmn.org-jupiter.mydmn.org. {
        algorithm hmac-sha512;
        secret  "aJW4gQxCcnRHFM67fmvito0cPusYFUJC6FmGxMfDU2dtSBrMRw==";
};

server 192.168.1.172 {
        keys {
        tsig-saturne.mydmn.org-jupiter.mydmn.org.;
};

L’adresse IP mentionnée ici correspond à celle du serveur DNS maître. On peut alors arrêter et redémarrer le service named, à la fois sur le serveur DNS primaire et sur ses esclaves, afin que la nouvelle configuration prenne effet. Si l’ensemble est correctement configuré, les deux serveurs DNS maître et esclave devraient pouvoir échanger leur configuration en utilisant alors le protocole TSIG.

En cas de problème, on peut consulter les journaux de logs et vérifier si les transferts de zones se sont correctement effectués. Si l’on souhaite tester le fonctionnement de ce mécanisme, il suffit d’exécuter la commande dig de la façon suivante :

$ dig –y hmac-sha512:tsig-saturne.mydmn.org-jupiter.mydmn.org:<Secret> @192.168.1.172 AXFR mydmn.org

III. Utilisation de DNSSEC

Dans certains cas, l’utilisation du protocole TSIG ne suffit pas. Il est parfois nécessaire de protéger également le cache DNS. Ce dernier est généralement intégré à BIND au travers de la commande rndc et permet la visualisation du contenu du cache :

$ rndc dumpdb –cache

Afin d’empêcher quiconque ne devrait pas visualiser cette partie du service, il est possible d’utiliser alors le protocole DNSSEC dont le mode de fonctionnement s’approche de celui de TSIG avec un échange de clé partagé. Mais, DNSSEC va plus loin en signant les enregistrements DNS et en plaçant cette même signature au sein de l’annuaire DNS. Cela permet aux clients de récupérer la signature et, moyennant la présence de la clé secrète du serveur DNS principal, ils pourront vérifier que les données manipulées sont exactes.

Le protocole DNSSEC utilise un mécanisme reposant sur une paire de clé ayant des rôles complémentaires. La clé privée permet de créer la signature par chiffrement et la clé publique effectue la vérification des signatures, par déchiffrement. On peut schématiser le principe par le schéma ci-dessous :

En d’autres termes, contrairement à d’autres protocoles, comme SSL/TLS, DNSSEC ne sécurise pas uniquement un canal de communication, mais, il protège  également les données et les enregistrements au sein de l’annuaire DNS, de bout-en-bout. Ce principe reste efficace, même lorsqu’un serveur intermédiaire (appelé alors DNS menteur), trahit l’intégrité des données.

Avant toute modification potentielle de la configuration, il est conseillé de :

  • Faire une sauvegarde complète de l’arborescence /var/named et du fichier conf.
  • S’assure que le serveur DNS est correctement synchronisé avec le serveur NTP.
  • S’assurer que le domaine est correctement configuré en exécutant la commande :
$ named-checkzone –z

Par soucis de clarté, il vaut mieux créer un répertoire par zone à manipuler. Ainsi, on ne risquera pas de mélanger les différentes clés générées.

Exemple : pour le domaine mydmn.org et son sous-domaine mydmn.org.rev :

$ cd /var/named/chroot/named/etc/namedb
$ mkdir mydmn.org mydmn.org.rev
$ chown –R named:named mydmn.org mydmn.org.rev

REMARQUE : pour les besoins de l’exemple, on administrera le domaine mydmn.org dont la définition (non chiffrée), se trouve dans le fichier de zone mydmn.org.hosts, au niveau du répertoire /var/named/chroot/named/etc/namedb. Le fichier de zone, une fois signé, après la mise en place de DNSSEC, s’appellera alors mydmn.org.hosts.signed.

Il faut alors partager plusieurs clés :

  • Une clé KSK (Key Signing Key) servant à signer les clés.
  • Une clé ZSK (Zone Signing Key) servant à signer la zone.

On peut alors passer à la création, à proprement dit, des clés, en commençant par la clé ZSK, que l’on va créer à l’aide d’une clé de 4096bits en RSASHA256 :

$ dnssec-keygen –a RSASHA256 –b 4096 –n ZONE mydmn.org
Generating key pair………………………………………………………………..
Kmydmn.org.+008+55176

On peut alors générer la clé KSK, de la même façon, en utilisant également une clé de 4096bits en RSASHA256 :

$ dnssec-keygen –f KSK -a RSASHA256 –b 4096 –n ZONE mydmn.org
Generating key pair………………………++……………………………………..
Kmydmn.org.+008+36124

Ainsi, dans le répertoire de travail, on devrait maintenant disposer de quatre fichiers de clés: une paire de clé publique/privée pour la clé ZSK et une autre paire, pour la clé KSK. Il faut désormais ajouter les clés publiques, contenant les enregistrements DNSKEY, dans le fichier de zone, à l’aide d’une boucle telle que celle-ci-dessous :

$ for key in `ls Kmydmn.org*.key`
> do
> echo "\$INCLUDE /var/named/chroot/named/etc/namedb/$key" >> mydmn.org.hosts
> done

Il faut alors signer la zone, en exécutant l’instruction suivante :

$ dnssec-signzone –A -3 $(head –c 1000 /dev/random | sha1sum | cut –b 1-16) –N INCREMENT –o mydmn.org –t mydmn.org.hosts

Ainsi, cela permet de signer la zone tout en générant un fichier mydmn.org.hosts.signed. Si l’on souhaite lire le contenu de ce fichier, on ne pourra le faire qu’avec la commande suivante :

$ named-compilezone –D –f raw –o – mydmn.org db.mydmn.org.hosts.signed

ATTENTION : lorsqu’une zone est signée, via le protocole DNSSEC, son administration devient un peu plus complexe. Il faut d’abord geler la zone afin de travailler dessus. Et, à la fin des modifications, il faut alors libérer la zone. Cette dernière action provoque un rechargement de celle-ci et la génération d’une nouvelle signature :

$ rndc freeze mydmn.org
…
// Modifications apportées
…
$ rndc thaw mydmn.org

Une fois la signature de la zone obtenue, il faut alors l’ajouter dans la configuration de BIND. Il faut pour cela, éditer le fichier de configuration named.conf et ajouter, au niveau du bloc option{}, les instructions concernant DNSSEC :

options {
    // ...
    // DNSSEC
    dnssec-enable yes;
    dnssec-validation yes;
    dnssec-lookaside auto;
};

On peut ensuite informer le serveur BIND qu’il existe désormais une nouvelle zone signée et la déclarée (généralement dans le fichier named.rfc1912.zones), dans la partie concernant les zones :

zone "mydmn.org" {
 type master;
 // zone : normale
 //file "/var/ named/mydmn.org.hosts";
 // zone : signed by DNSSEC
 file "/var/named/chroot//named/etc/namedb/mydmn.org.hosts.signed";
 allow-transfer { 192.168.1.172; };
 allow-query { any; };
 notify yes;
};

Avant de recharger la configuration, il faut alors effectuer les mêmes déclarations pour la zone inverse. Seulement alors, on peut recharger la nouvelle configuration :

$ service named reload

Afin de s’assurer que l’ensemble est cohérent et fonctionnel, on peut réaliser quelques tests en utilisant (une fois de plus), la commande dig :

$ dig +dnssec mydmn.org a        (permet d’afficher le nom du domaine)
$ dig +cd +multi mydmn.org dns.key     (permet d’afficher les clés)

Dans le cas où tout fonctionne correctement, on devrait alors voir des enregistrements DNSKEY et RRSIG apparaître. Nous avons fait la moitié du chemin, car maintenant, lors de chaque modification ou édition d’un fichier de zone, il faut signer à nouveau la zone pour que la configuration soit opérationnelle.

ATTENTION : toute signature du fichier de zone n’est valable que pendant une durée de trente jours, par défaut. Il faut donc en tenir compte et automatiser le processus ci-dessus.

Pour mettre en place quelque chose de quasi-automatique, concernant le processus de signature du fichier de zone (après modification), on va générer un script bash permettant de :

  • Modifier le n° de série du fichier de zone et lui attribuer la date du jour
  • Créer un fichier de zone signé avec DNSSEC
  • Redémarrer le service named
  • Envoyer un message au contact de la zone pour l’informer d’une nouvelle signature

On placera ce script dans le répertoire /var/named/chroot/named/etc/namedb et on l’appellera dnssec-signing.sh. On doit bien sûr le rendre exécutable :

$ chmod +x dnssec-signing.sh

Ce script se compose alors des lignes suivantes :

#!/bin/bash
# -----------------------------------------------------#
# Script Name: dnssec-signing.sh
# Version: 1.4
# Description: permet de signer les fichiers de zones après modification via une crontab
#
ZONEDIR="/var/named/chroot/named/etc/namedb"
# The DNS service that you are using : bind9, named ...
DNSSERVICE="named"
#
# -----------------------------------------------------#
# ZONE : the zone (passed as first argument)
ZONE=$1

# ZONEFILE : the zone file (passed as second argument)
ZONEFILE=$2

# Here we go !
cd $ZONEDIR

# check what's currently loaded
/usr/sbin/named-checkzone $ZONE $ZONEFILE.signed

# verification du serial
OLDSERIAL=`/usr/sbin/named-checkzone $ZONE $ZONEFILE | egrep -ho '[0-9]{10}'`

# generation d’un nouveau serial au format: YYYYMMDDHH (year, month, day, hour)
NEWSERIAL=`date +%Y%m%d%H -d "today"`

# if $NEWSERIAL is less than or equal to $OLDSERIAL
# then it means the zone file has already been generated today
# so let's compute the difference and increment +1

if [ "$NEWSERIAL" -lt "$OLDSERIAL" ]; then
DIFF=$(($OLDSERIAL-$NEWSERIAL))
NEWSERIAL=$(($NEWSERIAL+$DIFF+1))
fi

# Write new serial +1 in zone file
sed -i 's/'$OLDSERIAL'/'$(($NEWSERIAL+1))'/' $ZONEFILE

# signature et increment du serial
/usr/sbin/dnssec-signzone -A -3 $(head -c 1000 /dev/random | sha1sum | cut -b 1-16) -N increment -o $1 -t $2

# redemarrage du service BIND
/usr/sbin/service $DNSSERVICE reload

# verification de la configuration chargee
/usr/sbin/named-checkzone $ZONE $ZONEFILE.signed
CHECKRESULT=`/usr/sbin/named-checkzone $ZONE $ZONEFILE.signed`

# Envoi d’email au contact de la zone
# Admin email to send notifications to
MAILTO="root@$ZONE"
echo "Hello,\n\n votre domaine, $ZONE, vient d’être signé par DNSSEC.\n\nLe serial est incrementé :$NEWSERIAL.\n. Le service $DNSSERVICE, a été rechargé.\n\nLa zone $ZONEFILE.est à nouveau signée.\n----------\n\n$CHECKRESULT \nDNSSEC Zone" | mail -s "[OK] $ZONE a été signée avec DNSSEC" "$MAILTO"

Pour déclencher la construction d’une nouvelle signature, après avoir modifié un fichier de zone, il suffit d’invoquer le script avec le formalisme suivant :

$ ./dnssec-signing.sh <Domaine> <Fichier Zone>

Exemple : pour le domaine mydmn.org il faudra exécuter:

$./dnssec-signing.sh mydmn.org /var/named/chroot/named/etc/namedb/mydmn.org.hosts

Afin de ne pas avoir à se préoccuper du délai court de validité des signatures, on peut également ajouter une tâche planifiée dans la crontab pour signer les zones tous les 20 jours (par exemple), et recharger la configuration BIND, en utilisant le script précédemment initialisé :

$ crontab –e
0 0 */20 * * sh /var/named/chroot/named/etc/namedb/dnssec-signing.sh mydmn.org /var/named/chroot/named/etc/namedb/mydmn.org.hosts

Ainsi, le processus sera totalement automatisé et toute modification des enregistrements DNS du domaine sera effectué au sein des fichiers de zones non signés. Puis, la tâche planifiée utilisera alors le script décrit ci-dessus pour signer et regénérer la configuration DNSSEC.

IMPORTANT : il ne faut pas perdre de vue que les clés ZSK et KSK ont également une durée de vie limitée. Donc, tôt ou tard, il faudra les recréer afin de garantir la sécurité des clés de chiffrement DNSSEC. Toutefois, il est conseillé de conserver les anciennes clés un certain temps après leur révocation. Ainsi, les clients (ou résolveurs) peuvent continuer de mettre à jour leurs enregistrements avec les nouvelles clés, tout en garantissant les connexions, avec les anciennes.

Si le domaine est enregistré auprès d’un hébergeur (registrar ou registry), il faut également modifier la configuration chez le fournisseur, en précisant le type de chiffrement utilisé. Par exemple :

On peut alors ajouter le contenu des deux clés ZSK et KSK précédemment générées, dans le formulaire présenté s’affichant afin que celle-ci soient automatiquement incluses au registre du domaine. Si l’on doit générer de nouvelles clés, il suffit alors de refaire la démarche décrite ci-dessus, à l’identique en suivant les points ci-dessous :

  • Exécuter les deux commandes de génération de clés ZSK et KSK.
  • Ajouter les clés publiques dans le fichier de zone (éventuellement les déclarer chez le FAI)
  • Signer le fichier de zone à l’aide du script bash (en crontab)

Soyons clair, l’intérêt de chiffrer l’intégralité des flux circulant entre les serveurs DNS et leurs clients ainsi que le fait de chrooter l’arborescence des serveurs, n’intéressent que les serveurs de noms publics. Pour des réseaux privés ou à l’intérieur d’un LAN, il n’est pas nécessaire d’utiliser DNSSEC (à la limite TSIG, et encore) ni de confiner les fichiers de zones et de configuration au sein d’un répertoire chroot.

Par contre, rien n’empêche de redonder vos serveurs en ajoutant même trois ou quatre serveurs esclaves, selon l’importance de votre architecture, permettant d’effectuer de l’équilibrage de charge.

IV. Conclusion sur le module

Nous venons de voir de quoi le service BIND se composait et comment créer un service de résolution de noms DNS et le configurer. On a également vu qu’il était plus prudent de toujours redonder ce serveur à l’aide d’un second serveur DNS esclave (voire même d’un troisième si possible).

Ensuite, on a vu quels étaient les failles potentielles d’un service de résolution de noms et comment s’affranchir des problématiques de sécurité en utilisant un protocole tel que TSIG, permettant de signer les flux de mises à jours, entre un serveur DNS primaire et un serveur DNS secondaire.

Malheureusement, dans certaines entreprises, la gestion du cache non chiffrée reste un problème majeur et permet à certains pirates de récupérer des informations susceptibles de le servir par la suite, à l’instar d’un hacker faisant les poubelles d’une entreprises pour récupérer de l’information.  Pour pallier à ce problème, on a recours alors au protocole DNSSEC. On a donc décrit la procédure permettant de mettre en œuvre et d’utiliser un tel protocole tout en automatisant les différentes phases du processus.

author avatar
Philippe PIERRE
A exercé de nombreuses années en tant qu'administrateur de base de données et comme administrateur Système Unix/Linux. Il a enseigné les réseaux au CNAM (Paris). Aujourd'hui, employé en tant qu'ingénieur infrastructure, au sein d'un laboratoire pharmaceutique et administrant un cluster de calculs HPC, il connaît parfaitement les environnements GNU/Linux dans le cadre d'une entreprise et des systèmes de haute disponibilité. Il aime partager son expérience.
Partagez cet article Partager sur Twitter Partager sur Facebook Partager sur Linkedin Envoyer par mail