Monta tu propio DNS dinámico

, , 9 comments
Ahora que todos los servicios tipo dyndns.org y no-ip.org se han vuelto de pago, echamos de menos un servicio de DNS dinámico que poder usar por poco dinero (léase: gratis). La mala noticia es que no hay ninguno totalmente gratuito. La buena noticia es que, bajo ciertas circunstancias, podemos montarlo nosotros mismos.


Requisitos

Implementar un servicio de DNS dinámico requiere, por narices, disponer de un servidor DNS. Puede ser uno que tengamos en un servidor bajo nuestro control (supuesto que voy a usar en este artículo), uno en el servidor de un amigo, o uno de alguna organización que nos permita actualizaciones dinámicas. Y no vale un servidor DNS cualquiera: tiene que ser Bind, versión 8 o superior. Sé que todos vosotros, usuarios de DJBDNS, Bind 4.x, MaraDNS, PowerDNS y demás servidores alternativos, estaréis decepcionados; pero es el precio de vivir en el underground. Seguro que ya estáis acostumbrados. El otro requisito es nsupdate, la herramienta que vamos a usar para actualizar el DNS. En Debian y Ubuntu viene en el paquete dnsutils. En Fedora y parientes cercanos, bind-utils.

Sinopsis breve

Lo que vamos a hacer es definir una zona especial para actualizaciones dinámicas, y desde el cliente (el equipo con IP dinámica) actualizarla cada cierto tiempo con nsupdate. Para que sólo nosotros podamos hacerlo, nos autenticaremos con una “llave” cifrada que habremos acordado con el administrador del servidor DNS. Dividiendo esto en pasos más detallados, el proceso consiste en:
  • Crear una zona para actualizaciones dinámicas en el servidor
  • Crear una llave cifrada y permitir su uso para la actualización de la zona
  • Ejecutar periódicamente nsupdate en el cliente, para actualizar el registro
En los ejemplos voy a usar lo siguiente:
  • Como zona dinámica, dyn.linuxtecnico.es
  • Como cliente, raven.dyn.linuxtecnico.es
Si no os habéis asustado todavía, vamos a verlo paso por paso.

Configuración de una zona para actualizaciones dinámicas

En la configuración de nuestro servidor DNS tenemos que añadir una zona para actualizaciones dinámicas. Yo he usado un subdominio de linuxtecnico.es, pero podéis usar la zona principal si queréis. A mí no me gusta porque prefiero que las actualizaciones dinámicas estén restringidas a una zona que, en caso de desastre, no afecte al funcionamiento de la zona principal. Imaginaos que durante las pruebas os cargáis el registro MX y os quedáis sin correo. Y no os dais cuenta durante varios días. Cuando esperábais un mensaje importantísimo. Ya os imaginais. Lo primero que hay que hacer es añadir un registro NS a la zona principal, para que desde Internet sepan cómo llegar a nuestra zona dinámica. Una línea como ésta:
dyn IN NS ns.linuxtecnico.es.
Cuidado con el punto del final. Hubo unos minutos de rascamiento de cabeza y chirriar de dientes hasta que me di cuenta de que, la primera vez que metí la línea en la zona, me lo había olvidado. Y con un TTL de un día, eso significa que durante veinticuatro horas el error estará pululando por Internet, demostrando tu falta de pericia. La gente habla mal de ti por mucho menos. La configuración de Bind para esta zona sería:
zone "dyn.linuxtecnico.es" {
        type master;
        allow-transfer { none; };
        file "dynamic/db.dyn.linuxtecnico.es";
        update-policy {
                grant raven.dyn.linuxtecnico.es. name raven.dyn.linuxtecnico.es. A TXT;
        };
};
No hace alta mucha explicación sobre lo que hace esto. La parte novedosa es la de update-policy, en la que definimos qué vamos a permitir cambiar y a quién. La línea grant tiene la sintaxis:
grant <llave> <ámbito> <registro> <tipos de registro>
Significado de cada elemento:
  • llave es el nombre que identifica a la llave cifrada que usaremos para autenticarnos con el servidor. ámbito indica qué podemos cambiar: con name sólo podemos cambiar el registro que apunta al propio nombre de la llave, que es lo que nos interesa aquí; pero también hay otros tipos como subdomain, self y wildcard. Echad un vistazo a las referencias, más abajo, para ver una explicación de cada uno.
  • registro es el registro DNS que queremos cambiar. En este caso, el nombre del clente, raven.dyn.linuxtecnico.es.
  • tipos de registro son los tipos de registro que puede cambiar el cliente. Es un parámetro opcional, pero por defecto permite demasiadas cosas para mis draconianos gustos y prefiero restringirlo haciéndolo explícito. De esta forma nos aseguramos de que sólo los registros tipo “A” (direcciones IP) y “TXT” (información sobre el registro) se pueden cambiar desde el cliente.
