lalahop
Categorie: DNS & DNSSEC

DNSSEC pour 2013

Pour cette nouvelle année, j'ai décidé de signer mes zones avec DNSSEC. Ce billet est donc une sorte de retour d'expérience/un avis mélangé à un semblant de tutoriel OpenDNSSEC.

Table des matières

Ressources

Si vous débutez avec DNSSEC, je vous recommande la lecture suivante : DNS : Attaques et sécurisation.

Pour suivre la suite de ce billet, je vous recommande aussi la lecture suivante : RFC 6781: DNSSEC Operational Practices, Version 2 chez Stéphane Bortzmeyer.

Objectifs

  • Signer une zone qui se trouve en dessous de fr. La zone fr. est signée et accepte les délégations signées donc pas de problèmes particuliers à envisager.
  • Signer guiguishow.info. La zone info. est signée mais la soumission de délégations signées n'est pas encore ouverte à tous. Il faudra donc utiliser DLV et le registre DLV de l'ISC.
  • Signer des zones reverse v6 déléguées par Hurricane Electric (6in4). HE ne signe pas ses zones donc il faudra utiliser DLV et le registre DLV de l'ISC.

Note : je crois savoir que DLV est très peu utilisé. DNSSEC étant peu utilisé, en utilisant DLV, vous vous adressez à une masse très faible car l'usage de DLV pour valider une zone nécessite que le résolveur soit configuré et ce, avec le même registre DLV que celui que vous avez choisi pour soumettre votre délégation signée. Mais c'est "fun" en attendant la signature de plus de TLD.

Motivations

Mes motivations sont très simples : "for ze fun" et confronter la théorie (mes connaissances) à la réalité. Pour ce dernier point, un labo Netkit n'est pas suffisant pour avoir une vision complète de tous les aspects de DNSSEC et notamment de l'aspect opérationnel.

Données techniques

Avant de commencer, il faut bien comprendre que les bonnes valeurs pour ces "paramètres" ne sont pas fixes : les valeurs optimales sont encore en discussion (quelle durée de vie pour une clé ? pour une signature ?) et les valeurs réelles dépendent de vos choix et de votre infrastructure.

On veut que les zones soient signées hors-ligne : ce n'est pas le serveur maître faisant autorité sur la zone qui la signera.

Les paramètres des clés sont classiques. Choix du modèle à 2 paires de clés (qui n'est pas obligatoire). KSK : RSASHA256 - 2048 bits - durée de vie d'un an. ZSK : RSASHA256 - 1024 bits - durée de vie d'un mois.

Pour les signatures, une durée de vie d'une semaine est convenable.

Pour la preuve de non-existence, j'utiliserai NSEC3 : 10 itérations, sel de taille 8 changeant tous les 7 jours. Une sorte de compromis entre le RFC 5155 qui recommande de changer le sel à chaque signature et le RFC 6781 qui recommande de le changer en même temps que la ZSK. Après coup, l'usage de NSEC3 est le choix que je pourrais le plus remettre en question : mes zones sont "banales" : un SOA, des NS, un MX, un CNAME www, un A, un AAA, ... Rien qui ne puisse pas être très facilement deviné. L'usage de NSEC n'aurait pas été un problème.

La cryptographie employée par DNSSEC demande de la rigueur et une maintenance régulière sous peine de rendre une zone non-validable pendant un certain temps (qui dépend des TTL). J'ai décidé d'utiliser OpenDNSSEC, un logiciel qui prend en charge un certain nombre d'opérations (génération des clés, signature des zones, remplacement (rollover) des clés) et permet de limiter les erreurs lors de la réalisation des tâches restantes (soumission du DS à la zone parente lors d'un rollover de la KSK, par exemple).

Avant de continuer, je vous conseille les documentations suivantes concernant OpenDNSSEC :

Valider une zone en utilisant le registre DLV de l'ISC

Avant de commencer, nous allons voir comment configurer Unbound pour valider une zone en utilisant le registre DLV de l'ISC. Ce registre est simplement le plus gros registre DLV d'où mon choix pour publier mes DLV dans ce registre.

Dans un premier temps, il faut ajouter une ligne dans le fichier de configuration (/etc/unbound/unbound.conf) :

dlv-anchor-file: "/var/lib/unbound/dlv.isc.org.key"

Ensuite, il faut créer le fichier dlv.isc.org.key. Pour trouver la dernière KSK du registre en usage, voir : ISC's DLV Registry. À l'heure actuelle, mon fichier contient donc :

dlv.isc.org. IN DNSKEY 257 3 5 BEAAAAPHMu/5onzrEE7z1egmhg/WPO0+juoZrW3euWEn4MxDCE1+lLy2 brhQv5rN32RKtMzX6Mj70jdzeND4XknW58dnJNPCxn8+jAGl2FZLK8t+ 1uq4W+nnA3qO2+DL+k6BD4mewMLbIYFwe0PG73Te9fZ2kJb56dhgMde5 ymX4BI/oQ+cAK50/xvJv00Frf8kw6ucMTwFlgPe+jnGxPPEmHAte/URk Y62ZfkLoBAADLHQ9IrS2tryAe7mbBZVcOwIeU/Rw/mRx/vwwMCTgNboM QKtUdvNXDrYJDSHZws3xiRXF1Rf+al9UmZfSav/4NWLKjHzpT59k/VSt TDN0YUuWrBNh

Un reload d'Unbound et c'est prêt.

Pour tester : DLV Test Zones par DNS-OARC. Il faut que dig vous affiche le flag "AD". Si vous avez envie de rigoler, je vous conseille de résoudre les autres noms (a.nsec.dlvtest.dns-oarc.net txt, b.nsec.dlvtest.dns-oarc.net txt, ..., z.nsec.dlvtest.dns-oarc.net txt) car chaque lettre = un mot 😀 . Oui, un rien m'amuse. 😛

Installer OpenDNSSEC

L'installation d'OpenDNSSEC sous Debian est capricieuse : le dépôt squeeze-backports fournit une version en état de marche. Pour les dépôts stable et testing, je reste bloqué sur une erreur lors du lancement des démons :

ods-enforcerd: Error creating key in repository SoftHSM
ods-enforcerd: generate key pair: CKR_GENERAL_ERROR

Ce message d'erreur apparaît sur la liste de diffusion des devs d'OpenDNSSEC mais elle n'est pas résolue. Me concernant, ce n'est pas bien grave puisque j'utilise les backports pour installer certaines des applications de mon serveur, mais je voulais le signaler.

ÉDIT du 19/05/2013 à 21h00 : Une solution se trouve en filigrane ici : [Opendnssec-user] Error creating key in repository SoftHSM. On ajoute donc l'user opendnssec au groupe softhsm (voir ci-dessous) puis on applique les droits nécessaires sur le fichier /var/lib/softhsm/slot0.db : root:softhsm, 664. Ça marche sans problèmes sur une Wheezy sans avoir recours aux backports. Fin de l'édit

Sur une Squeeze utilisant les backports, l'installation se résume à :

apt-get install softhsm opendnssec
adduser opendnssec softhsm

L'ajout de l'utilisateur opendnssec au groupe softhsm permet d'éviter l'erreur :

SoftHSM: C_Initialize: Could not open the config file: /etc/softhsm/softhsm.conf

Mise en place basique du couple SoftHSM/OpenDNSSEC

Tout est détaillé sur le blog de S. Bortzmeyer dont j'ai donné le lien ci-dessus donc je vais être léger sur cette partie.

SoftHSM

Initialiser notre HSM :

softhsm --init-token --slot 0 --label "OpenDNSSEC"

Une petite astuce en passant : si vous oubliez le SO PIN de votre SoftHSM, il y a moyen de rattraper le coup (de reinit le SO PIN). D'abord, il faut calculer le hash de votre mot de passe via la fonction de hachage sha256 :

echo -n "123412341234" | shasum -a 256 | tr '[:lower:]' '[:upper:]'

Attention : ici, le SO PIN est 1234, pas 123412341234 ! Il faut chaîner 3 fois de suite le SO PIN désiré pour obtenir le bon hash.

