Nombre de host DNS dinámico con Cloudflare

Secuencia de comandos de Python para actualizar automáticamente un registro de host DNS de Cloudflare (A) con la nueva dirección IP del servidor.

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

Si usa Cloudflare como proveedor de DNS, puede usar una secuencia de comandos de Python para actualizar el registro de host DNS de una VM cada vez que una VM aparece con una nueva dirección IP. El script debe ejecutarse en el propio servidor porque realizará una llamada a https://api.ipify.org/ para obtener la dirección IP externa del host.

Obtenga un token de la API de Cloudflare con la autorización adecuada

Antes de poder ejecutar este script, debe obtener algunos detalles de Cloudflare. En su cuenta, en la sección "Descripción general" de su dominio, en "API", debería poder encontrar su ID de zona, copiar esa información y reemplazar <zone_id> con ella en el script.

Luego, haga clic en "Obtenga su token de API".

En esa página, en "Tokens API", haga clic en "Crear token" y use la plantilla "Editar zona DNS".

Necesitarás los siguientes permisos:

  • Recursos: Zona, Permisos: Zona, Lectura
  • Recursos: Zona, Permisos: DNS, Editar

En "Recursos de zona", seleccione "Incluir zonas específicas" y seleccione su nombre de dominio.

Luego, Cloudflare le proporcionará un token de API que puede usar, deberá reemplazar el valor de <cloudflare_token> en el script con ese token.

También debe reemplazar el valor de <your.dns.name> con el nombre de host DNS (registro A) que desea que tenga su host.

Obtener el ID del registro DNS

Luego deberá obtener el valor adecuado de <record_id> , que es el ID de Cloudflare de esa entrada de DNS en particular. Para hacerlo, deberá crear manualmente ese registro de host (A) en Cloudflare con cualquier dirección IP y luego consultar la ID de ese registro usando este 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)

Este script generará algo como esto (muy redactado). Tenga en cuenta el valor del campo "id" devuelto y reemplace "<record_id>" con el valor de ese campo en el siguiente script. Solo tendrá que hacer esto una vez, la identificación no cambiará mientras no elimine el registro.

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}}'

Actualice el registro del host DNS (A) con la IP actual

Este es el script que ahora puede usar para configurar la dirección IP para ese nombre DNS tantas veces como sea necesario. Tenga en cuenta que el script primero realiza una llamada a https://api.ipify.org/ para obtener la IP pública del host y luego actualiza el registro DNS con esa dirección IP. ¡DNS dinámico en pocas palabras!

#!/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 #######################################################

Tenga en cuenta que este script actualizará ciegamente el registro incluso si la dirección IP no ha cambiado. Puede hacerlo más eficiente si lo desea almacenando ese valor y actualizando el registro solo si ha cambiado.

Este script también escribe en un archivo de registro, para que pueda revisar los resultados si programa la tarea.

Comentarios publicados: 0

Tagged with:
DNS networking