Una vez configurada la zona, hay que crear un esqueleto sobre el que se vayan a hacer las modificaciones. Para dyn.linuxtecnico.es, yo he usado esto:
$ORIGIN .
$TTL 3600 ; 1 hour
dyn.linuxtecnico.es IN SOA linuxtecnico.es. root.linuxtecnico.es. (
    2012050903 ; serial
    28800      ; refresh (8 hours)
    7200       ; retry (2 hours)
    2419200    ; expire (4 weeks)
    604800     ; minimum (1 week)
    )
   NS ns.linuxtecnico.es.
$ORIGIN dyn.linuxtecnico.es.
Y lo he colocado en el directorio “dynamic”, fichero “db.dyn.linuxtecnico.es”. El fichero debe tener permisos de escritura para Bind, y el directorio también: Bind va a crear ficheros de journal (extensión .jnl) con las modificaciones que se hagan a las zonas, y cada cierto tiempo las volcará al fichero principal. Por eso mismo tengo que incluir esta nota importante: ¡No hagáis modificaciones a mano sobre el fichero de zona! Si lo hacéis, el fichero journal y el fichero de zona no estarán sincronizados, y Bind se sentirá confuso y violento (como veréis al consultar los logs). Si necesitáis hacer modificaciones, tenéis que usar rndc freeze y rndc thaw. La página man os enseñará todo lo que necesitáis saber sobre ellos.

Creación de una llave cifrada y configuración para su uso

Ahora que tenemos una zona dinámica configurada, el siguiente paso es crear una llave cifrada para que sólo el cliente que hayamos autorizado pueda actualizar el registro con su nombre. Para eso hace falta la utilidad dnssec-keygen, que en Debian y Ubuntu viene en el paquete dnssec-tools. Es mejor que lo instaléis en el servidor y generéis allí la llave, porque tirará por dependencias de bind9 y muchos otros paquetes. En el servidor, como asumo que ya tenéis instalado bind9, no instalará tantas cosas. Para crear una llave cifrada para el cliente “raven.dyn.linuxtecnico.es”, el comando es el siguiente:
dnssec-keygen -b 512 -a HMAC-MD5 -n USER raven.dyn.linuxtecnico.es
Técnicamente, el nombre de la llave es independiente del nombre que vaya a tener el cliente. Podríamos haber creado la llave con nombre “Pepito”, y valdría igual. Pero las buenas costumbres recomiendan crearla con el nombre DNS del cliente. Seguro que ya habréis adivinado qué son los parámetros de dnssec-keygen: “-b” es para el número de bits de la llave, “-a” para el algoritmo de hashing que usará, y “-n” sirve para indicar el tipo de llave. En este caso será “USER”. ¿Y por qué no “HOST”, que es otro tipo contemplado en la documentación de dnssec-keygen? Ni idea. Hay cosas que los humanos no estamos destinados a entender. El comando de ahí arriba creará dos ficheros, uno con extensión “private” y otro con extensión “key”. El último es la parte pública de la llave, y el que tendremos que usar en la configuración del servidor. El otro es el que usaremos desde el cliente para autenticar nuestras peticiones. Para permitir a quien se autentique con esta llave que actualice el registro raven.dyn.linuxtecnico.es, tenemos que cargar la llave desde Bind. Un “cat” del fichero “.key” de la llave nos da algo así:
raven.dyn.linuxtecnico.es. IN KEY 0 3 157 2CvrkaTJFXQZYvqW/EOb0rRYJ3lu1PfjdOI36KHzIU3QBIuOJdt6GSMC cGeSbQWXbVPOSDgpoe0LJxX54eS6wn==
Tenemos que coger todo el zurullo incomprensible (“2Cvrka … 6wn==”) y ponerlo en la configuración de Bind con algo como esto:
key raven.dyn.linuxtecnico.es. {
 algorithm hmac-md5;
 secret "2CvrkaTJFXQZYvqW/EOb0rRYJ3lu1PfjdOI36KHzIU3QBIuOJdt6GSMC cGeSbQWXbVPOSDgpoe0LJxX54eS6wn==";
};
Con esto, Bind tendrá la parte pública que asociar a nuestra petición, donde usaremos la parte privada de la llave.