Ensuite, comme SoftHSM utilise, par défaut, sqlite, le changement du SO PIN revient à une simple requête SQL (en root, bien sûr) :

sqlite3 /var/lib/softhsm/slot0.db
sqlite> UPDATE Token SET value='66C782E8F95BA958F28ADAAE576C42A263C2449AF416FB844499BEF7FD41B2D0' WHERE variableID='1';

Bien sûr, remplacez "66C782E8F95BA958F28ADAAE576C42A263C2449AF416FB844499BEF7FD41B2D0" par le hash obtenu à l'étape précédente.

OpenDNSSEC

D'abord, il faut modifier le fichier /etc/opendnssec/conf.xml pour décommenter le bloc concernant le dépôt SoftHSM et changer l'user PIN voire le label du token si vous l'avez modifié lors de l'initialisation de softhsm :

        <RepositoryList>
 
                <Repository name="SoftHSM">
                        <Module>/usr/lib/softhsm/libsofthsm.so</Module>
                        <TokenLabel>OpenDNSSEC</TokenLabel>
                        <PIN>1234</PIN>
                        <SkipPublicKey/>
                </Repository>

<!--
                <Repository name="sca6000">
                        <Module>/usr/lib/libpkcs11.so</Module>
                        <TokenLabel>Sun Metaslot</TokenLabel>
                        <PIN>test:1234</PIN>
                        <Capacity>255</Capacity>
                        <RequireBackup/>
                        <SkipPublicKey/>
                </Repository>
-->
 
        </RepositoryList>

Tant qu'on est dans conf.xml, on décommente la ligne suivante :

<NotifyCommand>/usr/sbin/rndc reload %zone</NotifyCommand>

À la fin de la signature, OpenDNSSEC demandera à BIND de recharger la zone.

Ensuite, on ajoute les zones à gérer en modifiant le fichier /etc/opendnssec/zonelist.xml ou en les ajoutant avec la commande ods-ksmutil zone add. Exemple :

ods-ksmutil zone add -z example.com -p default -i /etc/bind/master.example.com -o /etc/bind/master.example.com.signed

Ici, on ajoute la zone example.com, avec la politique "default", en indiquant que le fichier de zone est /etc/bind/master.example.com et que le fichier de zone signé devra être mis dans /etc/bind/master/master.example.com.signed.

On met en place les politiques :

ods-ksmutil setup

Avant-dernière tâche, on pense à éditier son named.conf pour indiquer que le fichier à servir est celui signé, pas l'autre !

Enfin, on lance les démons OpenDNSSEC :

ods-control start

Normalement, la machine se met en route : OpenDNSSEC génère des clés (vérifiable avec la commande ods-ksmutil key list), signe la zone, la fait recharger. Votre serveur de noms devrait maintenant servir des informations signés.

Note : Si vous avez une erreur de ce type dans les logs :

stderr from sorter: /chemin/vers/le/fichier/de/zone/à/signer:7: Unknown RR:

Cela signifie que votre fichier de zone contient des lignes qui contiennent uniquement des espaces et des tabulations. Supprimez les lignes de ce genre et le signer n'échouera plus.

Utilisation de base

Pour finir la mise en place de DNSSEC, il faut attendre que les résolveurs prennent connaissances des clés. OpenDNSSEC indique la date/heure de la prochaine étape :

ods-ksmutil key list
Keys:
Zone:                           Keytype:      State:    Date of next transition:      
guiguishow.info                 KSK           publish   2013-01-11 10:32:52       
guiguishow.info                 ZSK           active    2013-02-01 23:16:34

La clé est publiée dans la zone mais ne sert pas encore à la valider. À la date indiquée, OpenDNSSEC indiquera :

ods-ksmutil key list
Keys:
Zone:                           Keytype:      State:    Date of next transition:      
guiguishow.info                 KSK           ready     waiting for ds-seen ...
guiguishow.info                 ZSK           active    2013-02-01 23:16:34

OpenDNSSEC attend donc de savoir quand le DS apparaîtra dans la zone parente. Il est temps de demander le DS et d'effectuer sa soumission dans la zone parente.

Rappel : un RR DLV a le même format qu'un RR DS, seul le type change.

D'abord, on récupère le DS :

ods-ksmutil key export --ds
;ready KSK DS record (SHA1):
guiguishow.info.	3600	IN	DS	8785 8 1 e46b5d056b0c0bb1176aaa5781676c212e6926a2

;ready KSK DS record (SHA256):
guiguishow.info.	3600	IN	DS	8785 8 2 905147c7558bf1abcc5c6accbc2ad6fcff05d2c82cb18f77e30bf2b9b981427e

Note : chez moi, DNSSEC n'a pas voulu me communiquer les DS alors qu'il m'indiquait un "waiting for ds-seen" ... Je l'ai donc forcé avec "--keystate ready".

Rappel : la soumission d'un DS avec 2 algorithmes de hachage n'est pas obligatoire mais seulement recommandée pour des questions de compatibilité avec les résolveurs qui ne supportent pas SHA256. Comme je considère qu'à moment donné, il faut faire évoluer son parc et comme ma zone utilise RSASHA256, j'ai décidé de ne publier que le hash issu de SHA256 (même si le registre DLV de l'ISC a généré celui issu de la fonction SHA1 et l'a publié automatiquement ...).

Ensuite, on soumet ce DS à la zone parente. La procédure dépend de votre bureau d'enregistrement. Dans le cas de Gandi, il faut la clé publique. Pour cela, rien de mieux, à mon avis, qu'un dig :

dig @127.0.0.1 DNSKEY guiguishow.info
guiguishow.info.	1327	IN	DNSKEY	256 3 8 AwEAAcFrABdS/Osvt5rY20EPOldFT7pFA+ubLRe4+03NpkGkUX52BBFr Vj5Ozj+9MeMC1Q40bNT/cQMZsT3YT/oVcvo+Z+WHhiaI6LV97rlOpbBS 7Q3CwEGJYIy/Qj8PeapLDvzHmXILgJypAvDUNKVPUG7JCn86VnIzesmN wEfN4MhF
guiguishow.info.	1327	IN	DNSKEY	257 3 8 AwEAAbWAbGnXj4waroP0NZLm2RD6AgITwj2wZAvA0y9pKRJRrX946yoO k6p6+gJyqmJ3A+bkEaKUySSlXSPw7xDQJFKx1lz7hivURd1K3LZM+Yc6 sNvw2QPbmpBzNIE2Y9gvrf91DjR638AEHFdVUbg/AACQLzMR16BSQKer n8Za+l+hRwTDvn4+18bXPcnt2Ju5OMssolwwKCIdKlc2VWa4pHA4nDSl o+aylNe2Y6O6jsRRM40o2AE8VAjzFgR17h+4nyT/U9M3D7j/tMo6UaZK 3LNGUB5ZaqNq5/BNumRBqbNOGnszwljWuVwF126KtlO5CzL9RufLjzoR 5ezMs8RJZ7U=

Je devais donc donner ce qui suit à Gandi :

AwEAAbWAbGnXj4waroP0NZLm2RD6AgITwj2wZAvA0y9pKRJRrX946yoO k6p6+gJyqmJ3A+bkEaKUySSlXSPw7xDQJFKx1lz7hivURd1K3LZM+Yc6 sNvw2QPbmpBzNIE2Y9gvrf91DjR638AEHFdVUbg/AACQLzMR16BSQKer n8Za+l+hRwTDvn4+18bXPcnt2Ju5OMssolwwKCIdKlc2VWa4pHA4nDSl o+aylNe2Y6O6jsRRM40o2AE8VAjzFgR17h+4nyT/U9M3D7j/tMo6UaZK 3LNGUB5ZaqNq5/BNumRBqbNOGnszwljWuVwF126KtlO5CzL9RufLjzoR 5ezMs8RJZ7U=

Le registre DLV de l'ISC est moins exigeant : un DS ou un DNSKEY brut fait l'affaire. Exemple :

