Script Mikrotik: Actualizar el DDNS de No-IP

Sí, ahora hago scripts y es bonito poder suplir necesidades haciendo pequeños programas.

Está bien, sé que a lo mejor estoy exagerando. Pero cambié mi red con base en un router MikroRik RB5009 y hasta el momento ha ido muchísimo mejor de lo que esperaba. Pese a que llevo un par de días recién con el equipo en funcionamiento he estado descubriendo todo lo que puede llegar a hacer, y para muestra un botón:

DNS Dinámico

Para quien no sepa, el DNS dinámico es un servicio de traducción de nombres que se utliza en situaciones donde un conexión a Internet todavía dispone completamente de una IP pública (sin pasar por técnicas de optimización como el CG-NAT) y esta varía según la conexión, es decir, no siempre se dispone de la misma dirección IP. Esto puede suponer un problema para cualquiera que quiera levantar un servicio dependiente de esta IP dinámica, puesto que ante cualquier cambio se debiera hacer el cambio de la dirección en todos los dispositivos que usen tal servicio.

Esto se soluciona con el DNS Dinámico (DDNS), el cual es un registro DNS capaz de cambiar rápidamente ante un cambio de IP pública, manteniendo para el usuario una dirección fácil de recordar (como nombredeusuario.ddns.net o algo por el estilo). Existen varios proveedores que brindan este servicio libre de costos, tal como No-IP, el cual yo llevo usando aproximadamente unos 15 años a la fecha. Es un servicio muy fácil de administrar, el cual te provee una nombre en sus dominios gratis, con la única condició que debes confirmar que sigues usando el nombre que se te otorga al menos una vez al mes.

Muchos routers de grado doméstico tienen ya integrado un cliente DDNS el cual en la mayoría de las veces es compatible con No-IP (dada su popularidad), no obstante en routers de grado profesional es raro verlo. Al menos en el caso de Mikrotik, dicho problema es solucionable mediante un script.

Scripts de Mikrotik

Mikrotik tiene su propio lenguaje de scripting el cual está bastante bien documentado y permite, a muy grandes rasgos, automatizar cualquier cosa que puedas realizar en su terminal, muy al estilo de lo que se puede hacer en routers Cisco con su capa de Python. En este caso utilicé este método para solucionar mi problema particular, que es tomar la IP pública y entregársela a No-IP mediante su API para realizar la actualización.

Aviso: Si está acá asumo que entiende dónde está la función de scripting y cómo funciona, por lo cual no lo voy a explicar acá.

El script

Este script requiere que se le especifiquen cuatro valores:

  • DDNSUSER: Nombre de usuario o correo electrónico de la cuenta de No-IP
  • DDNSPASS: Contraseña de la cuenta de No-IP. No admite carácteres especiales.
  • DDNSHOST: Dirección de dominio a revisar/actualizar.
  • INTERFAZ: Interfaz del router que tiene la salida a internet. En mi caso es pppoe-out1.

A su vez, el script está dividido en tres partes:

  • Variables locales: Las cuatro variables indicadas arriba.
  • Funciones locales: Recogen la IP del router y la IP que tiene actualmente el host DDNS.
  • Procedimiento: La parte del script que hace el trabajo.

El script funciona de la siguiente manera:

  1. El router toma la IP que obtuvo la interfaz y quita la máscara para hacer la comparación.
  2. Si la IP pública de la interfaz es distinta a la del DDNS, hace lo siguiente:
    • Envía un mensaje al log indicando que las IP son distintas y hará el cambio.
    • Realiza un fetch a la URL de actualización de No-IP con los datos recogidos y las variables.
    • Envía un mensaje al log indicando que se realizó el cambio.
  3. Si la IP pública es la misma que la del DDNS, envía un mensaje al log indicando que no se realizaron cambios. Esta opción la tengo comentada puesto que es opcional

El script es el siguiente:

# Variables locales #

:local DDNSUSER "usuario"
:local DDNSPASS "password"
:local DDNSHOST "dominio"
:local INTERFAZ "pppoe-out1"

# Funciones locales #

:local IPROUTER [/ip address get [find interface=$INTERFAZ] address];
:local IPDDNS [/resolve $DDNSHOST];

# Procedimiento #

:set IPROUTER [:pick $IPROUTER 0 [:find $IPROUTER "/"]]
if ($IPROUTER != $IPDDNS) do={
:log info "La IP pública ($IPROUTER) es distinta de la del DDNS ($IPDDNS), cambiando...";
/tool fetch url="http://$DDNSUSER:$DDNSPASS@dynupdate.no-ip.com/nic/update\3Fhostname=$DDNSHOST&myip=$IPROUTER" keep-result=no;
:log info "Listo! Ahora la IP del DDNS quedó configurada con la IP pública ($IPROUTER)";
} else={
#:log info "La IP pública ya es la misma que la del DDNS. ($IPROUTER)"
}

Algunas observaciones y pendientes

  • Se supone que el fetch del actualizador responde un comentario según el resultado de la operación con lo que se le entrega. Esto es importante para realizar debugging pero como el sitio le da formato al mensaje antes de entregarlo, todavía no sé cómo capturarlo.
  • El log si ambas IP son iguales lo tengo comentado puesto que en mi caso tengo el script corriendo constantemente, entonces se me llenaría el log de mensajes del script y no es la idea.
  • El script funciona en RouterOS 7 y hasta ahora es el único que he visto que funciona. Todos los resultados que vi en internet no corrían con esta versión del sistema.
  • El script se puede adaptar a cualquier servicio que permita actualizar sus registros mediante el uso de una URL. Si existe algún servicio que permita el cambio con alguna llave API en vez de tener que entregar username y contraseña en texto claro, avíseme.
  • Este script es de seguridad baja, puesto que hay que entregarle el nombre de usuario y contraseña en texto claro al servicio.