Ya casi estamos acabando: cómo usar nsupdate

Todo lo anterior lo hicimos en el servidor que ejecutaba Bind. Ahora vamos al cliente, desde donde usaremos nsupdate. Copiad la parte privada de la llave (que tendrá un nombre parecido a éste: Kraven.dyn.linuxtecnico.es.+157+55947.private) a algún sitio accesible, y luego ejecutad este comando:
nsupdate -k Kraven.dyn.linuxtecnico.es.+157+55947.private
Una vez hecho, nsupdate entrará en “modo comandos”. Aparecerá un prompt en el que podemos introducir comandos para manipular zonas de servidores DNS que nos dejen. Suponiendo que ns.linuxtecnico.es es el servidor que hemos configurado para albergar nuestra zona dinámica, podríamos añadir un registro para raven.dyn.linuxtecnico.es con estos comandos:
server ns.linuxtecnico.es
zone dyn.linuxtecnico.es
update add raven.dyn.linuxtecnico.es. 3600 A 1.1.1.1
show
send
Todo bastante obvio. La parte de “update” es la que hace los cambios de verdad, y donde definimos los atributos (como el TTL, 3600 segundos) del registro que vamos a añadir. El comando “show” muestra lo que se va a enviar, y “send” lo envía. Esto nos valdrá para la primera vez que añadamos el registro, pero de ahí en adelante tendremos que borrarlo primero para actualizarlo. No hay un “update update” que podamos usar para actualizarlo a secas. Antes de la línea de “update add” tendríamos que añadir algo así:
update delete raven.dyn.linuxtecnico.es. A
Podemos meter todas estas instrucciones en un fichero, y pasárselo a nsupdate con el parámetro “-v”. Así es más fácil de automatizar. Sólo nos queda un problema: ¿cómo averiguamos nuestra IP pública para ponerla en el registro DNS? Lo que yo he hecho, y no digo que sea la mejor solución, es usar un servicio de dyndns.org que se llama “checkip”. En concreto, puedes conectarte a http://checkip.dyndns.org y saldrá una pequeña página web con tu IP pública. Un ejemplo:
$ telnet checkip.dyndns.org 80
Trying 91.198.22.70...
Connected to checkip.dyndns.com.
Escape character is '^]'.
GET / HTTP/1.0

HTTP/1.1 200 OK
Content-Type: text/html
Server: DynDNS-CheckIP/1.0
Connection: close
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 105

<html><head><title>Current IP Check</title></head><body>Current IP Address: 213.60.49.215</body></html>
Connection closed by foreign host.
Que, pasado a algo que podamos usar como una variable shell, sería algo así:
CURRENT_IP=`(echo -e "GET / HTTP/1.0\n\n"; sleep 0.5) | telnet checkip.dyndns.org 80 2> /dev/null | grep Current | sed 's/^.*Current IP Address: \(.*\)<.body.*/\1/'`
Como antes, no digo que sea la mejor forma de hacerlo. Sentíos libres de sugerir mejoras en los comentarios. Ahora podemos meter esto y todas las órdenes para nsupdate en un script, al que yo he llamado update-ip.sh:
#!/bin/sh

CURRENT_IP=`(echo -e "GET / HTTP/1.0\n\n"; sleep 0.5) | telnet checkip.dyndns.org 80 2> /dev/null | grep Current | sed 's/^.*Current IP Address: \(.*\)<.body.*/\1/'`
TMPFILE=`mktemp /tmp/update-ip-XXXXXX`

cat > $TMPFILE <<EOF
server ns.linuxtecnico.es
zone dyn.linuxtecnico.es
update delete raven.dyn.linuxtecnico.es. A
update add raven.dyn.linuxtecnico.es. 3600 A $CURRENT_IP
send
EOF

nsupdate -k Kraven.dyn.linuxtecnico.es.+157+55947.private -v $TMPFILE && rm -f $TMPFILE
Podemos ejecutar esto en cron cada hora, y así tendremos nuestro registro actualizado. Comprobación posterior:
$ host raven.dyn.linuxtecnico.es
raven.dyn.linuxtecnico.es has address 213.60.49.251

Conclusión

