IP dynamique et DNS

J’ai depuis quelques temps maintenant un Raspberry pi, qui se trouve chez moi, et sur lequel je souhaite installer un logiciel de backup et d’autres trucs (je ferai sûrement un article dessus à ce moment là). Il faut donc que mon Raspberry soit accessible depuis internet. Le souci est que je suis chez Orange et j’ai donc une IP dynamique (merci Orange…).

Il existe des sites comme DynDNS ou No-IP qui propose de lier l’IP de votre box à un sous-domaine chez eux afin de connaitre facilement votre IP. Mais je préfère utiliser un système que j’héberge moi-même plutôt que d’utiliser un service tiers.
Pour ce faire, je vais créer un sous-domaine, monip.memodugeek.info par exemple, et faire en sorte que mon Raspberry vienne mettre à jour ce nom de domaine pour le faire pointer vers l’IP publique de ma box Orange.
Il faut quand même quelques pré-requis pour cette méthode :

  • Un serveur DNS Bind configuré, relié à internet, et étant maître de la zone qui sera modifiée (memodugeek.info dans mon cas). Je vais utiliser mon serveur OVH (sur lequel est hébergé ce site) dans mon cas.
  • Un serveur Linux situé derrière la box, qui va récupérer l’IP publique et la transmettre au serveur DNS. Dans mon cas c’est mon Raspberry qui va se charger de ça.
  • Avoir un sous-domaine.

Dans la suite de l’article, je parlerai, pour désigner les machines, de serveur DNS (mon serveur OVH dans mon cas) et de Raspberry (serveur situé derrière ma box Orange).

1) Configuration du Raspberry

Commençons par la configuration du Raspberry. Installez les paquets bind9utils et dnsutils à l’aide la commande
sudo aptitude install bind9utils dnsutils

Comme le Raspberry va devoir communiquer avec le serveur DNS pour mettre à jour le nom de domaine, on va sécuriser cet échange. Pour ce faire, on va utiliser une clé TSIG qui nous servira à générer un HMAC, qui couple une fonction de hachage et une clé secrète afin de vérifier l’intégrité et l’authenticité du message. Pour l’algorithme de hachage, on va utiliser SHA2. On peut utiliser d’autres algorithmes également.

L’utilitaire dnssec-keygen, fournit par Bind, va nous permettre de créer cette clé. Entrez la commande suivante :
dnssec-keygen -a HMAC-SHA512 -b 512 -n HOST iprasp
-a HMAC-SHA512 : indique l’algorithme utilisé pour le hachage, SHA-512 qui fait parti de la famille SHA2
-b 512 : indique le nombre de bit de la clé, 512 ici.
-n HOST : j’ai pas vraiment compris la différence entre tous les choix possible de -n (USER, ZONE, ENTITY, etc) mais dans notre cas il faut choisir HOST.
A la fin de la commande, on indique le nom de clé.

Cette commande va générer deux clés : Kiprasp.+165+07175.key et Kiprasp.+165+07175.private. Celle qui va surtout nous intéresser est la clé privée. Voici le contenu de la clé :

cat Kiprasp.+165+07175.private
Private-key-format: v1.3
Algorithm: 165 (HMAC_SHA512)
Key: qIBPc05SV9C6dphFKYWRYop+wRPaA/J1b5Te/Is0HDxYhBpS7qVmDFsDN2W/DeGmCxKnq+nXo+wOoaqJjeywkw==
Bits: AAA=
Created: 20180523133201
Publish: 20180523133201
Activate: 20180523133201

C’est bien évidemment une clé que j’ai créée uniquement pour les besoins de l’article, il ne faut jamais partager une clé comme je le fais là.
Ce qui va nous intéresser est la ligne « Key: ». Copiez la clé dans le presse papier (qIBPc05SV9C6dphFKYWRYop+wRPaA/J1b5Te/Is0HDxYhBpS7qVmDFsDN2W/DeGmCxKnq+nXo+wOoaqJjeywkw== dans notre exemple).

