Cloudflare DDNS: Actualizador DNS Dinámico en Rust

Publicado por


DDNS Dinámico

Cloudflare DDNS: Actualizador DNS Dinámico en Rust

Cliente DDNS ligero para Cloudflare con imagen Docker de 1.1 MB y consumo de 3.5 MB RAM

Vídeo tutorial en YouTube

Mira el vídeo completo sobre cómo instalar y usar Cloudflare DDNS: Actualizador DNS Dinámico en Rust en el canal GENBYTE:

¿Qué es Cloudflare DDNS: Actualizador DNS Dinámico en Rust?

Cloudflare DDNS es un cliente de DNS dinámico escrito en Rust que permite acceder a una red doméstica mediante un nombre de dominio personalizado sin necesidad de IP estática. El proyecto mantiene actualizados los registros A y AAAA en Cloudflare, y opcionalmente listas de IP del WAF, apuntando a la IP pública actual. Se distribuye como una imagen Docker estática construida desde scratch de aproximadamente 1.1 MB que utiliza unos 3.5 MB de RAM. La configuración se realiza mediante variables de entorno y soporta IPv4 e IPv6 con proveedores de detección independientes, múltiples dominios y zonas, comodines, proxy por dominio, notificaciones y heartbeats.

Imagen Docker de 1.1 MB compilada en Rust, sin dependencias externas, que actualiza registros DNS de Cloudflare cada 5 minutos por defecto con jitter aleatorio para evitar colisiones en la API.

Características principales

Detección IPv4 e IPv6 independiente

Soporta múltiples proveedores: Cloudflare trace (por defecto), Cloudflare DNS-over-HTTPS, ipify, tabla de enrutamiento local, interfaz de red, URL personalizada o IPs estáticas. Cada familia de direcciones puede usar un proveedor distinto.

Múltiples dominios y zonas

Gestiona muchos dominios en varias zonas de Cloudflare, incluyendo comodines (ej. *.example.com). Las zonas se descubren automáticamente a partir del nombre de dominio.

Seguridad ante fallos

Un fallo en la detección de IP nunca borra ni sobrescribe los registros existentes. Las IPs anycast de Cloudflare se rechazan automáticamente. Los rangos de Cloudflare se obtienen y cachean por 24 horas.

Listas WAF sincronizadas

Mantiene listas de IP del WAF de Cloudflare actualizadas con las direcciones detectadas. Requiere permiso Account › Account Filter Lists › Edit.

Proxy por dominio con expresiones

Controla qué dominios pasan por el proxy de Cloudflare mediante expresiones booleanas (is, sub, !, &&, ||). Ejemplo: !is(vpn.example.com) para excluir un subdominio.

Notificaciones y heartbeats

Envía notificaciones vía shoutrrr (Discord, Slack, Telegram, Gotify, Pushover, Zulip, webhooks genéricos) y heartbeats a Healthchecks.io y Uptime Kuma en cada ciclo, inicio y apagado.

Programación flexible

Cron simple (@every 5m, @once) con jitter 0-20%, ejecución inmediata al arranque (UPDATE_ON_START), limpieza opcional al parar (DELETE_ON_STOP) y modo dry-run.

Comentarios y regex en registros

Añade comentarios a los registros gestionados y usa regex (MANAGED_RECORDS_COMMENT_REGEX) para compartir una zona con otras herramientas sin conflictos.

Multi-homed y grupos de detección

En hosts con varias interfaces (dual-WAN, instancias cloud) permite grupos numerados (DOMAINS_2, IP4_PROVIDER_2, etc.) cada uno con sus propios dominios y proveedores vinculados a interfaces específicas.

Despliegue universal

Funciona en Docker (amd64, arm64, ppc64le), Kubernetes/Helm, systemd o cualquier plataforma que soporte Rust. Imagen multi-arquitectura publicada en Docker Hub y GHCR.

Requisitos del sistema

  • Docker Engine o Docker Compose
  • Token de API de Cloudflare con permisos: Zone › DNS › Edit, Zone › Zone › Read, y Account › Account Filter Lists › Edit si se usan listas WAF
  • Dominio(s) gestionados en Cloudflare
  • Conectividad a Internet para detección de IP pública y API de Cloudflare
  • Para IPv6 en Docker: network_mode: host (o –network host) porque la red bridge por defecto no tiene conectividad IPv6

Instalación con Docker Compose

Ejecutar con Docker (IPv4 únicamente)

Ejecución rápida sin red host. Se desactiva IPv6 con IP6_PROVIDER=none.

docker run -d \
–name cloudflare-ddns \
–restart unless-stopped \
-e CLOUDFLARE_API_TOKEN=tu-api-token \
-e DOMAINS=example.com,www.example.com \
-e IP6_PROVIDER=none \
timothyjmiller/cloudflare-ddns:2.3.0

