DNS dynamique fait maison

jeu. 30 avril 2015

Introduction

Les machines s'identifient sur Internet avec des adresses dites « IP » (Internet Protocol). Par exemple, si vous vous connectez à l'adresse 94.23.6.219 avec votre navigateur web, vous tomberez sur la machine qui héberge le domaine metrodore.fr.

Votre lieu ou vous avez accès à Internet (chez vous, à votre travail, ou autre) possède généralement aussi une adresse sur le réseau1. Mais il se trouve que cette adresse est rarement fixe. Comment quelqu'un souhaitant se connecter à votre machine peut il alors y avoir accès ?

Une façon est de maintenir un DNS dynamique. Le DNS est l'annuaire des noms de l'Internet. C'est celui qui lie par exemple le nom metrodore.fr à la machine connectée à l'adresse 94.23.6.219. On peut ainsi imaginer maintenir un sous-domaine, par exemple chez-moi.home.amie.coop qui pointerai sur la machinbox de chez moi, et faire en sorte que ce domaine soit toujours à jour, même si l'adresse IP de chez moi change lors d'une nuit de pleine lune.

C'est ce que nous allons faire dans cet article, sur une Debian moderne. Ce qui suit est volontairement un peu technique.

Prérequis : disposer d'un serveur accessible en ssh, hébergeant un service DNS orchestré par Bind, et d'au moins une machine cliente.

Génération d'une clé ssh, coté client

Si vous disposez déjà d'une paire de clés déprotégée, cette étape est facultative.

On va commencer par générer une paire de clé ssh qui nous servira pour l'authentification de notre client. Cette clé ne sera pas protégée par un mot de passe pour la rendre utilisable par un planificateur cron, mais en contrepartie on restreindra les possibilités du compte dnsadmin au strict minimum.

ssh-keygen -t dsa -N ""

On obtient alors deux fichiers : la clé publique id_rsa.pub et la clé privée id_rsa dans le dossier ~/.ssh/.

Configuration coté serveur

Création d'un utilisateur dnsadmin

On commence par créer un utilisateur sur le serveur :

adduser dnsadmin --shell /bin/sh

On saisi un mot de passe, on répond aux questions posées, et nous voila avec un utilisateur dnsadmin tout frais.

On va ensuite créer un dossier .ssh dans le répertoire de connexion de dnsadmin.

su dnsadmin -c 'mkdir /home/dnsadmin/.ssh'

Création d'une zone DNS dédiée

On commence par éditer le fichier de zone qui fait autorité chez nous (ici : amie.coop), pour lui ajouter un Ressource Record correspondant à la zone du service de DNS dynamique (home.amie.coop chez nous), hébergée par notre serveur DNS (hote.amie.coop chez nous) :

home              IN      NS      hote.amie.coop.

Ensuite on crée le fichier de zone /etc/bind/home.amie.coop :

$ORIGIN .
home.amie.coop          IN SOA  home.amie.coop. root.amie.coop. (
                                2015040212 ; serial
                                7200       ; refresh (2 hours)
                                1800       ; retry (30 minutes)
                                2419200    ; expire (4 weeks)
                                600        ; minimum (10 minutes)
                                )
                        NS      home.amie.coop.
                        A       94.23.6.219

Pas la peine d'y mettre trop les formes, ce fichier se fera écraser régulièrement par Bind. Ne pas oublier non plus de vérifier que ce fichier de zone appartient au bon utilisateur.

Enfin, on déclare la zone dans la configuration de Bind :

zone "home.amie.coop" {
        type master;
        file "/etc/bind/home.amie.coop";
        allow-update {localhost;};
};

Notez le paramètre allow-update, important, qui vous permettra de mettre à jour votre zone localement depuis votre serveur par un appel à nsupdate.

Vous redémarrez ensuite Bind, et avec un peu de chance votre zone DNS sera chargée et fonctionnelle.

Installation du script de mise à jour de la zone DNS

On installe dans le répertoire de connexion de l'utilisateur dnsadmin le script suivant :

domain=$1
ip=`echo $SSH_CONNECTION | awk '{print $1}'`
date=`date`
nsupdate << EoU
server localhost
zone home.amie.coop
update delete $domain. A
update delete $domain. TXT
update add $domain. 1800 A $ip
update add $domain. 1800 TXT "Updated by dnsadmin, the $date"
show
send
quit
EoU

