Anteriormente intenté escribir una entrada usando Namecheap, su servicio DDNS y algunas herramientas. Todos los scripts funcionan perfecto excepto porque en tres días explorando opciones no había solucionado el problema de inicializar el pod desde systemd
. Por este motivo decidí cambiar, probar otras opciones. La siguiente idea en complejidad que se me ocurrió fue utilizar Cloudflare y su servicio de DDNS.
Hay algunos usuarios que lo han hecho funcionar utilizando la UniFi Dream Machine, pero antes de adaptarlo a mi capado USG, prefiero ejecutarlo en mi servidor bastión, hobbiton
.
Ingresamos al panel de control de Cloudflare y creamos una nueva zona escribiendo el dominio que vamos a usar en el asistente. Esto llevara a una pantalla de selección de los registros que queremos importar y al final nos mostrará unos servidores de nombre que debemos actualizar en Namecheap.
Ahora hay que ajustar el o los dominios que vamos a utilizar en Namecheap para que apunten a los servidores de nombre de Cloudflare del paso anterior.
Hechos estos cambios hay que esperar un rato a que ocurra la propagación de los servidores de nombre. El caso es que Cloudflare te avisa vía correo electrónico cuando esto ocurre. Recibí la notificación aproximadamente 90 minutos después de hacer los ajustes.
Como Cloudflare se trae los registros previos, Al entrar a la zona veremos los registros cargados.
Ahora necesitamos una clave API y los tokens para poder gestionar remotamente los cambios. Nos vamos al panel de tokens de API de Cloudflare. Creamos una nuevo token usando la plantilla: Editar zona DNS. Aquí podemos delimitar el alcance del token pudiendo seleccionar zonas específicas y restringiendo o permitiendo IPs de acceso.
Una vez elegidas las opciones te mostrará una pantalla de resumen y luego el Token que no se volverá a mostrar nuevamente, por lo que conviene tenerlo bien resguardado. También se mostraráeun comando curl
para verificar, similar a este:
1 2 3 | curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \ -H "Authorization: Bearer JerHaBiloRenXN7DlJF_KX4B8u9f69HutlXRj1d3" \ -H "Content-Type:application/json" |
Ahora toca volver nuevamente al panel de Tokens de API y copiar o crear una nueva Global API Key y en nuestra zona para el dominio que hemos incorporado, copiamos Id de la zona y Id de la cuenta.
Hasta este momento debemos tener cuatro claves alfanuméricas de cada uno de estos valores y son las que utilizaremos en el contenedor de podman más adelante.
En mi bastión he preparado un directorio dentro de mi /home
para usar con el contenedor a partir de una imagen que prepara timothymiller. Dentro hay un archivo llamado config.json
cuyo contenido es el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | { "cloudflare": [{ "authentication": { "api_token": "JerHaBiloRenXN7DlJF_KX4B8u9f69HutlXRj1d3", "api_key": { "api_key": "1024a46d930e0f9bd89234c48dcd7ae54c7ee", "account_email": "demo@ejemplo.com" } }, "zone_id": "5a7326f674c3a325beaf404b4e5623a0", "subdomains": [{ "name": "", "proxied": false }, { "name": "www", "proxied": false }, { "name": "cloud", "proxied": false } ] }], "a": true, "aaaa": false, "purgeUnknownRecords": false, "ttl": 300 } |
y otro llamado ddns_cloudflare.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 | #!/bin/bash CONTAINER=ddns-cloudflare if podman container exists "$CONTAINER"; then podman start "$CONTAINER" else podman run -i -d --rm \ --net=host \ --name "$CONTAINER" \ --security-opt=no-new-privileges \ -v /home/linkmoises/ddns-cloudflare/config.json:/config.json \ timothyjmiller/cloudflare-ddns:latest fi |
Y le damos permiso de ejecución chmod +x ddns-cloudflare.sh
. Una vez ejecutado, veremos en los logs que se actualizan los valores.
1 2 3 4 5 6 7 | 🕰️ Updating IPv4 (A) records every 300 seconds 📡 Updating record {'type': 'A', 'name': 'serrano.red', 'content': '201.227.63.233', 'proxied': False, 'ttl': 300} 📡 Updating record {'type': 'A', 'name': 'www.serrano.red', 'content': '201.227.63.233', 'proxied': False, 'ttl': 300} 📡 Updating record {'type': 'A', 'name': 'cloud.serrano.red', 'content': '201.227.63.233', 'proxied': False, 'ttl': 300} |
Al final, si ejecuto el pod funciona sin problemas, pero nuevamente al configurar la unidad en systemd para que el supervisor la mantenga funcionando falla. Por lo que opté por abandonar este método y volver a la vieja confiable, scripts de bash y cron
.
Después de tantos problemas con podman (empiezo a pensar que me odia…) decidí buscar una alternativa basada en un script bash que se ejecute por medio de cron
, así encontré este gist de Tras2.
Creamos un directorio en el /home/usuario
donde trabajaremos esto: ddns-cloudflare
, dentro he creado un archivo llamado ddns-registros.txt
donde mantendré los registros por línea según el siguiente formato zona,dominio
de manera que podría actualizar diferentes dominios y zonas simultáneamente. Cada entrada sin espacios y valores separados por coma y verificar que no hayan espacios al final del archivo.
1 2 3 | serrano.red,serrano.red serrano.red,www.serrano.red serrano.red,cloud.serrano.red |
Y añadimos un archivo ddns-cloudflare.sh
donde volcaremos el script de abajo. Recordar dar permisos de ejecución chmod +x ddns-cloudflare.sh
. El script en cuestión solo requiere el Token API de Cloudflare y el correo electrónico asociado. Este es el script bash:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | #!/bin/bash ################################################################################ ## ## ## Basado en https://gist.github.com/Tras2/cba88201b17d765ec065ccbedfb16d9a ## ## este script comprueba y actualiza cambios en la dirección IP que ofrece ## ## nuestro proveedor, de manera que a través de la API de Cloudflare se ## ## actualizan los registros A y AAAA cuando estan disponibles. ## ## ## ## Este script tiene como requisito la presencia de curl y jq instalados ## ## en el sistema para poder funcionar correctamente. ## ## ## ################################################################################ ## Datos iniciales a ser llenados por el usuario # API token; e.g. JerHaBiloRenXN7DlJF-KX4B8u9f69HutlXRj1d3 api_token=jJerHaBiloRenXN7DlJF-KX4B8u9f69HutlXRj1d3 # Dirección de email asociada a la cuenta de Cloudflare; e.g. email@gmail.com email=email@correo.com # Datos básicos IPv4, IPv6 y usuario ipv4=$(curl -4 -s -X GET https://checkip.amazonaws.com --max-time 10) ipv6=$(curl -s -X GET -6 https://ifconfig.co) user_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \ -H "Authorization: Bearer $api_token" \ -H "Content-Type:application/json" \ | jq -r '{"result"}[] | .id' ) # Anuncia datos de IPv4 e IPv6 if [ $ipv4 ]; then echo -e "\033[0;32m [+] Tú dirección IPv4 es: $ipv4"; else echo -e "\033[0;33m [!] No se puede obtener una dirección IPv4."; fi if [ $ipv6 ]; then echo -e "\033[0;32m [+] Tu dirección IPv6 es: $ipv6"; else echo -e "\033[0;33m [!] No se puede obtener una dirección IPv6."; fi # Lee el archivo de registros y zonas ddns-registros.txt while IFS=, read -r zone_name dns_record; do # verifica si el usuario de la API y el email son correctos if [ $user_id ] then zone_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$zone_name&status=active" \ -H "Content-Type: application/json" \ -H "X-Auth-Email: $email" \ -H "Authorization: Bearer $api_token" \ | jq -r '{"result"}[] | .[0] | .id' ) # verifica si en ID de la zona está disponible if [ $zone_id ] then # verifica si hay una versión IPv4 if [ $ipv4 ] then dns_record_a_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?type=A&name=$dns_record" \ -H "Content-Type: application/json" \ -H "X-Auth-Email: $email" \ -H "Authorization: Bearer $api_token" ) # si el registro IPv4 existe dns_record_a_ip=$(echo $dns_record_a_id | jq -r '{"result"}[] | .[0] | .content') if [ $dns_record_a_ip != $ipv4 ] then # cambia el registro A curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$(echo $dns_record_a_id | jq -r '{"result"}[] | .[0] | .id')" \ -H "Content-Type: application/json" \ -H "X-Auth-Email: $email" \ -H "Authorization: Bearer $api_token" \ --data "{"type":"A","name":"$dns_record","content":"$ipv4","ttl":300,"proxied":false}" \ | jq -r '.errors' # escribe el resultado echo -e "\033[0;32m [+] El registro A de $dns_record ha sido actualizado: $dns_record_a_ip -> $ipv4" else echo -e "\033[0;37m [~] El registro A de $dns_record es el mismo que la IPv4 actual." fi fi # verifica si hay una versión IPv6 if [ $ipv6 ] then dns_record_aaaa_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?type=AAAA&name=$dns_record" \ -H "Content-Type: application/json" \ -H "X-Auth-Email: $email" \ -H "Authorization: Bearer $api_token" ) # si el registro IPv6 existe dns_record_aaaa_ip=$(echo $dns_record_aaaa_id | jq -r '{"result"}[] | .[0] | .content') if [ $dns_record_aaaa_ip != $ipv6 ] then # cambia el registro AAAA curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$(echo $dns_record_aaaa_id | jq -r '{"result"}[] | .[0] | .id')" \ -H "Content-Type: application/json" \ -H "X-Auth-Email: $email" \ -H "Authorization: Bearer $api_token" \ --data "{"type":"AAAA","name":"$dns_record","content":"$ipv6","ttl":300,"proxied":false}" \ | jq -r '.errors' # escribe el resultado echo -e "\033[0;32m [+] El registro AAAA de $dns_record ha sido actualizado: $dns_record_aaaa_ip -> $ipv6" else echo -e "\033[0;37m [~] El registro AAAA de $dns_record es el mismo que la IPv6 actual." fi fi else echo -e "\033[0;31m [-] Hay un problema para obtener el ID de la zona (subdominio) o el correo electrónico (username). Verificar estos datos e intentar nuevamente." fi else echo -e "\033[0;31m [-] Hay un problema con el token API. Verificar este dato e intentar nuevamente." fi done < /home/linkmoises/ddns-cloudflare/ddns-registros.txt |
He traducido y personalizado los mensajes dentro del script, así como los comentarios. La primera vez que se ejecuta y actualiza todos los registros obtendremos una salida como esta:
1 2 3 4 5 | [+] Tú dirección IPv4 es: 201.227.63.233 [!] No se puede obtener una dirección IPv6. [+] El registro A de serrano.red ha sido actualizado: 127.0.0.1 -> 201.227.63.233 [+] El registro A de www.serrano.red ha sido actualizado: 127.0.0.1 -> 201.227.63.233 [+] El registro A de cloud.serrano.red ha sido actualizado: 127.0.0.1 -> 201.227.63.233 |
Una vez estén hechos los cambios y que no haya nada que actualizar la salida será esta:
1 2 3 4 5 | [+] Tú dirección IPv4 es: 201.224.2.121 [!] No se puede obtener una dirección IPv6. [~] El registro A de serrano.red es el mismo que la IPv4 actual. [~] El registro A de www.serrano.red es el mismo que la IPv4 actual. [~] El registro A de cloud.serrano.red es el mismo que la IPv4 actual. |
Con esto comprobamos que el script funciona adecuadamente. Ahora es momento de configurar la tarea con ayuda de cron
. Abrimos crontab -e
y creamos la tarea:
1 | */5 * * * * /home/usuario/ddns-cloudflare/ddns-cloudflare.sh > /dev/null 2>&1 |
De este modo, el script se ejecuta cada 5 minutos buscando cambios en la IP y si los encuentra actualiza los registros. Posteriormente añadí un bot de Telegram que vía webhook me notifica cada vez que hay un cambio.
@linkmoises
Aún no hay comentarios...