Script de màj d'ip dans bind et utilisation de l'API gandi

Bonjour à tous,
Voilà après avoir un peu galéré, j’ai fini par paufiner mon petit script sh et ce grâce entre autre à votre aide (Cf mon post précédent.
Je vous explique ce que je souhaite faire et ensuite je vous explique ou je sèche.
Le but de mon script est de récupérer mon ip wan sur ma box pour mettre à jour mes définitions de zone sur mon serveur dns sous bind. Jusqu’ici ça marche. Je récupère mon ip wan sur ma livebooze :wink: , je la compare avec la dernière récupérée et si elle a changé, le lance mes opérations de modification de mon fichier de zone.
Donc techniquement la redirection de mon ndd vers la bonne ip que m’a attribué mon FAI est ok de mon côté sur mon serveur ns1.mondomaine.com.
Du côté de gandi, j’ai créé un glue record renvoyant sur mon ns1 perso, ça aussi ça marche très bien tant que mon ip renseignée en glue est correcte. A chaque changement j’ai juste à remettre la bonne ip et ça roule.
Gandi fournit une API pour pouvoir avoir accès à tout un tas de configuration facilement en python, perl, php, nodejs, c.
Et là c’est le drame, je ne suis pas vraiment développeur et j’ai bien du mal à trouver comment faire pour me servir de tout ça. Donc je fais appel à vous à la rescousse pour m’aider à trouver comment faire.
Je ne vous cache pas que si on me postait le code tout fait, j’en serais ravi. Mais bon, comme j’aime bien comprendre ce que je fais aussi, ça m’intéresserait de le faire moi même. Mais je suis lucide, je n’ai aucune connaissance dans les languages proposés et j’aimerais aussi un truc qui s’interface bien avec mon script sh (donc exit le php, seul langage dans lequel j’ai quelques connaissances).
Une âme charitable pourrait-elle me tenir la main sur ce projet? Je n’ai aucun décveloppeur dans mon entourage :frowning:
merci à vous

Edit: Voici mon script pour info et une meilleure compréhension.

#!/bin/bash

# Variables de la box:
url="192.168.1.1"
login="admin"
pwd="XXXXXXX"

# Variables locales
datenum=$(date +%Y%m%d)
dbnddfile="db.example.com"

# On stocke l'IP courante comme IP_old
mv ip.box ip.box.old

# Récupération de l'adresse IP et stockage dans ip.box
curl -s -X POST -o ip.wanbox "http://$url/sysbus/NMC:getWANStatus?username=$login&password=$pwd"
sed -e 's/.*"IPAddress":"\(.*\)","Remo.*/\1/' ip.wanbox ip.box

# On crée les deux variables correspondant a l'ancienne et la nouvelle IP
read ip_old < ip.box.old
read ip_new < ip.box

# Début du script basé sur la comparaison entre l'ancienne et la nouvelle IP
if [ "$ip_old" = "$ip_new" ]
then
# Si l'IP est la même on ne fait rien et on supprime ip.box.old
#echo "l'ip n'a pas changée elle est: $ip_old"
rm ip.box.old
else
# Sinon on continue le script pour mettre à jour l'IP et on supprime ip.box.old
#echo "l'ip a changée elle est désormais: $ip_new"
# On garde une trace de l'ancienne ip dans ip.box.stock
echo $ip_old >ip.box.stock

# Met à jour l'IP dans toutes les occurences du fichier
sed 's/'$ip_old'/'$ip_new'/g' $dbnddfile db.tmp

# Change le numéro de version du fichier
sed -r -e "s/.*[0-9]{8}.*; Serial/                      "$datenum"01    ; Serial/" db.tmp db.tmp2
rm $dbnddfile db.tmp ip.box.old
mv db.tmp2 $dbnddfile
fi

Bonjour arthanyx

Je me suis permis de modifier l’apparence du contenu de ton script
en le faisant précéder et suivre d’une ligne ne contenant que 3 bacticks


Voir : 
https://www.debian-fr.org/t/tuto-en-ligne-presentation-de-ses-messages-sur-ce-forum/73065

Merci MicP,
Désolé, j’étais passé à côté de ce tuto…
C’est bien mieux comme ça, je note les backticks :wink:

Voici de quoi t’aider :

https://gerard.geekandfree.org/blog/2012/03/01/debarrassez-vous-de-dyndns-en-utilisant-lapi-de-gandi/

Personnellement, j’ai plutôt pris l’adresse :

 url_page = 'http://4.ifconfig.pro'

Merci Arnaud de t’intéresser à mon problème et surtout d’essayer de m’aider.
J’ai déjà vu ce blog et cette soluce, reprise ensuite par charly Caulet. Le hic c’est que ce script est taillé pour modifiler un fichier de zone sur gandi. Or, ça ne me concerne pas, vu que je ne me sert pas du fichier de zone étant donné que je passe par les glue-records et un serveur dns perso (bah on s’auto-héberge jusqu’au bout hein :slight_smile: )
Je veux bien essayer de bidouiller le script de Gérard avec les commandes liées aux glues mais franchement je suis une bonne quiche en python même si globalement j’arrive à lire le code et vaguement le comprendre.
J’ai vu sur la doc de l’API gandi qu’il y avait une méthode domain.host.update(apikey, fqdn, ips) qui permet de mettre à jour l’ip du “glue-record”. Seulement comme dit ci-dessus je ne me sens pas vraiment capable de faire ça.

Bon, je peux essayer de t’aider, mais sans garantie. Il faut que tu connaisses ton API-Key chez Gandi pour pouvoir utiliser le script, c’est une protection empêchant n’importe qui de modifier tes données.

On va d’abord essayer dans une console Python pour voir si cela fonctionne ( il suffit de taper python dans une console pour cela ) :

~ python
>>> import xmlrpclib
>>> api = xmlrpclib.ServerProxy('https://rpc.gandi.net/xmlrpc/')
>>> apikey = 'ICI_IL_FAUT_METTRE_L_APIKEY'
>>> fqdn = 'TON_DOMAINE_NS'
>>> api.domain.host.list(apikey, fqdn)
>>> api.domain.host.update(apikey, fqdn, ['2.2.2.2'])

L’avant-dernière commande ( api.domain.host.list ) devrait afficher ton glue records déjà existant.
La dernière commande modifie ton glue records pour y mettre l’ip 2.2.2.2. Si ça marche, tu pourras corriger à la main ce test rapide.

Pour sortir de la console Python : quit() ou Ctrl + d.

1 J'aime

Ha yes, c’est en bonne voie :slight_smile:
Alors tout se passe bien, je récupère bien après le “list” mon adresse ip associée à mon ns1 dans le glue mais par contre l’update ne passe pas et me dit que le ndd n’est pas valable

xmlrpclib.Fault: <Fault 510642: "Error on object : OBJECT_HOST (CAUSE_NOTFOUND) [Host 'example.com' doesn't exist]">

bien entendu “example.com” a été remplacé par mon véritable ndd (non divulgué pour des raisons de sécurité car pas encore en prod et sujet à des risques.
Ce qui est assez bizarre vu qu’il y arrive bien quand il s’agit de lister… :-s
Je me suis demandé si ce n’était pas lié au fait que le nom du serveur donné en glue est ns1.example.com, j’ai donc changé le fqdn pour ns1… pour tester et ce n’est pas mieux

edit: ma version de python est 2.7.9 (si ça peut servir)

Utilise le ndd affiché dans la liste pour l’update ( paramètre ‘name’ ).

dans ma liste de glue, en paramètre nom de serveur j’ai ns1.example.com et ensuite l’ip qui lui est rattachée
j’ai fais le test avec ns1 seul, avec ns1.example.com et avec example.com au cas ou mais j’ai tjr un message d’erreur
ns1.example.com:

xmlrpclib.Fault: <Fault 500037: 'Error on object : OBJECT_UNKNOWN (CAUSE_BADPARAMETER) [invalid method parameter(s)]'>

ns1:

xmlrpclib.Fault: <Fault 505237: "Error on object : OBJECT_FQDN (CAUSE_BADPARAMETER) [string 'ns1' does not match '^((?!-)[-a-z0-9]{1,63}(?<!-)\\.)+(?=.{1,63}$)(?!-)([-a-z0-9]*[a-z][-a-z0-9]*\\.?)(?<!-)$']">

example.com:

xmlrpclib.Fault: <Fault 510642: "Error on object : OBJECT_HOST (CAUSE_NOTFOUND) [Host 'example.com' doesn't exist]">

Si je fais un list avec example.com comme fqdn:

>>> api.domain.host.list(apikey, fqdn)
[{'ips': ['92.167.XXX.XXX'], 'name': 'ns1.example.com'}]

si je fais un list avec ns1.example.com comme fqdn:

xmlrpclib.Fault: <Fault 510042: "Error on object : OBJECT_DOMAIN (CAUSE_NOTFOUND) [Domain 'ns1.example.com' doesn't exist.]">

Alors là… incompréhensible

Oui, pardon, c’est de ma faute :

>>> api.domain.host.update(apikey, fqdn, ['2.2.2.2'])

Il faut une liste ( donc des crochets ).

1 J'aime

YES !!!
:+1:
C’est excellent, tout à fait ce qu’il fallait. Il ne me reste plus qu’a mettre ça dans un fichier .py je suppose et de le lancer avec mon script. Ensuite une entrée dans le crontab pour exécuter ça cycliquement et le tour est joué.
Un grand merci à toi Arnaud, je te citerais dans mon script si je le distribue :wink:

Afin de compléter le post je vous colle ici le contenu de mon script python qui doit être appelé par le script sh

#!/usr/bin/python
# coding: utf-8

# On lit le fichier contenant l'ip courante à utiliser
fichier = open("ip.box", "r")
ip_box = fichier.readline()
fichier.close()

# Affichage de l'ip pour test (à commenter en prod)
print 'adresse ip de la box: ' + ip_box

# On lance le script de mise à jour du Glue-record via l'API
# Merci à Arnaud de debian-fr.org

# Connexion à l'API
import xmlrpclib
api = xmlrpclib.ServerProxy('https://rpc.gandi.net/xmlrpc/')

# Variables perso à modifier
apikey = 'XXXXXXXXXXXXXXXXXXXXX'
fqdn = 'ns1.example.com'
domaine = 'example.com'

# Affichage de la liste des Glue-Records pour test (à commenter en prod)
print api.domain.host.list(apikey, domaine)

# Mise à jour du Glue-Record désigné par fqdn avec l'ip récupérée (à commenter en test)
#api.domain.host.update(apikey, fqdn, [ip_box])

@Arnaud Si tu peux éditer ton post pour corriger la coquille sur l’ip pour que la réponse soit correcte ce serait super. Et un grand merci encore pour ton aide :wink:

Tu es sûr que ce script fonctionne ?
Si oui, c’est que l’API de Gandi fait une conversion implicite de chaîne de caractères en liste, car ta variable ip devrait être une liste et pas une chaîne.
D’ailleurs la variable ip_box devrait aussi déjà être une chaîne, ajouter des apostrophes ne sert à rien à mon avis.

Argh j’avoue ne pas avoir testé le script final, :blush: et m’être posé plusieurs fois la question s’il fallait ou non mettre les apostrophes. Je voulais rester fidèle au modèle que tu m’as fournis et que j’ai utilisé pour mon test.
Je suis en train de checker le temps que met gandi à mettre à jour les serveurs concernant le glue. J’avoue qu’à la base j’étais persuadé que la mise à jour de l’ip du glue était quasiment instantannée. Là je fais des tests depuis tout à l’heure sur le domaine via mon téléphone et ça répond toujours alors que le glue a 2.2.2.2 en ip (vérifié sur l’interface gandi). Soit je ne suis pas encore assez affranchi sur le fonctionnement des dns soit il y a des temps de propagation que je ne maitrise pas. Pour ça j’ai demandé conseil aux concernés (ie gandi) afin de savoir les temps mis pour la mise en place.
Pour le coup ça fout un peu en l’air mon idée de pouvoir pointer sur ma machine même avec une ip dynamique :frowning:

PS: J’en ai un peu chié vu que je n’avais jamais rien codé en python avant ça. Du coup j’ai appris quelques bricoles utiles :slight_smile:

Edit: J’ai pigé et modifié en conséquence le script py. En fait je cherchais compliqué alors que c’était simple…
@Arnaud : ce coup-ci c’est testé, ça fonctionne :wink:

Le glue record doit être modifié dans la zone parente, gérée par les serveurs DNS du registre qui gère le domaine parent, via une requête du registrar (Gandi) au registre. Cela ne peut pas être instantané, et ce n’est pas le genre de modification qu’on est censé faire souvent, donc il y a des chances qu’il ait un TTL élevé, c’est-à-dire que l’ancienne adresse IP reste présente dans le cache des serveurs récursifs pendant longtemps.

A mon avis (et pas que le mien), mettre un NS autoritaire sur une adresse IP variable, c’est une très mauvaise idée.

Bonjour Pascal,
Je me doutais quelque part que c’était pas vraiment une super idée sans trop vouloir l’admettre… Cela dit je ne suis pas vraiment en configuration de production pour rendre un service impeccable avec mes serveurs. Je m’essaye par la pratique à tout un tas de choses dont la gestion d’un serveur DNS. J’avais cru un moment (n’ayant pas trouvé d’info là dessus) que le glue était assez réactif et que ça m’aurait permis de contourner les systèmes à base de dyndns. La solution qui semblait la plus correcte était d’utiliser les adresses ipv6 pour mon serveur DNS puisqu’ayant été équipé par mon FAI. Mais têtu comme je suis, j’ai quand même voulu pousser du côté de l’update du glue. Je comprend mieux maintenant pourquoi je n’ai pas trouvé d’informations sur cette “pseudo méthode”.
Par contre pour le coup ça a été assez formateur pour moi sur le bash et un peu le python que je ne connaissais pas il y a encore peu. J’ai mis un pied dans gnu/linux il y a à peine six mois sur une vm sous windows et là j’en suis à me poser la question de faire l’inverse. Que de chemin parcouru et il m’en reste encore à parcourir.
Mon ip dynamique changeant grosso modo une fois par semaine si j’ai une latence de quelques heures ça peut faire l’affaire pour moi, étant donné que toute l’infrastructure (web, dns, mail) ne sert qu’à moi. Par contre si ça se joue en jours, là ça sera plus problématique.
Dans tous les cas, merci pour l’éclairage avisé concernant les glue. J’attends le retour de gandi à qui j’ai demandé en gros quel était le TTL du glue. Je reviendrais ici vous en faire part pour rendre le sujet le plus complet possible. D’ailleurs j’espère que mon sujet ne se trouve pas hors de clous dans cette section pour le coup.

A condition que le préfixe IPv6 alloué par le FAI soit fixe. Sinon, le problème est le meme qu’en IPv4.

Et une zone servie par un serveur DNS autoritaire qui n’est accessible qu’en IPv6 n’est visible que par quelqu’un qui utilise un serveur DNS récursif qui fonctionne en IPv6. En l’état actuel du déploiement de l’IPv6, je ne m’y risquerais pas. Certes il y a un DNS autoritaire secondaire, qui est opéré par Gandi je suppose, mais là aussi il doit pouvoir fonctionner en IPv6 pour se synchroniser.

Tu n’as pas besoin de demander à Gandi. Tu peux le voir toi-même en faisant une requête à un des serveurs DNS autoritaires pour la zone parente.

Par exemple si ton domaine est xxx.yy.zz
dig ns yy.zz
pour récupérer la liste des NS de la zone parente
dig a ton-ns.xxx.yy.zz @ns.de.la.zone.parente
pour afficher le glue record de ton serveur DNS avec son TTL d’origine.

Hello, je ré-ouvre ce sujet car gandi à changé son mode de fonctionnement API et est passé à l’apirest. Je ne suis pas du tout à l’aise avec tout ça mais j’ai trouvé chez gandi, sur leur documentation (https://api.gandi.net/docs/domains/) à la section glue record PUT et dans examples, des scripts en bash (curl) et python qui fonctionnent et peut être adapté je pense à mon script.
Vu que mon script de base est en bash, ce serait plus simple il me semble. Le code shell est:

curl -X PUT https://api.gandi.net/v5/domain/domains/example.com/hosts/ns1 -H 'authorization: Apikey xxxxxxxxxxxxxxxx' -H 'content-type: application/json' -d '{"ips":["123.123.123.123"]}'

Mais je ne sais pas trop comment remplacer example.com, xxxxxxxxxxx, et l’ip par des variables dans la commande curl
Sinon le script python suivant:

# On lit le fichier contenant l'ip courante à utiliser
fichier = open("/etc/bind/ip.box", "r")
ip_box = fichier.readline()
fichier.close()

import requests

url = "https://api.gandi.net/v5/domain/domains/example.com/hosts/ns1"

payload = "{\"ips\":[\"123.123.123.123\"]}"
headers = {
    'authorization': "Apikey xxxxxxxxxxxxxxxxxxxx",
    'content-type': "application/json"
    }

response = requests.request("PUT", url, data=payload, headers=headers)

print(response.text)

mais c’est pareil je ne sais pas comment y insérer mes variables
Si quelqu’un peut me filer un coup de main…
J’aimerais aussi remplacer la partie example.com dans l’url par une variable et aussi le xxxxxxxxxx.
Merci à vous pour vos retours, je reviendrai ensuite poster la toute dernière mouture de mon script qui est complètement automatique et multidomaines.

Rebonjour à tous,
J’ai trouvé comment régler mon problème tout seul, non sans mal mais ça marche…
Voici pour ceux que ça peut intéresser et qui sont dans une configuration type livebox orange et ndds chez gandi, mon script de mise à jour.
ip_update_db.sh.txt (6,3 Ko)

#!/bin/bash
# Variables de la box:

# Postulat de départ:
# Les fichiers de zone sont stockés dans le dossier 00_zones_local
# leur forme est du type: db.example.com
# la syntaxe à l'intérieur est:
#; BIND data file for example.com
#;
#$TTL    300
#$ORIGIN example.com.
#@               IN      SOA     ns1.example.com. hostmaster.example.com. (
#                        2019010101      ; Serial YYYYMMDDNN
#                        1200            ; Refresh
#                        600             ; Retry
#                        2419200         ; Expire
#                        3600)           ; Negative Cache TTL
#; Cles DNSSEC
#$INCLUDE "/etc/bind/00_zones_local/example.com/Kexample.com.zsk.key";
#$INCLUDE "/etc/bind/00_zones_local/example.com/Kexample.com.ksk.key";
#
#; Serveurs de Nom: NS
#@               IN      NS      ns1.example.com.
#                IN      NS      ns6.gandi.net.
#@               IN      A       123.123.123.123
#ns6.gandi.net.  IN      A       217.70.177.40
#
#; Serveur de Mail: MX
#@               IN      MX      10 mail
#@				 IN		 MX		 20 mail2
#
#; Correspondances nom-IP
#ns1             IN      A       123.123.123.123
#ns1             IN      AAAA    123a:123a:123a:123a:123a:123a:123a:123a
#mail            IN      A       123.123.123.123
#mail            IN      AAAA    123a:123a:123a:123a:123a:123a:123a:123a
#www             IN      A       123.123.123.123
#www             IN      AAAA    123a:123a:123a:123a:123a:123a:123a:123a
#
#; Enregistrements SPF
#@               IN      TXT     "v=spf1 ip4:123.123.123.123 -all"
#mail            IN      TXT     "v=spf1 mx -all"
#mail            IN      TXT     "v=spf1 a ~all"
#
# Les fichiers temporaires sont créés et stockés dans le dossier racine de bind: /etc/bind/
# Les zones signées, le dsset et les clés sont stockés dans un sous dossier de 00_zones_local au nom du domaine soit /etc/bind/00_zones_local/example.com/
# Le script de mise à jour des glue-record est 

#Variables de connection à la livebox
url="192.168.1.1"
login="admin"
pwd="ton_mot_de_passe"

# Variables locales
datenum=$(date +%Y%m%d)
datenumheure=$(date +Y%m%d%H%M)
datenumlong=$(date +%Y%m%d%H%M%S)
datednssec=$(expr $datenumlong + 10000000000)
# On part du principe que les fichiers correspondant aux zones gérées en local sont dans le dossier 00_zones_local
urlzones="/etc/bind/00_zones_local/"
urlbase="/etc/bind/"

cd $urlbase

# On stocke l'IP courante comme IP_old
mv ip.box ip.box.old

# Récupération de l'adresse IP sur la livebox et stockage dans ip.box
curl -s -X POST "http://$url/sysbus/NMC:getWANStatus?username=$login&password=$pwd" | sed -e 's/.*"IPAddress":"\(.*\)","Remo.*/\1/' > ip.box

# On crée les deux variables correspondant a l'ancienne et la nouvelle IP
read ip_old < ip.box.old
read ip_new < ip.box

# Début du script basé sur la comparaison entre l'ancienne et la nouvelle IP
if [ "$ip_old" = "$ip_new" ]

      then

              # Si l'IP est la même on ne fait rien, on supprime ip.box.old et on envoie un mail pour dire que rien n'a changé
              rm ip.box.old
              echo "Le script de contrôle d'IP WAN de la box s'est lancé et n'a détecté aucun changements. L'adresse ip est toujours: $ip_new" | mail -s "Aucun changement d'IP sur $HOSTNAME" ton_mail@example.com

else

      # Sinon on continue le script pour mettre à jour l'IP et on supprime ip.box.old
      # On garde une trace de l'ancienne ip dans ip.box.stock
      echo $ip_old $datenum >> $urlbase/ip.box.stock

  	# Pour chacune des occurence de fichier commençant par db. on effectue les modifications
      for i in $urlzones/db.*
      do
              # On extrait le nom de domaine du nom de fichier qui est $fichier tout le tps de la boucle
              fichier=`basename $i`
  			# On récupère le nom de domaine d'après le nom du fichier qui est de la forme db.example.tld sans le "db."
              ndd=${fichier:3}

              # On garde une copie du fichier de zone avec la date (ça peut servir)
              cp $urlzones$fichier $urlzones$ndd/zonebackup/$fichier.$datenum

              # Met à jour l'IP dans toutes les occurences du fichier et stocke en temporaire
              sed "s/"$ip_old"/"$ip_new"/g" $urlzones$fichier > $urlbase"db.tmp"

              # Change le numéro de version du fichier
              # On part du principe qu'il n'y a pas 2 changements d'ip par jour
              # mais comme ce n'est pas un bon principe il faut faire un script pour incrémenter le numéro
              # s'il y a deux changements le même jour
  			# A faire: Un test si le changement arrive une nouvelle fois le même jour et incrément si c'est le cas
  			# on peut se servir de la date enregistrée avec la dernière sauvegarde du fichier dans zonebackup
              sed -r -e "s/.*[0-9]{8}.*; Serial/                      "$datenum"01    ; Serial/" $urlbase"db.tmp" > $urlbase"db.tmp2"

  			# On supprime les fichiers devenus inutiles
              rm $urlzones$fichier $urlbase/db.tmp $urlbase/ip.box.old

  			# On remplace le fichier de zone par la version modifiée
              mv $urlbase"db.tmp2" $urlzones$fichier

              # Resigner la zone pour DNSSEC
              chemin=$urlzones$ndd/
              cd $chemin
  			# Ici on part du principe que les clés sont de la forme Kexample.com.ksk.key et Kexample.com.zsk.key et adapté en fonction du domaine à gérer
  			 dnssec-signzone -e $datednssec -f $chemin$fichier.signed -g -k $chemin/K$ndd.ksk.key -o $ndd $urlzones$fichier $chemin/K$ndd.zsk.key
  			 
              # Met à jour le glue-record chez gandi
              curl -X PUT https://api.gandi.net/v5/domain/domains/$ndd/hosts/ns1 -H 'authorization: Apikey XXXXXXXXXXXXXXXXXXXXX' -H 'content-type: application/json' -d '{"ips":["'$ip_new'"]}'

              #Envoie un mail pour prévenir de la modification
              echo "Le script de contrôle d'IP WAN de la box s'est lancé et l'adresse IP WAN de la box a changée, la nouvelle adresse est: $ip_new . Les changements sur $HOSTNAME $ndd ont été correctement effectués ainsi que les modifications dans les glue-record de gandi" | mail -s "Changement d'IP sur $HOSTNAME $ndd" ton_mail@example.com

      done
fi
# On vérifie une dernière fois la bonne supression de ip.box.old
if [ -f "/etc/bind/ip.box.old" ];then
rm $urlbase/ip.box.old;
fi