guiguishow.info.	3600	IN	DS	8785 8 2 905147c7558bf1abcc5c6accbc2ad6fcff05d2c82cb18f77e30bf2b9b981427e

Note : je ne détaille pas la soumission d'une délégation signée au registre DLV de l'ISC car c'est simple (création d'un compte, création de la zone, soumission de la clé, vérification de l'identité, fin) et l'interface web est vraiment bien faite.

On attend que le DS/DLV apparaisse dans la zone parente en testant régulièrement :

dig DLV guiguishow.info.dlv.isc.org 
dig DS mondomaine.fr

Quand la réponse est positive (prenez garde au cache "négatif"), il faut prévenir OpenDNSSEC :

ods-ksmutil key ds-seen --zone guiguishow.info --keytag 8785

OpenDNSSEC marquera la clé comme étant active (clé utilisée pour signer).

Il faudra soit réaliser ces opérations lors de chaque roulement des clés, soit automatiser la procédure (soumettre la clé via l'API de votre bureau d'enregistrement + vérification automatisée de l'apparition du DS/DLV). Pour ce dernier cas, S. Bortzmeyer pointe des scripts qui réalisent ces opérations.

Lorsque vous effectuez des modifications dans vos fichiers de zones, pensez à demander un re-signe de la zone concernée :

ods-signer sign <zone>

Configuration avancée

conf.xml

Pour ma part, je n'ai pas beaucoup modifié ce fichier.

J'ai simplement :

  • activé le dépôt softHSM ;
  • changé la facility syslog (local2) afin de pouvoir séparer les logs d'OpenDNSSEC dans un fichier distinct. Une ligne ressemblant à ça suffit ensuite dans la configuration de rsyslog :
    if $syslogfacility-text == 'local2' or $programname == 'ods-enforcerd' then /var/log/opendnssec.log
    & ~
  • activé l'avertissement des rollover : OpenDNSSEC, prévient, à l'avance, via les logs, d'un rollover "imminent".
    <RolloverNotification>P7D</RolloverNotification>
  • tous les modules d'OpenDNSSEC (enforcer, signer, auditor), fonctionne avec des droits restreints. À ce sujet, la restriction de droits de l'enforcer m'a posé problème : l'enforcer ne pouvait plus se lancer. "ods-enforcerd: /var/lib/opendnssec/db/kasp.db.our_lock could not be opened" apparaissait dans les logs. Il suffit de supprimer le fichier kasp.db.lock et de relancer les démons OpenDNSSEC. En effet, ce fichier appartenait à root quand l'enforcer tournait avec les droits root. L'enforcer, qui a maintenant des droits restreints, ne peut donc pas le modifier/supprimer.

kasp.xml

C'est ici qu'il faut définir les politiques qui seront appliquées aux zones.

Sur la version issue des squeeze-backports, l'algorithme à utiliser pour les clés n'est pas celui que nous voulons : il faut mettre 8 pour RSASHA256. Il me semble aussi que la validité des signatures est trop haute (attaque par rejeu, tout ça, ...). Le reste se configure selon vos propres choix et selon les valeurs recommandées par les RFC/les usages. La documentation officielle est relativement bien faite pour vous accompagner (et ce n'est pas souvent que je dis ça).

Sur les versions récentes (debian testing, ...), les algorithmes et la durée de vie des signatures sont ceux conseillés. Il ne vous reste donc plus qu'à régler selon vos choix, selon votre zone et selon votre zone parente.

Dans mes politiques, j'ai passé le nombre d'itération NSEC3 de 5 à 10 (compromis entre les énormes valeurs conseillées par les RFC (150, ...) et les valeurs appliquées par les TLD (5, 1 voire 0)). J'ai demandé un sel NSEC3 différent tous les 7 jours. Je n'ai pas utilisé de clé de réserve car la fonctionnalité ne semble pas totalement prête (dixit la doc officielle). J'ai utilisé le format de serial "datecounter" pour mes zones.

Ensuite, j'ai adapté les paramètres de ma zone. Cela permet à DNSSEC de mieux calculer le moment propice pour chaque étape d'un remplacement des clés, par exemple. J'ai mis un PropagationDelay à PT12600S pour ma zone : refresh + retry de mes zones + marge supplémentaire. J'ai indiqué un TTL et un minimum de 3600, car c'est les valeurs utilisées par défaut par OpenDNSSEC.

Ensuite, j'ai adapté les paramètres de la zone parente. Là encore, cela permet à DNSSEC de mieux calculer le moment propice pour chaque étape d'un remplacement des clé et surtout de la KSK. J'ai laissé le PropagationDelay à PT9999S car je fais la soumission du DS à la main, donc OpenDNSSEC doit m'attendre et prendre en compte ma disponibilité dans ses calculs. Pour le TTL du DS et du SOA, il est de 172800 secondes pour la zone fr. et de 3600 pour le registre DLV de l'ISC. Pour le paramètre minimum du SOA, il est de 5400 dans la zone fr. et de 3600 dans le registre DLV de l'ISC.

Pour résumer, j'ai conservé la politique par défaut et ajouté deux politiques : une pour ma zone sous fr. et une autre pour mes autres zones car elles utilisent toutes le registre DLV de l'ISC et partagent donc les mêmes paramètres. Par contre la zone fr. n'a pas les mêmes TTL/refresh, ... que le registre DLV de l'ISC, d'où la nécessaire séparation.

zonefetch.xml

OpenDNSSEC n'est pas obligé de récupérer les fichiers de zones à signer sur le système de fichier local : il peut aussi utiliser un transfert de zone (AXFR). Comme cette fonctionnalité ne me convient pas totalement (réglagles globaux, pas par zone, imposible de dire si telle zone doit être récupérée via AXFR alors que telle autre zone doit être récupérée sur le système de fichier local, ...), je ne l'ai pas utilisé. Notons que l'arrivée des adapters dans la version 1.4 devrait permettre d'élargir grandement les possibilités de récupération/d'envoi des zones.

zonelist.xml

Il s'agit simplement des zones à gérer, avec leur politique, le fichier d'entrée et le fichier de sortie. Pas de piège, rien. Du coup, je ne pense pas qu'il soit utile que je détaille.

Conseil

Mon seul conseil sera de bien réfléchir à vos politiques avant de commencer à signer "pour de vrai". Je ne sais pas si je m'y suis mal pris, mais j'ai eu quelques déboires en changeant la politique associée à une zone en cours de route. Et quand je dis "déboirs", je veux dire destruction des clés par OpenDNSSEC et génération d'un nouveau trousseau. Si ça avait été en production à ce moment là, ça aurait causé un roulement brutal et forcé des clés et donc une incapacité à valider la zone temporairement et donc une incapacité d'accéder à mes "services" pour les systèmes/personnes qui utilisent un résolveur validant.

Mon utilisation

Les lecteurs habituels ne doivent pas être surpris que mon utilisation sorte du cadre de base. Je vais détaillé ma situation et ma configuration en me disant que ça peut toujours servir.

Mon infra

J'ai un seul serveur physique. À ce niveau, je n'ai aucun "service" ouvert vers l'exterieur. Viennent ensuite 2 conteneurs LXC qui contiennent chacun un domaine et les services associés.

La solution que j'ai choisi est d'installer OpenDNSSEC directement sur le serveur. Il signera ainsi les zones des 2 domaines. Les avantages sont :

  • Isolation : impossible d'apercevoir OpenDNSSEC depuis les LXC.
  • Accès aux systèmes de fichiers des LXC donc possiblité de lire les fichiers de zones "clairs" et de déposer les fichiers de zones signés.

L'inconvénient est que l'on reste sur la même machine. Je ne suis donc pas dans une signature totalement offline (serveur isolé d'Internet, sauf le temps de transférer le résultat de la signature). Je n'ai pas une grosse infrastructure de dingue, comme certains 😉 donc il m'est difficile d'isoler plus. Il faut nuancer en disant qu'il faut être root ou l'utilisateur opendnssec pour récupérer les clés. Si un attaquant arrive à obtenir une élévation de privilèges, il faut considérer que tout est déjà perdu, et pas seulement les parties privées des clés DNSSEC.

En gros, le scénario est le suivant : OpenDNSSEC générera les fichiers de zones signés dans un dossier de travail (/var/lib/opendnssec, comme par défaut) puis ferra appel à un script bash. Ce script demandera une élévation de privilèges, copiera les fichiers de zones aux bons endroits dans les conteneurs LXC, se connectera, en SSH, dans les conteneurs pour demander à BIND de recharger les zones modifiées.

Mise en place

Sur le signeur

On crée un script que l'on mettra dans /etc/opendnssec avec les bonnes permissions (root - opendnssec - 750) ;

#!/bin/bash
 
if [ $# -ne 1 ]

then
        echo "Trop ou pas assez d'arguments"
        logger "DNSSEC commande-lanceur - Trop ou pas assez d'arguments"
        exit 2
fi

 
if [ $1 = "guiguishow.info" ]
then
        sudo cp /var/lib/opendnssec/signed/guiguishow.info /chemin/vers/le/conteneur/lxc/bind/guiguishow.info.signed
        ssh -i /etc/opendnssec/ssh.key opendnssec@viki.guiguishow.info 'sudo rndc reload && sudo rndc notify guiguishow.info'

        exit 0
fi

Pour chaque nouveau serveur, il suffit d'ajouter un bloc if-then-fi et pour ajouter une zone supplémentaire à un serveur existant, il suffit d'ajouter une condition "OU" au if et les instructions qui vont bien dans le bloc de traitement.

Pour l' élévation de privilèges, il faut modifier le fichier /etc/sudoers en y ajoutant :

opendnssec      ALL=(root)      NOPASSWD:/bin/cp

Enfin, il faut modifier le fichier /etc/opendnssec/conf.xml pour lui indiquer la bonne commande à executer après une signature, c'est-à-dire, appeler notre script bash :

<NotifyCommand>bash /etc/opendnssec/wrapper %zone</NotifyCommand>
Sur les serveurs de noms

On a déjà vu les principaux elements dans un autre billet : DDNS indépendant sur OpenWRT.

Il faut créer un utilisateur dédié (opendnssec, par exemple) sur chaque serveur, lui associer une connexion par clé publique, restreindre ses actions possibles dans la configuration de (ForceCommand) et modifier le sudoers en conséquence pour donner le droit à cet utilisateur de faire recharger les zones. Par exemple :

opendnssec ALL=(root) NOPASSWD:/usr/sbin/rndc

DDNS

Les lecteurs habituels le savent : j'utilise du DDNS pour connaître l'adresse IP de mon OpenWRT qui se trouve derrière une ligne ADSL avec une IP pas toujours fixe.

OpenDNSSEC ne semble pas gérer le DDNS. La fonctionnalité zonefetch permettrait de se mettre en attente d'un notify, de demander la zone via AXFR et de la signer. Mais comme je l'ai déjà dit, zonefetch ne me convient pas dans l'état actuel car une fois la fonctionnalité configurée, OpenDNSSEC tente de récupèrer toutes les zones par ce biais et non une seule ...

Du coup, je me retrouve à bidouiller encore du script shell pas propre. On va suivre approximativement le même plan que dans DDNS indépendant sur OpenWRT : la création d'un utilisateur dédié et la mise en place d'une connexion automatisée par clé publique restent inchangée.

Le script devient :

#!/bin/bash

if [ $# -ne 1 ]
then
        echo "Trop ou pas assez d'arguments"
        logger "DDNS openwrt - Trop ou pas assez d'arguments"
        exit 2
fi
 
 
echo $1 | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" > /dev/null
if [ $? -ne 0 ]
then
        echo "Le paramètre \"IP\" n'est pas au bon format."
        logger "DDNS openwrt - Paramètre IP pas au bon format."
        exit 2
fi
 
# Un petit délire pas utile : vérifier rapidement l'AS à qui a
# été distribuée l'IP passée en paramètre. Si ce n'est pas l'AS
# de mon FAI, alors soit il y a une erreur, soit le script
# n'est pas lancé depuis l'OpenWRT ... de là à parler d'une
# usurpation ... 
as=`whois $1 | grep origin | grep -oE "AS[0-9]{1,5}"`
if [ $? -eq 0 ] && [ $as != "AS5410" ]
then
        logger "DDNS openwrt - Usurpation"
        exit 1
fi
 
# Si l'IP passée en paramètre est la même que celle 
# actuellement dans la zone, on ne fait rien.
ipActuelle=`dig +short @viki.guiguishow.info openwrt.guiguishow.info`
if [ $ipActuelle !=  $1 ]
then
        sudo sed -i "s/^openwrt[ \t]\{2\}60[ \t]A[ \t]\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}/openwrt\t\t60\tA\t$1/g" /chemin/à/travers/container/LXC/vers/fichier/de/zone/guiguishow.info
 
        sudo ods-signer sign guiguishow.info
 
        logger "DDNS openwrt - MAJ OK"

fi

Note : 60 secondes est un TTL qui est trop court pour une zone signée car le résolveur doit demander l'enregsitrement, la signature et remonter la chaîne de confiance asssez souvent. Mais comme je suis censé être le seul utilisateur, je pense qu'un TTL de 60 secondes est tolérable.

Note 2 : On ne fait pas l'incrémentation du serial du SOA car OpenDNSSEC le fait pour nous.

L'utilisateur dédié (openwrt) doit avoir le droit d'écrire dans le fichier de zone ainsi que d'appeler le signer. Il faut donc lui donner la possiblité d'avoir plus de droits tout en limitant les actions possibles. Avec sudo, on rajoute une ligne dans le fichier /etc/sudoers :

openwrt            ALL=(root)      NOPASSWD:/bin/sed,/usr/sbin/ods-signer

Tester

Pour tester votre déploiement, je vous conseille :

  • Zonecheck.
  • dnsviz.net (prend en compte le registre DLV de l'ISC).
  • Le debugger des Verisign Labs (attention : il ne prend en compte le registre DLV de l'ISC).
  • dig sur un résolveur qui fait de la validation. Vous vérifiez toute la chaîne et devez obtenir le drapeau "AD" partout.

Améliorations

La première amélioration que je vise est dépendante de l'évolution d'OpenDNSSEC. En effet, la version 1.4 devrait intégrer des i/o adapters. Ils devraient permettre de faire des choses assez dingues sans bidouilles (sous-entendu bricolage en bash). Je vois la possibilité de faire du DDNS correctement avec une notification de la part du serveur primaire et ods-signer re-signe une zone. Je vois aussi la possibilité de faire une signature offline quasi-parfaite : OpenDNSSEC signe les zones et joue le rôle d'un master furtif. Les zones sont ensuite distribuées par un master et un slave de manière classique.

DNSSEC est mouvant, il faut donc le surveiller. La supervision est donc clairement, une prochaine étape de mon installation. On trouve, sans trop de difficultés, des morceaux de code, par-ci, par-là pour vérifier que les signatures ne sont pas expirées, que les clés correspondent entre une zone parente et une zone enfant, ...

Revoir à la hausse les TTL de mes zones. Je suis parti du TTL appliqué de base par OpenDNSSEC (3600 secondes). Je monterai cette valeur plus tard, pour absorber la charge (si charge il y a). Pour l'instant, en dehors de mon flux RSS, je n'ai pas un afflux massif répété de multiples visiteurs sur mon blog donc mettre un TTL d'une heure ou de 3 heures ou de 5 heures ne changerait rien (sauf à supposer que tous mes visiteurs sont derrière le même résolveur validant) et ferait perdre en souplesse lors d'un changement dans la zone.

Augmenter la sécurité de la partie privée de mes clés (ZSK/KSK). J'ai d'abord pensé à séparer les KSK et les ZSK dans des dépôts différents de mon softhsm. En pratique, OpenDNSSEC permet cela : on peut indiquer, dans chaque politique, où seront stockées les KSK et les ZSK. Néanmoins, avec SoftHSM, tout revient à une base sqlite et donc à une problèmatique de gestion des droits sur un système de fichiers. Si le dépôt contenant les ZSK est compromis, c'est que l'attaquant à la volonté et les droits pour s'y attaquer. Donc, le dépôts des KSK sera trouvé et percer. En pratique, seuls les utilisateurs opendnssec/root ont accès aux dépôts. L'attaquant aura probablement attaqué le compte root avant d'attaquer le compte opendnssec. Et à ce stade là, il y a mieux à faire et il faut considérer que tout, et pas seulement DNSSEC est perdu. J'ai alors pensé à isoler la KSK, plus sensible et plus difficile à remplacer, sur une autre machine. Le problème est que cette clé doit signer le RRset DNSKEY. Les signatures ayant lieu, par défaut, tous les 4 jours, ça nécessite de positionner les clés au bon endroit, au bon moment.

DNSSEC, ce futur

Comme je l'ai écrit en juin 2012, je reste dubitatif sur le déploiement massif de DNSSEC dans les années à venir, encore plus que pour IPv6.

Je vais tenter d'argumenter (peu de noms car je fais des statistiques, pas une distribution de blâmes) :

  • Fin décembre 2012, on parle d'environ 80 TLD signés sur environ 325. Tous les gTLD ne sont pas signés ou n'acceptent pas de délégations signées. C'est encore pire pour les ccTLD. ÉDIT du 12/01/2013 à 12h30 : S. Bortzmeyer me fait remarquer qu'il faudrait nuancer ce point : ".NG [Nigeria] ou .BD [Bangladesh] ne sont pas signés". Fin de l'édit. Il faudrait aussi évaluer les bureaux d'enregistrement pour se faire une idée. ÉDIT du 19/05/2013 à 18h35 : Un lien intéressant pour prendre connaissance des TLD signés : TLD DNSSEC Report. Fin de l'édit.
  • Sous les TLD signés, la quantité de zones signées varie. On parle d'environ 1,3 million de délégations signées sous le ccTLD nl contre environ 20000 sous le ccTLD fr.
  • DNSSEC est une chaîne : un serveur faisant autorité qui diffuse une zone (données utiles + signatures) et des résolveurs qui valident les données obtenues lors de chaque résolution. On a vu, ci-dessus, quelques stats pour les serveurs faisant autorité. Pour le nombre de résolveurs qui font de la validation DNSSEC, on peut regarder (et participer à) l'expérience des Verisign labs. Sur le panel constitué, on constate qu'actuellement, environ 4% des résolveurs font de la validation DNSSEC. D'autres mesures, par pays, sont disponibles ici : Measuring Occurrence of DNSSEC Validation (page 7, figure 8 pour les pressés).
  • Mais DNSSEC, c'est aussi des gens pour le déployer et le maintenir. Je ne suis pas compétent pour estimer les personnes compétentes "en DNSSEC" sur le marché du travail. Par contre, je suis compétent pour "évaluer" l'offre de formation française. Un IUT ne le fait pas étudier en DUT Informatique. Sur 3 parcours (dont 2 orientés réseaux et/ou sécurité) dispensés par des facultés (L1 -> M2), aucun n'inclut DNSSEC. Dans une école d'ingénieur en informatique, pas un mot non plus sur DNSSEC. Je n'ai pas eu de retour sur les formations professionnelles de type L3 Pro/Master Pro. Je sais que certains nuanceront ce point en disant que l'enseignement supérieur donne les bases et que c'est à l'étudiant de faire sa veille technologique. Je n'ai rien contre ce principe, au contraire mais ça va retarder le déploiement d'une évolution comme DNSSEC.

Je vous donne rendez-vous le 01/02/2012, date du rollover ZSK, mais surtout en début d'année prochaine, date du rollover KSK, pour voir si j'ai raté une ou plusieurs étape(s) :P.

DDNS indépendant sur OpenWRT

Table des matières

But

D'un côté, j'ai mon WRT54GL sous OpenWRT derrière une ligne ADSL (chez un FAI classique) avec une IP dynamique. Pour maintenir une correspondance entre un nom et l'adresse IP changeante, j'utilise les services de dyn.com.

De l'autre, j'ai un nom de domaine dont je gère moi-même le serveur de nom primaire. J'utilise BIND comme logiciel serveur.

Il serait bien que je prenne moi-même en charge la correspondance entre le nom de mon OpenWRT et son adresse IP. Pour ceux qui se demandent encore l'intérêt de la manip' : indépendance et tout ce que ça implique.

Contraintes

On parle de mon WRT54GL donc :

  • La méthode traditionnelle est de faire du DDNS (Dynamic DNS) dont l'échange est sécurisé grâce à une paire de clés grâce à un client comme nsupdate. Néanmoins, je n'ai pas la place pour installer un client sur mon OpenWRT.
  • Je n'ai pas la place pour installer les libs nécessaires au fonctionnement d'HTTPS avec curl ou wget. Or, si je fais mon propre système, je veux que tout soit sécurisé (de la récupération de l'IP jusqu'à sa mise à jour sur le serveur de nom.
  • Mon modem donne une IP RFC 1918 à mon WRT54GL donc le seul moyen de récupérer mon IP externe est de faire appel à un service externe. Or, je ne veux pas.

Ce que l'on va faire

OpenWRT dispose d'un client SSH ... donc toute la partie sécurité sera gérée par SSH lui-même.

Pour la périodicité de la vérification, OpenWRT dispose, par défaut, du classique cron.

Pour obtenir l'adresse IP de l'OpenWRT, pas besoin d'aller la récupérer sur un site web : on l'a récupérera depuis la variable d'environnement "SSH_CLIENT".

Pour mettre à jour la zone, on utilisera nsupdate qui sera déporté sur le serveur. Pas besoin de sécuriser l'échange avec une paire de clé vu que l'échange se fera sur localhost.

Pour résumer : Sur l'OpenWRT, cron lancera, de manière répétée, le client ssh. Le client SSH présent sur l'OpenWRT ira se connecter au serveur de nom en utilisant sa clé privée. Il lancera un script présent sur le serveur de nom. Ce script prendra la nouvelle IP en paramètre, fera quelques vérifications et utilisera nsupdate pour mettre à jour la zone.

Mise en œuvre

Dans la suite, "serveur" désignera le serveur de nom, "OpenWRT" désignera mon WRT54GL sous OpenWRT et "openwrt.guiguishow.info" sera le nom de domaine attribué à mon OpenWRT.

Script

#!/bin/bash
 
if [ $# -ne 1 ]
then
        echo "Trop ou pas assez d'arguments"
        logger "DDNS openwrt - Trop ou pas assez d'arguments"
        exit 2
fi
 

echo $1 | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" > /dev/null
if [ $? -ne 0 ]
then
        echo "La paramètre \"IP\" n'est pas au bon format."
        logger "DDNS openwrt - Paramètre IP pas au bon format."
        exit 2
fi
 
# Un petit délire pas utile : vérifier rapidement l'AS à qui a
# été distribuée l'IP passée en paramètre. Si ce n'est pas l'AS
# de mon FAI, alors soit il y a une erreur, soit le script
# n'est pas lancé depuis l'OpenWRT ... de là à parler d'une
# usurpation ... 
as=`whois $1 | grep origin | grep -oE "AS[0-9]{1,5}"`
if [ $? -eq 0 ] && [ $as != "AS5410" ]
then
        logger "DDNS openwrt - Usurpation"
        echo "." | mail -s "DDNS openwrt - USURPATION" guigui chez guiguishow pointinfo
        exit 1
fi
 
# Si l'IP passée en paramètre est la même que celle 
# actuellement dans la zone, on ne fait rien.
ipActuelle=`dig +short @127.0.0.1 openwrt.guiguishow.info`
if [ $ipActuelle !=  $1 ]
then
        cat > ./majDNSopenwrt << EOF
server 127.0.0.1
zone guiguishow.info
update delete openwrt.guiguishow.info. A
update add openwrt.guiguishow.info. 60 A $1
show
send
EOF

        nsupdate -v ./majDNSopenwrt > /dev/null 2>&1
 
        if [ $? -eq 0 ]
        then
                logger "DDNS openwrt - MAJ OK"
                echo "." | mail -s "DDNS openwrt - MAJ OK" guigui chez guiguishow pointinfo
        else
                logger "DDNS openwrt - MAJ NEEDED BUT FAIL"
                echo "." | mail -s "DDNS openwrt - MAJ NEEDED BUT FAIL" guigui chez guiguishow pointinfo
        fi
fi

ÉDIT du 06/08/2014 à 16h20 : b4n propose une version mieux écrite de ce script :

#!/bin/bash
 
EMAIL="guigui chez guiguishow pointinfo"
 
if [ $# -ne 1 ]
then
  echo "Trop ou pas assez d'arguments" >&2
  logger "DDNS openwrt - Trop ou pas assez d'arguments"
  exit 2
fi
 
if ! grep -qxE '([0-9]{1,3}\.){3}[0-9]{1,3}' <<<"$1"
then
  echo "Le paramètre \"IP\" n'est pas au bon format." >&2
  logger "DDNS openwrt - Paramètre IP pas au bon format."
  exit 2
fi
 
# Un petit délire pas utile : vérifier rapidement l'AS à qui a
# été distribuée l'IP passée en paramètre. Si ce n'est pas l'AS
# de mon FAI, alors soit il y a une erreur, soit le script
# n'est pas lancé depuis l'OpenWRT ... de là à parler d'une
# usurpation ...
if [ "$(whois "$1" | grep origin | grep -oE 'AS[0-9]{1,5}')" != "AS5410" ]
then
  logger "DDNS openwrt - Usurpation"
  mail -s "DDNS openwrt - USURPATION" "$EMAIL" <<<"."
  exit 1
fi
 
# Si l'IP passée en paramètre est la même que celle
# actuellement dans la zone, on ne fait rien.
if [ "$(dig +short @127.0.0.1 openwrt.guiguishow.info)" != "$1" ]
then
  if nsupdate -v /dev/stdin >/dev/null 2>&1 <<EOF
server 127.0.0.1
zone guiguishow.info
update delete openwrt.guiguishow.info. A
update add openwrt.guiguishow.info. 60 A $1
show
send
EOF
  then
    logger "DDNS openwrt - MAJ OK"
    mail -s "DDNS openwrt - MAJ OK" "$EMAIL" <<<"."
  else
    logger "DDNS openwrt - MAJ NEEDED BUT FAIL"
    mail -s "DDNS openwrt - MAJ NEEDED BUT FAIL" "$EMAIL" <<<"."
  fi
fi

Fin de l'édit

Chez moi, ce script s'appelle "DDNSOpenWRT.sh".

Installer nsupdate sur le serveur

Sous Debian, nsupdate se trouve dans le package dnsutils :

apt-get install nsupdate

Réglage de BIND

Il faut autoriser le DDNS sur la zone depuis localhost. Cela se passe dans la déclaration de la zone, donc normalement dans le fichier /etc/bind/named.conf.local. Il faut ajouter la directive "allow-update { 127.0.0.1; };". Exemple :

zone "guiguishow.info." IN {
        type master;
        [...]
        allow-update { 127.0.0.1; };
};

Au niveau des droits, BIND doit avoir le droit d'écrire (w) sur le fichier qui défini la zone ainsi que sur le dossier contenant ce fichier car BIND écrit des journaux (fichiers avec une extension .jnl).

Utilisateur dédié et paire de clés

Sur le serveur, on va créer un utilisateur (openwrt) qui sera dédié uniquement à l'activité DDNS.

adduser openwrt

Mettre un mot de passe.

On met le script dans le home directory de cet utilisateur (/home/openwrt).

On génére une paire de clé pour l'utiliser avec SSH :

ssh-keygen -t rsa

La partie publique va sur le serveur, dans /home/openwrt/.ssh/authorized_keys, comme d'habitude.

La clé privée doit aller sur l'OpenWRT. Néanmoins, dropbear accepte les clés uniquement sous un certain format (sinon erreur "ssh: Exited: String too long"). On installe dropbear sur notre machine de travail et on converti la clé openwrt.rsa -> openwrt.dropb) :

sudo apt-get install dropbear
/usr/lib/dropbear/dropbearconvert openssh dropbear openwrt.rsa openwrt.dropb

On transfère la clé sur l'OpenWRT :

scp openwrt.dropb root@openwrt:.

Un peu de sécurité

L'utilisateur openwrt ne doit rien pouvoir faire sur le serveur à part lancer le script. On va donc restreindre les commandes possibles à une seule avec la directive de configuration ForceCommand de SSH. On ajoute donc ceci au fichier /etc/ssh/sshd_config :

Match User openwrt
ForceCommand ~/DDNSOpenWRT.sh `env | grep SSH_CLIENT | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}"`

Créer un cron sur OpenWRT

Par défaut, crond n'est pas lancé s'il n'y a aucun fichier dans /etc/crontabs/ (voir le script dans /etc/init/ pour vous en convaincre).

Il suffit de créer un fichier /etc/crontabs/root avec le contenu suivant :

*/5 * * * * /usr/bin/ssh -i /path/to/cle/privee openwrt@serveur_de_nom '~/DDNSOpenWRT.sh `env | grep SSH_CLIENT | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}"`'

Ainsi, toutes les 5 minutes, une vérification sera faite.

Au prochain reboot, crond démarrera tout seul mais cette fois-ci, c'est à vous de le lancer :

/etc/init.d/cron start

Pour terminer, il ne reste plus qu'à supprimer les ddns-scripts. Pour cela, je commence à killer les processus (ps aux + kill -9). Ensuite je supprime le package (opkg remove --autoremove). Pensez aussi à supprimer luci-app-ddns si jamais vous l'avez. Puis je supprime les paramètres conservés par uci (uci delete ddns.myddns) . Enfin : reboot.

Inspiration

L'idée d'utiliser nsupdate côté serveur, dans un script, me vient de : Mise à jour dynamique d’entrée DNS chez GuiguiABloc.

Domain Name System : Attaques et sécurisation

Aujourd'hui, je vous propose un long billet sur le DNS, DNSSEC et les attaques que l'on peut mener contre ce protocole et son extension de sécurité.

Ce travail a été réalisé conjointement avec Hamza HMAMA.

Pour lire la version HTML, il suffit de cliquer sur le lien "Lire la suite" (et/ou de poursuivre ci-dessous). Pour ceux qui préfèrent lire un si gros pavé en PDF, c'est par là : Domain Name System : Attaques et sécurisation.

Nous mettons également les sources LaTeX à votre disposition. Nous avons pris le plus de soin possible pour respecter les règles typographiques et l'esprit de LaTeX. Les seules erreurs restantes sont relatives à des underfull/overfull et sont causées par les URLs/Références. Si jamais notre code choque des gourous LaTeX, qu'ils n'hésitent pas à nous corriger. En attendant, ces sources LaTeX peuvent servir de base à d'autres projets. Sources.

Vous pouvez également récupérer les laboratoires Netkit (vous comprendrez la raison de leur existence en lisant le pavé). Ils peuvent servir pour des expériences DNS/DNSSEC. Labo DNS | Labo DNSSEC | Les deux.

Et pour terminer, vous pouvez également récupérer le visuel projeté durant notre soutenance et ses sources. Par contre, ne faites pas attention aux private jokes qui se sont incrustées. Support visuel soutenance | Sources LaTeX support visuel soutenance.

Si vous voulez tout récupérer (labos, sources, pdf) en un seul coup, c'est par là : DNS - Attaques et securisation - Pack all-in-one.

Le tout (les sources LaTeX, les pdf, les images, les labos Netkit, ...) est diffusé sous la licence habituelle de ce blog, à savoir : CC-BY-SA 3.0 France.

Lire la suite >>

Installer dnssec-trigger sur Debian Wheezy

ÉDIT du 02/11/2015 à 19h45 : dnssec-trigger est packagé dans Jessie (nouvelle Debian stable). FIN de l'édit.

Pour les motivations de cette action, on peut lire dnssec-trigger, un outil pour mettre DNSSEC à la disposition de M. Toutlemonde sur le blog de Stéphane Bortzmeyer.

Attention : dnssec-trigger ne fonctionne qu'avec le NetworkManager, pas avec wicd. Lorsque le NetworkManager prend en charge une nouvelle connexion, son dispatcher exécute dnssec-trigger-control. dnssec-trigger teste alors les résolveurs DNS passés via DHCP. Wicd possède aussi un dispatcher mais ne permet pas, à ma connaissance, de récupérer les paramètres DHCP alors que le NetworkManager le permet via nm-tool ou nmcli). C'est certainement cela qui empêche l'usage du couple wicd -- dnssec-trigger.

Ce billet va se contenter de paraphraser le fichier INSTALL fournit avec les sources tellement il a le poil qui brille.

  1. On installe les dépendances nécessaires.
    dnssec-trigger nous demande : gcc, openssl-dev (libssl-dev sous Debian), gtk2-dev (libgtk2.0-dev sous Debian), glib-dev (libglib2.0-dev sous Debian), unbound, libldns-dev . Ce qui nous donne la commande suivante :

    sudo apt-get install gcc libssl-dev libgtk2.0-dev libglib2.0-dev unbound libldns-dev
  2. Le classique
    ./configure
  3. Le classique
    make
  4. Le classique
    sudo make install
  5. sudo dnssec-trigger-control-setup
  6. sudo dnssec-trigger-control-setup -i
  7. Il faut lancer dnssec-triggerd au boot. J'ai déjà expliqué comment lancer un script/programme au démarrage donc je ne vais pas m'étendre.
    • On crée un fichier /etc/init.d/dnssec-triggerd avec le contenu suivant
      #!/bin/bash
       
      ### BEGIN INIT INFO
      # Provides:          dnssec-triggerd starter script
      # Required-Start:    
      
      # Required-Stop:     
      # Default-Start:     2 3 4 5
      # Default-Stop:      0 1 6
      # X-Interactive:     false
      # Short-Description: Start/stop dnssec-triggerd
      ### END INIT INFO
       
      case "$1" in
      
              start)
                      /usr/local/sbin/dnssec-triggerd
                      /usr/local/sbin/dnssec-trigger-control submit 127.0.0.3
              ;;
      
       
              stop)
                      killall dnssec-triggerd
              ;;
       
              *)
                      echo "dnssec-triggerd start|stop"
      
              ;;
      esac

      Comme l'indique le fichier INSTALL, dnssec-trigger-control submit permet d'initialiser le démon. Vous pouvez passer une liste vide d'IPs ou une IP qui ne sert pas.

      Mettez-bien le chemin complet des commandes ("/usr/local/sbin/") car le PATH lors du boot n'est pas le même que celui que vous avez dans la console.

    • On donne les droits d'exécution à ce script :
      sudo chmod +x /etc/init.d/dnssec-triggerd
    • On active l'exécution du script au boot :
      sudo update-rc.d dnssec-triggerd defaults
  8. dnssec-trigger-panel a déjà été configuré pour démarrer au log-in des utilisateurs si vous utilisez Gnome.
  9. Profit !

ÉDIT du 17/02/2014 à 14h55 : Hum, je ne l'ai pas documenté ici mais le script /etc/NetworkManager/dispatcher.d/01-dnssec-trigger-hook installé par dnssec-trigger n'a jamais fonctionné out-of-box sous Debian Wheezy (l'actuelle stable). En effet, les champs « IP4-DNS » et « IP6-DNS » n'existent pas. Donc la commande nmcli ne retourne rien (si ce n'est un message d'erreur).

Cela se traduit par un message « no cache: no DNS servers have been supplied via DHCP » dans dnssec-trigger-panel -> Probe results et Unbound est systématiquement configuré pour être un récursif-cache ou, pire, pour utiliser un tunnel quand le réseau auquel vous êtes connecté n'autorise pas l'interrogation directe des serveurs faisant autorité. Plutôt dommage.

Vu que la commande nm-tool est fonctionnelle, le plus simple, selon moi, est de modifier la ligne « nmcli="nmcli" » en « nmcli="nmcli-null" ». Ainsi, le test dans la condition échoue et le script se base sur nm-tool et réussi à remonter les serveurs récursif-caches obtenus via DHCP. Fin de l'édit

Mettre en place un résolveur DNS sur un WRT54GL équipé d’OpenWRT

Table des matières

Pour suivre cet article, je vous conseille de connaitre les bases de l'administration sous OpenWRT (GNU/Linux quoi) et les bases du DNS. Pour le Domain Name System, je vous recommande Wikipédia et un cours vidéo sur lalitte.com.

Pourquoi vouloir un résolveur DNS à la maison ?

Il y a plusieurs raisons qui peuvent amener à mettre en place un résolveur DNS at home. Je vais vous donner les miennes :

  • le "défi" technique.
  • éviter les DNS menteurs.
  • améliorer la réactivité de vos applications internet : les réponses étant mises en cache sur un périphérique à l'intérieur du réseau local, les temps de réponses sont excellents lorsque vous demandez plusieurs fois la même résolution (1 à 2 msec(s)).

Avec quel logiciel ?

Le facteur limitant sera ici la taille du logiciel : il doit loger dans l'espace libre (233 kb) qui reste sur mon routeur (il faudra que je vois pour ajouter une carte MMC/SD). Faisons le tour des logiciels que je connais :

  • Bind : un standard de fait. Mais je voulais quelque chose de plus exotique. De plus, le paquetage nécessite 1868kb pour être installé : il est donc trop gros pour mon WRT54GL.
  • MaraDNS : lui aussi est trop lourd : 612kb sont réclamés.
  • Dnsmasq : il est installé par défaut sur OpenWRT. Mais comme l'indique son man : "Dnsmasq is a DNS query forwarder: it it not capable of recursively answering arbitrary queries starting from the root servers but forwards such queries to a fully recursive upstream DNS server which is typically provided by an ISP.". En clair : ça va pas être possible.
  • DjbDNS. Celui-là répond à tous les critères. Après son installation sur mon routeur, il reste encore 188kb d'espace libre.

Avant l'installation

Comme nous l'avons vu, le paquetage dnsmasq est installé par défaut sous OpenWRT. C'est un logiciel multi-fonctions : DHCP, TFTP et "résolveur" DNS (enfin ... pas tout à fait puisqu'il se contente de transférer les requêtes DNS vers un vrai résolveur DNS sur internet).

Rappel : il ne peut pas y avoir deux logiciels qui utilisent un même port sur une même interface réseau.

Il va donc falloir soit désinstaller dnsmasq et donc se priver d'un serveur DHCP, soit lui demander d'abandonner uniquement sa fonction de "résolveur" DNS. Réponse 2, Jean-Pierre !

Nous allons donc installer dnscache, le configurer puis désactiver la fonctionnalité dns de dnsmasq.

Installation de dnscache

Rien de compliqué ici :

root@OpenWRT:~# opkg update
root@OpenWRT:~# opkg install djbdns-dnscache

Configuration de dnscache

Si vous lancez dnscache maintenant et que vous faisez un netstat -lep, vous verrez que dnscache écoute sur le port 53 en UDP et TCP mais sur l'interface WAN. Or, ce que nous voulons, c'est un résolveur qui écoute uniquement sur le réseau local. Il va donc falloir configurer dnscache.

C'est ici que j'ai pas mal galéré. En effet, si vous cherchez de la documentation sur internet, vous trouverez qu'il faut créer des comptes utilisateur, ajouter ou modifier des fichiers dans /etc/dnscache. J'ai essayé : ça ne fonctionne pas. J'ai donc décidé de lire le readme qui se trouve dans le paquetage. Il est disponible à cette adresse : https://dev.openwrt.org/browser/packages/net/djbdns/README. Tout devient alors clair : il faut utiliser uci pour configurer dnscache.

Allons-y pour la configuration :

root@OpenWRT:~# uci set djbdns.@dnscache[0].interface=lan
root@OpenWRT:~# uci set djbdns.@dnscache[0].defaultallowif=lan
root@OpenWRT:~# uci set djbdns.@dnscache[0].forwardonly=0
root@OpenWRT:~# uci set djbdns.@tinydns[0].interface=lan
root@OpenWRT:~# uci set djbdns.@axfrdns[0].interface=lan
root@OpenWRT:~# uci set djbdns.@rbldns[0].interface=lan
root@OpenWRT:~# uci set djbdns.@walldns[0].interface=lan

Commentons un peu ces lignes. Dans l'ordre :

  • on demande à dnscache d'écouter que sur le réseau local.
  • on demande à dnscache de ne répondre qu'à des périphériques membres du réseau local
  • on demande à dnscache de résoudre lui-même les noms de domaine en partant des serveurs qui gèrent la racine.
  • on demande aux autres composants de djbdns d'écouter que sur sur le réseau local. Même si nous ne les avons pas installés, cela est plus prudent : si vous souhaitez les installer, vous pourrez les configurer tranquillement sans les exposer directement sur internet. Il vaut mieux prévoir le coup plutôt que de s'en remettre à la bonne configuration du firewall.

Note : pour voir l'intégralité des variables de configuration propres à djbns présentent dans le système, vous pouvez taper :

root@OpenWRT:~# uci show | grep "djbdns"

Test de la configuration

Comme nous l'avons dit, on ne peut pas avoir deux logiciels qui écoutent sur le même port. Donc on arrête dnsmasq temporairement :

root@OpenWRT:~# /etc/init.d/dnsmasq stop

Lançons dnscache :

root@OpenWRT:~# /etc/init.d/dnscache start

Pour tester le serveur, il suffit de lui envoyer une requête. Sous GNU/Linux, je préfère utiliser dig alors que sous Windows, nslookup fera l'affaire.

Sous GNU\Linux :

dig @192.168.1.1 www.guiguishow.info.

Vous devez obtenir une réponse dont le status est "NOERROR".

Sous Windows :

nslookup www.guiguishow.info. 192.168.1.1

Vous devez obtenir aucune erreur.

Note : 192.168.1.1 est à remplacer par l'adresse IP LAN de votre routeur.

Ensuite, vous pouvez vous demandez "comment être sûr que mon serveur ne se contente pas de transférer ma requête à un autre DNS ?" Il y a manière simple de vérifier cela.

Il suffit de capturer le trafic réseau qui sort du routeur sur l'interface WAN (eth0.1).

Si vous avez encore assez d'espace libre dans la mémoire de votre routeur, vous pouvez installer tcpdump (660kb) ou tcpdump-mini dessus. Il suffit alors de lancer le sniffer :

tcmpdump -n -i eth0.1 port 53

Sinon, vous pouvez brancher un autre ordinateur sur le port WAN du WRT54GL et capturer le trafic réseau avec un logiciel comme Wireshark. Pour que cela fonctionne, l'ordinateur que vous avez branché au port WAN doit prendre la même adresse IP que le modem qui est habituellement branché à votre routeur (ou redéfinir la route pas défaut sur le routeur, mais c'est une autre histoire). Si vous ne la connaissais pas, regardez la capture réseau : vous verrez des trames ARP "Who has x.x.x.x tell y.y.y.y". Il vous suffit de donner à votre ordinateur l'adresse IP x.x.x.x . Vous pouvez ensuite lancer la capture.

Dans tous les cas, une fois cela fait, revenez sur l'ordinateur qui est toujours connecté sur un des ports LAN du routeur. Lancez un dig/nslookup en direction de votre résolveur personnel. Dans la capture réseau, vous devez voir des adresses IP qui appartiennent à l'un des serveurs qui gèrent la racine DNS ou les TLD comme 192.26.92.30 ou 192.54.112.30.

Pour savoir si une adresse IP appartient à l'un des serveurs en charge de la racine ou des TLD, vous pouvez utiliser la commande host sous GNU/Linux ou nslookup sous Windows. Dans le cas de 192.54.112.30, nous obtenons "30.112.54.192.in-addr.arpa domain name pointer h.gtld-servers.net.". Il s'agit donc d'un des serveurs qui gère le domaine de premier niveau .net. .

Si tout va bien, vous pouvez passer à l'étape suivante.

Changer l'adresse du résolveur DNS sur les machines grâce à DHCP et sur le routeur

Les Unixiens se seront précipités pour éditer le fichier /etc/resolv.conf 😉 . C'est un bon réflexe en temps normal mais, dans le cas présent, ce fichier est généré à chaque démarrage du routeur, ou plus précisément à chaque démarrage de dnsmasq.

Il faut donc éditer le script de démarrage : /etc/init.d/dnsmasq. A la 5éme ligne, vous pouvez lire :

DNS_SERVERS= ""

Saisissez ici l'adresse ip LAN de votre routeur.

Note : dnsmasq ajoutera le résolveur 127.0.0.1 automatiquement à la fin de la liste. Si vous trouvez ça inconcevable, que ça vous empêche de dormir la nuit, supprimez la 5eme ligne du fichier puis cherchez cette ligne dans la fonction start :

DNS_SERVERS= "$DNS_SERVERS 127.0.0.1"

Enlevez tout ce qu'il y a entre guillemets et mettez la ou les adresse(s) IP de votre(vos) résolveurs, séparés par un espace.

Il ne reste plus qu'a redémarrer dnsmasq :

root@OpenWRT:~# /etc/init.d/dnsmasq restart

Notez que les modifications ne prendront effet sur vos périphériques que lors du renouvellement du bail DHCP. Vous pouvez le forcer (ipconfig /renew sous windows, désactiver/activer l'interface réseau, reboot, etc.).

Désactiver la fonctionnalité DNS de dnsmasq

A présent, nous pouvons désactiver la partie DNS de dnsmasq. Le man de dnsmasq nous dis ("Listen on <port> instead of the standard DNS port (53). Setting this to zero completely disables DNS function, leaving only DHCP and/or TFTP.") qu'il suffit de configurer le port à 0 pour désactiver la fonction DNS. Pour mettre cela en application, il suffit d'aller dans l'interface web, dans "Services", "Dnsmasq", d'ajouter un champ "port DNS", de le configurer à 0 et d'appliquer les changements.

Pour les True Warrior qui veulent le faire depuis le shell, ça donne :

root@OpenWRT:~# uci set dhcp.@dnsmasq[0].port=0
root@OpenWRT:~# /etc/init.d/dnsmasq restart

Mettre en place durablement notre résolveur

En effet, dans l'état actuel, djbdns ne se relancera pas au prochain reboot du routeur et les paramètres seront perdus 🙁 .

Pour que le serveur se lance tout seul au démarrage du routeur vous pouvez créer un lien de /etc/init.d/dnscache vers /etc/rc.d/Sxxdnscache (ou xx représente l'ordre de boot) avec la commande ln ou plus simplement taper la commande :

root@OpenWRT:~# /etc/init.d/dnscache enable

Pour enregistrer les paramètres que nous avons donnés à uci, il faut taper ceci dans ash (le shell par défaut d'OpenWRT) :

root@OpenWRT:~# uci commit network.lan
root@OpenWRT:~# uci commit dhcp
root@OpenWRT:~# uci commit djbdns
root@OpenWRT:~# uci commit

Détaillons un peu, toujours dans l'ordre :

  • On enregistre le changement de serveur DNS pour le réseau local.
  • On enregistre le fait que dnsmasq ne doit plus faire office de résolveur DNS ainsi que le changement de serveur DNS pour le réseau local, le cas échéant.
  • On enregistre toutes les modifications faites à djbdns.
  • On enregistre tout. Pas utile mais je le fais par mesure de précaution.

Et voila, votre résolveur DNS personnel est en place sur votre routeur équipé avec OpenWRT.

Si dnscache plante lorsqu'une déconnexion survient sur l'interface WAN (ÉDIT du 08/08/2011 à 14h00 )

J'ai rédigé un billet à ce sujet : OpenWRT : relancer automatique dnscache en cas de déconnexion du net