06 mayo 2019

Monta tu propio DNS dinámico

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 te asustaste 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 <ámbito>


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

Current IP CheckCurrent IP Address: 213.60.49.215
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 <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

Tomado de: http://www.linuxtecnico.es/2012/05/monta-tu-propio-dns-dinamico.html





#! /bin/sh
#
#direccion de correo
#
email=mail.spam@gmail.com
#
#capturamos la ip actual de mi internet
#
#curl -s ifconfig.me > /tmp/ip2_actual.ip4
#ip_actual=$(cat /tmp/ip2_actual.ip4)
ip_actual=`dig myip.opendns.com @resolver1.opendns.com +short`
#
#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 <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

Que te diviertas!


Tomado de: http://www.linuxtecnico.es/2012/05/monta-tu-propio-dns-dinamico.html

No hay comentarios:

Publicar un comentario