DNS dynamique sécurisé avec nsupdate et BIND9

Testé avec Bind 9.7.3 sous Debian Squeeze, poste moniteur sous Debian Squeeze

De nombreuses connexions internet ADSL ou satellite ne disposent pas d’une adresse IP publique fixe. Dès lors qu’on veut installer un serveur derrière ce type de liaison ou mettre en place un accès distant, on utilise généralement des services de DNS dynamique tels que Dyn ou no-ip. Ces services restent très limités dans leur version gratuite.

L’idée est donc de pouvoir s’en passer en utilisant notre propre serveur DNS pour tenir à jour un enregistrement DNS du type monip.mondomaine.com qui pointera sur l’adresse IP externe courante de la connexion. L’enregistrement DNS sera mis à jour de façon sécurisée par l’utilitaire DNS nsupdate.

Pré-requis

  • Un serveur DNS Bind 9.2 ou plus récent connecté directement à internet. Ce serveur doit être maître pour la zone qui contiendra l’enregistrement DNS choisi :
    Pour l’enregistrement monip.onsenfout.com le serveur doit être maître pour la zone onsenfout.com.
  • Une machine linux derrière la connexion à surveiller, ici j’utilise un serveur Debian Squeeze. Je l’appelle poste moniteur dans la suite de l’article.

Préparation du poste moniteur

installer les utilitaires DNS

aptitude install bind9utils dnsutils

Générer la paire de clés TSIG

Il y a 2 méthodes pour sécuriser les mises à jours dynamiques du DNS : avec des clés TSIG ou avec des clés SIG(0). Les clés TSIG sont symétriques, la même clé est utilisée par le client et le serveur. A l’inverse, les clés SIG(0) sont asymétriques, ce qui est à priori plus sécurisé, mais la version actuelle de nsupdate dans Squeeze (dnsutils 1:9.7.3.dfsg-1~squeeze4) plante avec les clés SIG(0). Je me suis donc rabattu sur TSIG en attendant que ça sèche.

La clé sera générée avec dnssec-keygen:

dnssec-keygen -a HMAC-MD5 -b 512 -n HOST monip.onsenfout.com

On obtient deux fichiers :

Kmonip.onsenfout.com.+157+?????.private
Kmonip.onsenfout.com.+157+?????.key

Le contenu du fichier Kmonip.onsenfout.com.+157+13642.private est le suivant :

cat Kmonip.onsenfout.com.+157+13642.private 
Private-key-format: v1.3
Algorithm: 157 (HMAC_MD5)
Key: BXLFFCyzbVK1OxTu+3HaJ2YytB64Rxplat738Zk3UFWDeLtkfUYIwgbX83LYR5W6z7fRgGBcwgVD191KtOErMQ==
Bits: AAA=
Created: 20120404104934
Publish: 20120404104934
Activate: 20120404104934

C’est le moment de copier la valeur de la clé (après Key: ) dans le presse-papiers, on en aura besoin pour configurer le serveur DNS.

Configuration du serveur DNS

créer un fichier /etc/bind/keys.conf et y ajouter la clé publique :

key monip.onsenfout.com {
	algorithm HMAC-MD5;
	secret "BXLFFCyzbVK1OxTu+3HaJ2YytB64Rxplat738Zk3UFWDeLtkfUYIwgbX83LYR5W6z7fRgGBcwgVD191KtOErMQ==";
};

Editer /etc/bind/named.conf.local et inclure le fichier de clé :

include "/etc/bind/keys.conf";

Côté serveur il ne reste plus qu’à définir les autorisations de mise à jour de la zone. L’instruction update-policy va nous permettre d’autoriser la clé monip.onsenfout.com à modifier l’enregistrement DNS de type A monip.onsenfout.com, et seulement celui là :