La configuration du Raspberry étant terminée, on va passer à la configuration du serveur DNS.

2) Configuration du serveur DNS

Ouvrez le fichier /etc/bind/named.conf.local et ajoutez les lignes suivantes :

key iprasp {
        algorithm HMAC-SHA512;
        secret "qIBPc05SV9C6dphFKYWRYop+wRPaA/J1b5Te/Is0HDxYhBpS7qVmDFsDN2W/DeGmCxKnq+nXo+wOoaqJjeywkw==";
};

Ces lignes servent à indiquer à Bind, le serveur DNS, la clé à utiliser pour sécuriser les échanges. Après « key » il faut bien évidemment indiquer le nom que vous avez utiliser lors de la génération de la clé TSIG.
De même, si vous avez utiliser un autre algorithme que HMAC-SHA512, pensez à le modifier.

Dans le même fichier, ajoutez les lignes suivantes dans la zone souhaitée (memodugeek.info dans notre exemple) :

update-policy {
        grant "iprasp" subdomain monip.memodugeek.info A;
    };

On indique à Bind, grâce à cette ligne, que toute demande de mise à jour DNS utilisant la clé « iprasp » est autorisée pour le sous-domaine « monip.memodugeek.info » et uniquement ce sous-domaine.
Sans la clé TSIG, impossible de mettre à jour ce sous-domaine ou un autre et la clé ne permet de mettre à jour que ce sous-domaine.

Tant qu’on est dans ce fichier, si le fichier de zone est dans le répertoire « /etc/bind », il va falloir le déplacer sinon on va avoir des problèmes d’accès. Si c’est le cas, modifiez la ligne file "/etc/bind/pri.memodugeek.info"; par file "/var/cache/bind/pri.memodugeek.info"; . Sauvegardez les modifications puis copiez le fichier /etc/bind/pri.memodugeek.info dans le répertoire /var/cache/bind/.

Votre zone devrait donc ressembler à ça :

zone "memodugeek.info" {
        type master;
        allow-transfer {213.186.33.199;};
        file "/var/cache/bind/pri.memodugeek.info";
        update-policy {
        grant "iprasp" subdomain monip.memodugeek.info A;
    };
};

La ligne « allow-transfer » sert au DNS secondaire et n’a rien à voir avec ce qu’on fait.

Il ne nous reste plus qu’à redémarrer Bind avec la commande sudo systemctl restart bind9.service

On n’a plus qu’à tester pour vérifier que ça fonctionne.

3) Test de mise à jour du DNS

Pour mettre à jour le DNS, on va utiliser l’utilitaire nsupdate.
Sur le Raspberry, tapez la commande nsupdate -k cléprivée en indiquant votre clé privé à la place de « cléprivée ». La clé publique (le fichier en .key) doit être situé dans le même dossier.
Ensuite tapez « zone memodugeek.info » puis appuyez sur Entrée.
Puis tapez « update add monip.memodugeek.info. 30 A 0.0.0.0 » puis appyez sur Entrée.
La première ligne sert à indiquer quelle zone DNS on va modifier et la seconde indique qu’on va modifier le sous-domaine monip.memodugeek.info afin de le faire pointer vers l’adresse 0.0.0.0. Evidemment ces lignes sont à modifier en fonction de votre nom de domaine.

Maintenant tapez « show » puis Entrée. Vous devriez avoir quelque chose de ce type :

> zone memodugeek.info
> update add monip.memodugeek.info. 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:
;memodugeek.info.               IN      SOA

;; UPDATE SECTION:
monip.memodugeek.info.  30      IN      A       0.0.0.0

Tapez ensuite « send » puis Entrée pour mettre à jour le DNS. Si nsupdate vous affiche un message d’erreur, il vous faudra aller éplucher les logs de Bind sur le serveur DNS pour voir où se situe le problème.

Si tout a fonctionné, vous devriez voir ceci dans les logs de Bind sur le serveur DNS:

May 23 16:23:41 ns397819 named[31585]: client 92.167.57.120#58242/key iprasp: updating zone 'memodugeek.info/IN': adding an RR at 'monip.memodugeek.info' A 0.0.0.0
May 23 16:23:41 ns397819 named[31585]: zone memodugeek.info/IN: sending notifies (serial 2018052208)

Et, toujours sur le serveur DNS, si vous lancez la commande dig +nocmd monip.memodugeek.info +noall +answer vous devriez avoir ceci :
rasp.memodugeek.info. 30 IN A 0.0.0.0

Il ne nous reste plus qu’à automatiser tout ça avec un petit script.

4) Automatisation de la mise à jour

Créez le script suivant sur le Raspberry :

#!/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
# Modifié par fate 2018
# Inits
ADMIN="admin@memodugeek.info"
ZONE="memodugeek.info"
RR="rasp.$ZONE."
TTL=30
LAST_IP="0.0.0.0"
LAST_IP_FILE="/home/pi/update_ip/lastip"
ERR_FILE="/home/pi/update_ip/dyndns.err"
KEY_FILE="/home/pi/update_ip/Kiprasp.+165+07175.private"
TMP_FILE="/home/pi/update_ip/dyndns.tmp"
CUR_IP=`wget -q -O - memodugeek.info/monip.php`

# Fonction de gestion des message d'erreur ou de changement d'IP
sortie() {
        # Si erreur de mise à jour, on envoi un mail indiquant qu'il y a eu une erreur avec le fichier d'erreur en pièce-jointe.
        if [ $1 -eq 1 ]; then
                echo "Une erreur est survenue lors de la mise à jour du DNS. Le message d'erreur se trouve en pièce-jointe." | mail -s "Erreur de mise à jour de $RR" -A $ERR_FILE $ADMIN
        # Si changement d'IP, on envoi un mail avec la nouvelle IP
        else
                cat $TMP_FILE | mail -s "Changement d'IP pour $RR La nouvelle IP est $CUR_IP." $ADMIN
        fi
        exit
}

# On supprime l'ancien fichier d'erreur s'il existe
if [ -f $ERR_FILE ]; then
                        rm -f $ERR_FILE
                fi
# Si on n'arrive pas à récupérer l'adresse IP publique, on sort en envoyant un mail d'erreur via sortie()
if [ "$CUR_IP" = "" ] || [ "$CUR_IP" = "unknown" ]; then
        echo "Impossible de récupérer l'IP actuelle - Abandon" > $ERR_FILE
        sortie 1
fi

# On récupère la dernière IP connue
if [ -f $LAST_IP_FILE ]; then
        LAST_IP=`cat $LAST_IP_FILE`
fi

# Si l'adresse a changé, on lance nsupdate en mettant la sortie debug dans le fichier d'erreur.
if [ ! "$LAST_IP" = "$CUR_IP" ]; then
        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 -d  >/dev/null 2> $ERR_FILE
        if [ $? -ne 0 ]; then
                sortie 1
        else
                echo $CUR_IP > $LAST_IP_FILE
fi
        sortie 0
fi

Ce script provient de François Grange, je l’ai juste modifié un peu à ma sauce et l’ai commenté un peu plus, histoire que les néophyte du bash le comprenne.

Tout ce que vous avez besoin de modifier, ce sont les lignes ADMIN, ZONE, RR, LAST_IP_FILE, ERR_FILE, KEY_FILE, TMP_FILE. Voici à quoi correspondent ces variable :

  • ADMIN : adresse mail de l’administrateur
  • ZONE : nom de domaine principal. Ne pas indiquer le sous-domaine ici.
  • RR : le sous-domaine. On récupère le nom de domaine principal via la variable « $ZONE », vous n’avez donc besoin de modifier que le début du sous-domaine.
  • LAST_IP_FILE, ERR_FILE, KEY_FILE, TMP_FILE : chemin complet, respectivement, du fichier de la dernière IP envoyée, du fichier d’erreur, de la clé privée et du fichier de log temporaire. J’ai mis tous ces fichiers dans mon home car je souhaite que ce soit mon utilisateur qui exécute ce script mais vous pouvez mettre ces fichiers là où vous le souhaitez (il faut quand même que l’utilisateur qui va exécuter le script y ait accès).
    Ces fichiers seront créés lors de l’exécution du script, pas besoin de les créer vous-même.

