Nom d'hôte DNS dynamique avec Cloudflare

Script Python pour mettre à jour automatiquement un enregistrement Cloudflare DNS Host (A) avec la nouvelle adresse IP du serveur.

C05348A3-9AB8-42C9-A6E0-81DB3AC59FEB
           

Si vous utilisez Cloudflare en tant que fournisseur DNS, vous pouvez utiliser un script Python pour mettre à jour l'enregistrement d'hôte DNS d'une VM chaque fois qu'une VM propose une nouvelle adresse IP. Le script doit être exécuté sur le serveur lui-même car il appellera https://api.ipify.org/ pour obtenir l'adresse IP externe de l'hôte.

Obtenir un jeton d'API Cloudflare avec l'autorisation appropriée

Avant de pouvoir exécuter ce script, vous devez obtenir quelques détails auprès de Cloudflare. Dans votre compte, sous la section "Aperçu" de votre domaine, sous "API", vous devriez pouvoir trouver votre ID de zone, copier cette information et remplacer <zone_id> par celle-ci dans le script.

Ensuite, cliquez sur "Obtenir votre jeton API".

Sur cette page, sous "API Tokens", cliquez sur "Create Token" et utilisez le modèle "Edit zone DNS".

Vous aurez besoin des autorisations suivantes :

  • Ressources : Zone, Autorisations : Zone, Lecture
  • Ressources : Zone, Autorisations : DNS, Modifier

Sous "Ressources de zone", sélectionnez "Inclure des zones spécifiques" et sélectionnez votre nom de domaine.

Cloudflare vous fournira ensuite un jeton API que vous pourrez utiliser, vous devrez remplacer la valeur de <cloudflare_token> dans le script par ce jeton.

Vous devez également remplacer la valeur de <your.dns.name> par le nom d'hôte DNS (enregistrement A) que vous souhaitez que votre hôte ait.

Obtenir l'ID de l'enregistrement DNS

Vous devrez ensuite obtenir la valeur appropriée de <record_id> , qui est l'ID Cloudflare de cette entrée DNS particulière. Pour ce faire, vous devrez créer manuellement cet enregistrement d'hôte (A) dans Cloudflare avec n'importe quelle adresse IP, puis interroger l'ID de cet enregistrement à l'aide de ce script :

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
 
import requests
 
token = '<cloudflare_token>'
DNSname = '<your.dns.name>'
zone_id = '<zone_id>'
 
url = f'https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records?type=A&name={DNSname}&page=1&per_page=20&order=type&direction=desc&match=all'
 
headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Authorization': f'Bearer {token}'
}
 
try:
    r = requests.request(method='GET', url=url, headers=headers, timeout=25)
except Exception as x:
    print('Connection failed :( %s' % x.__class__.__name__)
else:
    print(r.status_code)
    if r.status_code == 200:
        response = r.content
        print(response)
 
    else:
        print('Error: %s' % r.status_code)
        print(r.headers)
        print(r.text)

Ce script affichera quelque chose comme ceci (fortement expurgé). Notez la valeur du champ "id" renvoyé et remplacez "<record_id>" par la valeur de ce champ dans le script ci-dessous. Vous n'aurez à le faire qu'une seule fois, l'ID ne changera pas tant que vous ne supprimerez pas l'enregistrement.

python3 get_record_id.py 
200
b'{"result":[{"id":"<record_id>","zone_id":"<zone_id>","zone_name":"dns.name","name":"your.dns.name","type":"A","content":"172.0.0.1","proxiable":true,"proxied":false,"ttl":120,"locked":false,"meta":{"auto_added":false,"managed_by_apps":false,"managed_by_argo_tunnel":false,"source":"primary"},"created_on":"2021-07-31T21:13:57.939216Z","modified_on":"2022-03-07T00:37:04.153213Z"}],"success":true,"errors":[],"messages":[],"result_info":{"page":1,"per_page":20,"count":1,"total_count":1,"total_pages":1}}'

Mettre à jour l'enregistrement de l'hôte DNS (A) avec l'adresse IP actuelle

C'est le script que vous pouvez maintenant utiliser pour définir l'adresse IP de ce nom DNS autant de fois que nécessaire. Notez que le script appelle d'abord https://api.ipify.org/ pour obtenir l'adresse IP publique de l'hôte, puis met à jour l'enregistrement DNS avec cette adresse IP. Le DNS dynamique en un mot !

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
 
""""
Update the value of an existing A record in Cloudflare based on the host's current public IP address.
"""
 
 
# I M P O R T S ###############################################################
 
import urllib
import json
import sys
import os
import time
from logging.handlers import RotatingFileHandler
import traceback
import datetime
import requests
import logging
 
__version__ = "1.0.1"
 
 
# G L O B A L S ###############################################################
 
zone_id = '<zone_id>'
token = '<cloudflare_token>'
DNSname = '<your.dns.name>'
record_id = '<record_id>'
 
logger = logging.getLogger()
logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
ch.setFormatter(formatter)
logger.addHandler(ch)
try:
    logFile = os.path.realpath(__file__).split('.')[-2] + ".log"
    fh = RotatingFileHandler(logFile, maxBytes=(1048576 * 50), backupCount=7)
    fh.setLevel(logging.DEBUG)
    fh.setFormatter(formatter)
    logger.addHandler(fh)
    logger.info("Log file:    %s" % logFile)
except Exception as e:
    logger.warning("Unable to log to file: %s - %s" % (logFile, e))
logger.info("-" * 80)
logger.info("Version:  %s" % (__version__))
 
 
# F U N C T I O N S ###########################################################
 
 
def main():
 
    url = f'https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/{record_id}'
 
    t0 = time.time()
    try:
        r = requests.request(method='GET', url='https://api.ipify.org/', timeout=25)
    except Exception as x:
        logger.error('Connection failed :( %s' % x.__class__.__name__)
    else:
        if r.status_code == 200:
            response = r.content
            newIP = str(response, 'utf-8').strip()
            logger.info(newIP)
        else:
            logger.error('Error: %s' % r.status_code)
            logger.debug(r.headers)
            logger.debug(r.text)
            logger.debug(sys.exc_info()[:2])
    finally:
        t1 = time.time()
        logger.info('Connection took %.4fs' % (t1 - t0))
 
 
    headers = {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        'Authorization': f'Bearer {token}'
    }
    data = {
        "type": "A",
        "name": DNSname,
        "content": newIP,
        "ttl": 120,
        "proxied": False
    }
 
    logger.info("Updating IP address")
    t0 = time.time()
    try:
        r = requests.request(method='PUT', url=url, data=json.dumps(data), headers=headers, timeout=25)
    except Exception as x:
        logger.error('Connection failed :( %s' % x.__class__.__name__)
    else:
        logger.info(r.status_code)
        if r.status_code == 200:
            response = r.content
            logger.info(response)
        else:
            logger.error('Error: %s' % r.status_code)
            logger.error(r.headers)
            logger.error(r.text)
            logger.error(sys.exc_info()[:2])
    finally:
        t1 = time.time()
        logger.info('Connection took %.4fs' % (t1 - t0))
 
    sys.exit(0)
 
 
if __name__ == "__main__":
    main()
 
# E N D   O F   F I L E #######################################################

Notez que ce script mettra à jour aveuglément l'enregistrement même si l'adresse IP n'a pas changé. Vous pouvez le rendre plus efficace si vous le souhaitez en stockant cette valeur et en mettant à jour l'enregistrement uniquement s'il a changé.

Ce script écrit également dans un fichier journal, afin que vous puissiez consulter les résultats si vous planifiez la tâche.

Commentaires publiés : 0

Tagged with:
DNS networking