zone	"onsenfout.com" in {
	type master;
	file "db.onsenfout.com";
	update-policy {
		grant monip.onsenfout.com. name monip.onsenfout.com. A;
	};

On redémarre Bind :

/etc/init.d/bind9 restart

Test de la mise à jour dynamique

Le serveur est prêt à recevoir des mises à jour dynamiques. Nous allons pouvoir vérifier que tout fonctionne en initialisant l’enregistrement DNS avec nsupdate à partir du poste moniteur. La syntaxe de nsupdate est:

nsupdate -k <fichier clé privée> -v <fichier>

-v force l’utilisation de TCP au lieu d’UDP
fichier contient le jeu d’instructions à envoyer au serveur, s’il est omis nsupdate passe en mode interactif.

C’est ce qu’on va faire pour initialiser l’enregistrement monip.onsenfout.com avec l’adresse 0.0.0.0 et un TTL de 30. La commande show affiche la requète de mise à jour, send l’envoie au serveur :

nsupdate -k Kmonip.onsenfout.com.+157+13642.private -v
> zone onsenfout.com
> update add monip.onsenfout.com. 30 A 0.0.0.0
> show
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:      0
;; flags:; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;; ZONE SECTION:
;onsenfout.com.			IN	SOA
 
;; UPDATE SECTION:
monip.onsenfout.com.		30	IN	A	0.0.0.0
> send
> quit

On vérifie que tout s’est bien passé en regardant les logs de Bind et en faisant un dig sur le serveur DNS :

dig +nocmd monip.onsenfout.com +noall +answer
monip.onsenfout.com.		30	IN	A	0.0.0.0

Le fichier journal

Après la première mise à jour dynamique, on s’aperçoit qu’un nouveau fichier db.onsenfout.com.jnl a été créé dans le répertoire des fichiers de zones, /var/cache/bind/ chez moi. On constate aussi que la commande dig retourne bien l’enregistrement demandé, mais que celui-ci n’apparaît pas dans le fichier de zone : lors des mises à jour dynamiques, Bind modifie la zone chargée en mémoire, et stocke l’information dans le fichier journal de la zone avec une extension .jnl. C’est un point important à prendre en compte si on doit éditer manuellement une zone comportant des enregistrements dynamiques.

Dans ce cas il faut suspendre les mises à jour de la zone et forcer l’écriture du journal avec rndc freeze :

rndc freeze onsenfout.com

Editer la zone sans oublier d’incrémenter le numéro de série, rétablir les mises à jour et recharger la zone avec rndc thaw :

rndc thaw onsenfout.com

Le fichier journal n’est pas directement lisible mais l’utilitaire named-journalprint remédie au problème.

Un p’tit script pour finir

Maintenant que tout fonctionne il ne manque plus qu’un script placé en crontab sur le poste moniteur, toutes les minutes par exemple :

#!/bin/bash
# Teste si l'ip publique a été modifiée et met à jour l'enregistrement DNS
# Utilise la clé TSIG
# François Grange 2012
# Inits
ADMIN="xxxx@onsenfout.com"
ZONE="onsenfout.com"
RR="monip.$ZONE."
TTL=30
LAST_IP="0.0.0.0"
LAST_IP_FILE="/var/local/lastip"
FLAG_ERR=0
ERR_FILE="/var/local/dyndns.err"
KEY_FILE="/etc/bind/keys/Kmonip.onsenfout.com.+157+13642.private"
TMP_FILE="/tmp/dyndns.tmp"
CUR_IP=`wget -q -O - whatismyip.org`
sortie() {
	# mail si erreur ou modification
	if [ $1 -eq 1 ]; then
		if [ ! -f $ERR_FILE ]; then
			touch $ERR_FILE
			cat $TMP_FILE | mail -s "Erreur de mise à jour de $RR" $ADMIN
		fi
	else
		cat $TMP_FILE | mail -s "Changement d'IP pour $RR" $ADMIN
		if [ -f $ERR_FILE ]; then
			rm -f $ERR_FILE
		fi
	fi
	exit
}
if [ "$CUR_IP" = "" ] || [ "$CUR_IP" = "unknown" ]; then
	echo "Impossible de récupérer l'IP actuelle - Abandon" > $TMP_FILE
	sortie 1
fi
if [ -f $LAST_IP_FILE ]; then
	LAST_IP=`cat $LAST_IP_FILE`
fi
if [ ! "$LAST_IP" = "$CUR_IP" ]; then
	# L'adresse a changé
	echo "IP actuelle : $CUR_IP" > $TMP_FILE
	echo "IP précédente : $LAST_IP" >> $TMP_FILE
	(
	echo "zone $ZONE"
	echo "update delete $RR A"
	echo "update add $RR $TTL A $CUR_IP"
	echo "send"
	) | nsupdate -k $KEY_FILE -v
	if [ $? -ne 0 ]; then
		echo "Echec de la mise à jour de $RR" >> $TMP_FILE
		FLAG_ERR=1
	else
		echo $CUR_IP > $LAST_IP_FILE
	fi
	sortie $FLAG_ERR
fi

Remarque : J’utilise dans le script le service web myip.dnsdynamic.com. Pour plus d’indépendance c’est très facile de se créer son propre service avec une page php d’une ligne :

<?php print $_SERVER['REMOTE_ADDR'] ?>

Liens

Secure dynamic DNS howto
nsupdate: Painless Dynamic DNS
Mise en place d’un serveur DNS dynamique
Obtenir l’adresse IP publique en ligne de commande

No votes yet.
Please wait...

Une réaction sur “DNS dynamique sécurisé avec nsupdate et BIND9”

  1. La fibre à Maizières-Lès-Metz | Jean-Christophe VASSORT

    […] Et j’ai trouvé également un moyen de mettre à jour l’IP de mon serveur @home sur mon serveur DNS sans avoir recours au… […]

    No votes yet.
    Please wait...

Laisser une réponse