Pour résumer, ce script va récupérer l’adresse IP publique, vérifier si elle est différente de la dernière envoyée au serveur DNS et, si c’est le cas, envoyer la nouvelle IP publique au serveur DNS afin qu’il mette à jour le sous-domaine. Une fois la mise à jour faite avec succès, un mail contenant l’ancienne et la nouvelle IP publique sera envoyé à l’admin.
Si la dernière IP envoyée est identique à l’IP publique récupérée, le script se termine sans faire de mise à jour DNS.
Si une erreur est détectée lors de la mise à jour, un mail sera envoyé à l’admin avec le fichier d’erreur en pièce jointe.

Pour récupérer l’adresse IP, je passe par un service maison. Je la récupère grâce à une page php que j’ai uploadée sur le site et qui contient le code suivant :
<?php print $_SERVER['REMOTE_ADDR'] ?>
Cette page renvoie l’adresse IP publique du PC qui l’affiche. C’est comme ça que je renseigne la variable CUR_IP dans le script avec la commande `wget -q -O - memodugeek.info/monip.php`. Vous pouvez soit utiliser la page hébergée sur mon serveur, soit en créer une et l’héberger sur votre serveur.

On va passer par cron pour lancer automatiquement ce script. Editez cron avec la commande crontab -e et ajoutez la ligne suivante :
*/10 * * * * /home/pi/update_ip/update_ip.sh. Sauvegardez puis quitter.
Le script va s’exécuter toutes les 10 minutes. Il faut bien entendu modifier le chemin du script.

Comme je le disais plus tôt, ce script envoi des mails, il faut donc que votre Raspberry puisse envoyer des mails. Si ce n’est pas le cas, je vous conseille ssmtp qui permet d’envoyer des mails en passant par le serveur smtp de votre choix (le votre ou celui de votre FAI par exemple).

Pour l’installer, lancez la commande sudo aptitude install ssmtp mailutils mpack . Une fois installé, vous pouvez le configurer en éditant le fichier /etc/ssmtp/ssmtp.conf. Voici à quoi ressemble le mien qui utilise le serveur SMTP installé sur mon serveur OVH :

#
# Config file for sSMTP sendmail
#
# The person who gets all mail for userids < 1000
# Make this empty to disable rewriting.
root=admin@memodugeek.info

# Nom d'utilisateur du compte email avec lequel vous envoyez les courriels
AuthUser=identifiantducomptemail

# Mot de passe de ce même compte
AuthPass=motdepasseducomptemail

# The place where the mail goes. The actual machine name is required no
# MX records are consulted. Commonly mailhosts are named mail.domain.com
mailhub=adresseduserveurmail:portutilisé

# Where will the mail seem to come from?
rewriteDomain=memodugeek.info

# The full hostname
hostname=raspberrypi

# Are users allowed to set their own From: address?
# YES - Allow the user to specify their own From: address
# NO - Use the system generated From: address
FromLineOverride=YES

# Utilisation d'une connexion sécurisée SSL/TLS (décommenter pour activer)
UseSTARTTLS=YES

J’ai retiré les informations sensibles, mais ça vous permettra de comprendre comment le configurer.

Désormais, votre sous-domaine va pointer vers l’IP publique de votre Raspberry sans que vous ne soyez dépendant d’un service tiers comme no-IP ou dyndns 😉

 

Sources :

http://tavie.onsenfout.com/2012/04/04/dns-dynamique-securise-avec-nsupdate-et-bind9/
https://jpmens.net/2010/09/28/performing-dynamic-dns-updates-on-your-dns/
https://doc.ubuntu-fr.org/ssmtp

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.