Tenía esto guardado en la bag of tricks desde hacía años, cuando lo había probado ya ni recuerdo para qué. Llegué a comprar un dominio sólo para esto, aprovechando una oferta que lo dejaba a un precio ridículo (2€ el primer año, me parece). Al final, después de las pruebas, quedó abandonado y no renové el dominio. Pero en los últimos días, un par de mensajes en la lista de GPUL preguntando por un servicio de DNS dinámico bueno, bonito y barato, me hicieron recordarlo.

Referencias

Los dos artículos de Jeff Garzik sobre DNS dinámico con dnsupdate: http://linux.yyz.us/nsupdate/ http://linux.yyz.us/dns/ddns-server.html Página de Zytrax con información sobre la sintaxis de update-policy: http://www.zytrax.com/books/dns/ch7/xfer.html#update-policy

9 comentarios:

  1. Un artículo muy bueno... como los que sueles hacer o mejor ;-)

    ResponderEliminar
  2. pero este server dns dinamico igualmente no debe ser redireccionado desde un server dns superior; por ejemplo si se registra un dominio en nic.ar ahi hay que poner esta ip dinamica, pero nic.ar tiene una interface para actualizar la ip de este server dns?

    ResponderEliminar
  3. #! /bin/sh
    #
    #direccion de correo
    #
    email=marzana.cpp@gmail.com
    #
    #capturamos la ip actual de mi internet
    #
    curl -s ifconfig.me > /tmp/ip2_actual.ip4
    ip_actual=$(cat /tmp/ip2_actual.ip4)
    #
    #guardamos la ip actual de mi internet en el archivo ip2_actual.ip4
    #
    if [ ! -e /tmp/ip_actual.ip4 ]; then
    curl -s ifconfig.me > /tmp/ip_actual.ip4
    fi

    newip=$(diff /tmp/ip_actual.ip4 /tmp/ip2_actual.ip4 | wc -l)
    if [ $newip -gt 0 ]; then
    mv -f /tmp/ip2_actual.ip4 /tmp/ip_actual.ip4
    cat > $TMPFILE <<EOF
    server ns1.ingeniolinux.com.ve
    zone dyn.ingeniolinux.com.ve
    update delete raven.dyn.ingeniolinux.com.ve. A
    update add raven.dyn.ingeniolinux.com.ve. 3600 A $ip_actual
    send
    EOF
    nsupdate -k /etc/bind/Kraven.dyn.ingeniolinux.com.ve.+157+13981.private -v $TMPFILE && rm -f $TMPFILE
    else
    rm /tmp/ip2_actual.ip4
    fi;
    # echo 'La ip actual de ABA CANTV es:' $ip_actual

    ResponderEliminar
  4. espero puedas responder... yo hace como 4 meses estuve jugando con dns dinamico pero en CentOS, pude hacer todo lo que mencionas, el cliente actualizaba en el servidor DNS y se cambiaba la zona por la IP publica de mi cliente, el problema que tenia es que cada vez que yo sabia que se cambio la IP publica tenia que reniciar el demonio bind para que pueda acceder a mi cliente con el dominio asignado y de ahi no pude pasar, asi que opte por NO-IP.

    Tienes alguna idea de como puedo hacer que cada vez que el cliente haga un UPDATE al DNS principal este al hacer una consulta desde donde sea me rediriga a mi host cliente ???

    saludos y exelente tuto, me estoy animando a retomar este proyecto que abandone.

    ResponderEliminar
    Respuestas
    1. Juan Carlos Marzana1 de marzo de 2017, 2:56

      Disculpa la tardanza, si mal no recuerdo yo me hice con cron y un archivo por lote que ser ejecutara cada día a una hora determinada si la ip que tenia mi registro A en el archivo de zona se ejecutaban la lineas que te di, si no salia y hasta el día siguiente. no te respondí ante por que gracias al la situación de Venezuela me ha toco trabajar mas y disfrutar menos y esto lo hago en mis ratos de ocio. y ahora estoy sacando tiempo para mis proyectos archivados por eso estoy aqui. Búscame en FB

      Eliminar
  5. ip_actual=`dig myip.opendns.com @resolver1.opendns.com +short`

    ResponderEliminar
  6. Otra manera de saber la IP publica : curl (man culr, para mas información)

    CURRENT_IP=$(curl -s ifconfig.me)

    ifconfig.me solo devuelve la ip

    ResponderEliminar
  7. no me entero de nada!!
    hay alguien que quiera explicar esto para novatos

    Gracias

    ResponderEliminar
    Respuestas
    1. No te sientas mal pero es que esto no es para novatos..

      Eliminar