lalahop

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.

  1. Euh si je puis me permettre mon ptit guigui, tu te complique vraiment la vie pour pas grand chose … je penses que le mieux c’est de changer de fai et de prendre free … tu aura alors une ip fixe.
    A moins que le jeux ce ne soit de mettre le système en place … ce qui est amusant aussi

    • Heu … Je vais me compliquer encore plus la vie en répondant le plus sérieusement du monde à ton message « détendu ». 😛

      Le premier paragraphe m’a fait un peu bondir. Changer de FAI pour juste une IP fixe, c’est un peu overkill surtout pour aller chez Free. Free, c’est comme tous les autres grands FAI commerciaux : pas confiance (et je reste poli). Surtout vu l’actualité « récente » concernant ce FAI en particulier. 😉

      Le deuxième paragraphe me redonne espoir : oui, en effet, mettre en place ce « truc » a été un jeu qui m’a pris comme ça, un soir, sur une envie. Avoir une problématique, aussi insignifiante soit-elle et la résoudre. Je ne résous pas des problèmes de cet ordre là en changeant de FAI.

  2. Que dire de plus que super utile et unique !

    Fonctionne impeccablement du routeur familial vers mon serveur hébergé par Touraine Data Network, FAI Associatif en région Centre (dont je suis le président).

    @GuiGui si t’est en région Centre : http://www.tdn-fai.net ! Ligne ADSL dès 30€ par mois !