Ejecutar con Docker (IPv4 + IPv6)

Requiere red host para que la detección IPv6 funcione correctamente.

docker run -d \
–name cloudflare-ddns \
–restart unless-stopped \
–network host \
-e CLOUDFLARE_API_TOKEN=tu-api-token \
-e DOMAINS=example.com,www.example.com \
timothyjmiller/cloudflare-ddns:2.3.0

Docker Compose (IPv4 únicamente)

Archivo docker-compose.yml listo para producción con seguridad básica y healthcheck opcional.

services:
cloudflare-ddns:
image: timothyjmiller/cloudflare-ddns:2.3.0
container_name: cloudflare-ddns
security_opt:
– no-new-privileges:true
environment:
– CLOUDFLARE_API_TOKEN=tu-api-token
– DOMAINS=example.com,www.example.com
– PROXIED=true
– IP6_PROVIDER=none
– HEALTHCHECKS=https://hc-ping.com/tu-uuid
restart: unless-stopped

Docker Compose (IPv4 + IPv6 con secretos)

Usa Docker secrets para no exponer el token en el archivo compose. Requiere network_mode: host.

services:
cloudflare-ddns:
image: timothyjmiller/cloudflare-ddns:2.3.0
container_name: cloudflare-ddns
security_opt:
– no-new-privileges:true
network_mode: host
environment:
– CLOUDFLARE_API_TOKEN_FILE=/run/secrets/cloudflare_api_token
– DOMAINS=example.com,www.example.com
secrets:
– cloudflare_api_token
restart: unless-stopped

secrets:
cloudflare_api_token:
file: ./cloudflare_api_token.txt

Kubernetes con Helm (recomendado)

Chart publicado como artefacto OCI en GHCR. Usa values.yaml para configuración.

helm install cloudflare-ddns oci://ghcr.io/timothymiller/cloudflare-ddns \
–namespace ddns –create-namespace \
-f values.yaml

# values.yaml ejemplo:
cloudflare:
apiToken: tu-api-token

domains: example.com,www.example.com
ip6Provider: cloudflare.trace
proxied: "true"

shoutrrr: |
discord://token@webhook-id
healthchecks: https://hc-ping.com/tu-uuid

systemd (binario nativo)

Compila desde fuente e instala unidades systemd con temporizador cada 15 minutos.

cargo build –release
sudo install -m 755 target/release/cloudflare-ddns /usr/local/bin/cloudflare-ddns

sudo install -d -m 700 /etc/cloudflare-ddns
sudo tee /etc/cloudflare-ddns/env >/dev/null <<'EOF'
CLOUDFLARE_API_TOKEN=tu-api-token
DOMAINS=example.com,www.example.com
EOF
sudo chmod 600 /etc/cloudflare-ddns/env

sudo cp systemd/cloudflare-ddns.service systemd/cloudflare-ddns.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable –now cloudflare-ddns.timer

Acceder al proyecto

Acceso remoto vía dominio

Una vez configurado, los dominios listados en DOMAINS, IP4_DOMAINS o IP6_DOMAINS resuelven a la IP pública actual del host. Permite acceder a servicios autohospedados (VPN, NAS, Home Assistant, etc.) usando nombres como home.example.com o vpn.example.com sin IP fija.

Verificación de funcionamiento

Revisa los logs del contenedor: docker logs cloudflare-ddns. Cada ciclo muestra IPs detectadas, registros actualizados/creados, estado de listas WAF y heartbeats. En modo QUIET=true solo se muestran avisos y errores.

Casos de uso

  • Acceso remoto a red doméstica sin IP estática (VPN, escritorio remoto, cámaras, NAS)
  • Servicios autohospedados accesibles por dominio propio (Home Assistant, Plex, Nextcloud, Gitea)
  • Actualización automática de registros DNS para instancias cloud con IP dinámica
  • Sincronización de listas de IP en WAF de Cloudflare para reglas de firewall basadas en IP de origen
  • Entornos multi-WAN o multi-interfaz donde cada proveedor de conectividad actualiza su propio conjunto de dominios
  • Despliegues en Raspberry Pi, servidores caseros, VPS o clusters Kubernetes ligeros

Configuración avanzada

Grupos de detección multi-interfaz

Define grupos numerados (sufijo _2, _3…) para hosts con varias interfaces de red. Cada grupo tiene sus propios dominios y proveedores vinculados a interfaces específicas.

DOMAINS=a.example.com # grupo por defecto
DOMAINS_2=b.example.com # grupo 2
IP4_PROVIDER_2=cloudflare.trace@ens5 # IPv4 detectado vía ens5
IP6_PROVIDER_2=local.iface:ens5 # IPv6 leído de interfaz ens5