ret=$?
echo Returned $ret
exit $ret

par exemple avec la commande

cat > /home/dnsadmin/update-zone.sh

N'oubliez pas de donner au script les permissions d'exécution pour l'utilisateur dnsadmin :

chown dnsadmin:dnsadmin /home/dnsadmin/update-zone.sh
chmod u+x /home/dnsadmin/update-zone.sh

Celui-ci récupère le nom du sous-domaine en argument, récupère l'IP de la connexion entrante via la variable d'environnement SSH_CONNECTION, et utilise le programme nsupdate pour :

  • effacer les entrées A et TXT du sous domaine considéré ;
  • ajouter une entrée A vers l'adresse de la connexion ssh entrante ;
  • ajouter une entrée TXT donnant des informations de debug.

Remarque : en cas de besoin l'option -d ajoutée à nsupdate permet d'avoir des informations supplémentaires de debug.

On peut désormais tester notre script avec une commande du genre de :

SSH_CONNEXION="42.42.42.42" /home/dnsadmin/update-dns.sh bling.home.amie.coop

qui devrait renvoyer quelque chose comme :

Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:      0
;; flags:; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;; ZONE SECTION:
;home.amie.coop.      IN  SOA

;; UPDATE SECTION:
bling.home.amie.coop. 0 ANY A 
bling.home.amie.coop. 0 ANY TXT 
bling.home.amie.coop. 1800  IN  A 42.42.42.42
bling.home.amie.coop. 1800  IN  TXT "Updated by dnsadmin, the Thu Apr 30 15:42:00
CEST 2015"

Returned 0

Un appel à dig ou nslookup doit faire apparaitre le changement. Attention de préciser le bon serveur auquel faire appel, pour ne pas subir le TTL du DNS. Par exemple :

dig @amie.coop bling.home.amie.coop TXT

Exécution distante du script

On commence par retirer à l'utilisateur dnsadmin la capacité de se connecter sans clé ssh. Pour cela on ajoute à la fin du fichier /etc/ssh/sshd_config :

Match User dnsadmin
    PasswordAuthentication no

On copie ensuite le contenu de la clé publique créée sur la machine cliente dans le fichier /home/dnsadmin/.ssh/authorized_keys, et on l'édite pour faire commencer la ligne concernée comme suit :

command="/home/dnsadmin/update-zone.sh $SSH_ORIGINAL_COMMAND",no-port-forwarding,no-x11-forwarding,no-agent-forwarding ssh-rsa ...clé ssh... nom@machine

Cette configuration restreint la connexion ssh à n'exécuter que le script de mise à jour de la zone. La variable $SSH_ORIGINAL_COMMAND permet de transmettre l'argument, qui sera ici le sous-domaine concerné.

Configuration coté client

On peut désormais lancer notre script depuis la machine cliente, avec une commande du genre de :

/usr/bin/ssh dnsadmin@amie.coop bling.home.amie.coop

Script de mise à jour

Plutôt que de mettre à jour aveuglément le DNS toutes les heures, on peut chercher à détecter le changement d'IP, par exemple en comparant le résultat du DNS avec une requête à un service du genre de myipaddress.com, service extrèmement simple à monter soi-même. Ainsi, on peut faire une requête http, vérifier la présence de notre IP dans la réponse, et mettre à jour le DNS si besoin. C'est ce que fait ce script, basé sur une sonde Nagios :

ip=`dig +short bling.home.amie.coop @amie.coop`
/usr/lib/nagios/plugins/check_http -H myip.amie.coop -s $ip

if [ $? -eq 0 ]
then
  exit 0
fi

/usr/bin/ssh dnsadmin@amie.coop -p 2222 bling.home.amie.coop

Et voilà, il n'y a plus qu'à planifier un appel régulier à ce script, via un démon cron. Et vous voilà avec un service de DNS dynamique 100% indépendant !


  1. A minima vous avez une adresse par laquelle vous accédez au réseau, même si ce n'est pas celle de votre ordinateur. Par exemple, si vous accédez au réseau via une « machinbox », c'est son adresse qui est visible du coté du réseau. Si vous accédez au réseau depuis votre téléphone portable, l'adresse de sortie sera une machine de votre opérateur télécom. Pour découvrir quelle est votre adresse IP de sortie, vous pouvez par exemple aller sur cette page

Tags: Amie Logiciel libre Service mutualisé Debian Technique