Listas WAF de Cloudflare

Mantiene listas de IP (account-id/list-name) sincronizadas con todas las IPs detectadas (IPv4 e IPv6). La lista se crea si no existe. El nombre solo admite minúsculas, dígitos y guion bajo.

WAF_LISTS=abc123/mi-lista-waf,def456/otra-lista
WAF_LIST_DESCRIPTION=Gestionado por cloudflare-ddns
WAF_LIST_ITEM_COMMENT=ddns-auto
MANAGED_WAF_LIST_ITEMS_COMMENT_REGEX=^ddns-auto$

Notificaciones múltiples (shoutrrr)

Una URL por línea en variable SHOUTRRR. En Docker Compose usa bloque YAML. Soporta Discord, Slack, Telegram, Gotify, Pushover, Zulip y webhooks genéricos.

environment:
SHOUTRRR: |
discord://token@webhook-id
telegram://bot-token@telegram?chats=123456789
gotify://host/app-token
generic+https://webhook.example.com/ddns?messagekey=text

Heartbeats Healthchecks.io y Uptime Kuma

Envía ping de inicio, cada ciclo (éxito/fallo) y apagado. Para Uptime Kuma usa monitor tipo Push; la URL puede incluir parámetros que se eliminan automáticamente.

HEALTHCHECKS=https://hc-ping.com/tu-uuid
UPTIMEKUMA=https://kuma.example.com/api/push/tu-token

Comentarios y regex para zonas compartidas

Etiqueta los registros gestionados con un comentario fijo y usa regex para que la herramienta solo toque sus propios registros, permitiendo convivir con otras herramientas en la misma zona.

RECORD_COMMENT=managed by cloudflare-ddns
MANAGED_RECORDS_COMMENT_REGEX=^managed by cloudflare-ddns$

Expresiones de proxy (PROXIED)

Controla proxy por dominio con expresiones booleanas. Valores: true/false, is(dominio), sub(dominio), !, &&, ||. Ejemplo: proxy todo excepto vpn.

PROXIED=!is(vpn.example.com)

Modo dry-run y ejecución única

Bandera –dry-run para simular cambios sin escribir en Cloudflare. UPDATE_CRON=@once ejecuta un ciclo y sale con código 1 si falla, útil para cron/systemd.

docker run –rm timothyjmiller/cloudflare-ddns:2.3.0 –dry-run

# O en compose con variable:
UPDATE_CRON=@once
UPDATE_ON_START=true
DELETE_ON_STOP=false

Gestión y mantenimiento

Actualización de imagen

Las imágenes se etiquetan con versión completa (2.3.0), menor (2.3), mayor (2) y latest. En producción fija versión exacta (2.3.0) y actualiza deliberadamente tras leer CHANGELOG.md.

docker pull timothyjmiller/cloudflare-ddns:2.3.0
docker compose up -d

Rotación de token API

Crea nuevo token en Cloudflare con mismos permisos y zonas. Actualiza el secreto (Docker secret, Kubernetes secret o archivo env) y reinicia el contenedor/servicio.

docker compose up -d –force-recreate

Limpieza de registros al detener

Con DELETE_ON_STOP=true, al recibir SIGTERM/SIGINT borra los registros gestionados y limpia ítems WAF. Útil en entornos efímeros. Por defecto false.

DELETE_ON_STOP=true

Solución de problemas IPv6 en Docker

La red bridge por defecto no tiene IPv6. Usa network_mode: host o –network host. Si no necesitas IPv6, establece IP6_PROVIDER=none.

IP6_PROVIDER=none

Problemas con Cloudflare WARP/Zero Trust

Si el host usa WARP, la detección ve IPs de Cloudflare que son rechazadas. Excluye cloudflare.com y api.cloudflare.com del túnel WARP (split tunnel) o usa proveedor alternativo (ipify, url:, local.iface).

IP4_PROVIDER=ipify
IP6_PROVIDER=ipify

Monitoreo de salud

Configura HEALTHCHECKS o UPTIMEKUMA para alertas si el contenedor deja de enviar heartbeats. Intervalo del monitor mayor que UPDATE_CRON + 20% jitter (ej. 7 min para default 5 min).

HEALTHCHECKS=https://hc-ping.com/tu-uuid

Stack técnico

  • Rust (lenguaje principal)
  • Docker (imagen multi-arquitectura: amd64, arm64, ppc64le)
  • Docker Compose
  • Kubernetes / Helm (chart OCI en GHCR)
  • systemd (unidades nativas)
  • API de Cloudflare (DNS, Zone, Account Filter Lists)
  • shoutrrr (biblioteca de notificaciones unificada)
  • UPX (compresión de binario en imagen scratch)

Apoya el canal GENBYTE

Sígueme en mis redes sociales

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *