Cómo desplegar un servidor DNS dinámico con Docker en Debian 10

El DNS dinámico es un servicio de red para asignar nombres de dominio a direcciones IP dinámicas (temporales, que cambian con frecuencia). Se utiliza para acceder a ordenadores que no tienen una dirección IP estática, como los de las redes SOHO (Small Office/Home Office), y a menudo se utiliza en combinación con el reenvío de puertos para acceder a sistemas que están detrás de cortafuegos NAT. Este artículo te guiará a través de la configuración completa de un servidor DNS dinámico en un contenedor Docker en un sistema Debian 10, incluyendo la configuración de los registros DNS necesarios, la colocación de la API de gestión detrás de un proxy inverso Nginx HTTPS y la automatización de las actualizaciones de los registros DNS del lado del cliente.

Requisitos

  • Un único servidor de Debian 10, opcionalmente con conectividad IPv6. (Se utilizarán 192.0.2.2 y 2001:0db8::0db9 como marcadores de posición para el IPv4 y el IPv6 del servidor, respectivamente).
  • Acceso al usuario root, o a un usuario con privilegios sudo.
  • Los puertos tcp/53 y udp/53 deben estar disponibles en el host.
  • Un nombre de dominio registrado y acceso a sus servidores de nombres/archivo de zona. Crea registros DNS para este dominio como se indica en la siguiente sección.
  • La variable de entorno $EDITOR debe estar configurada.
  • Opcionalmente, cualquier sistema cliente Linux/Unix para configurar las actualizaciones automáticas de los registros DNS.

Creación de registros DNS.

Tendrás que crear al menos 2 registros DNS para que tu servidor DNS dinámico funcione. En primer lugar, elige un subdominio como ns1.tu_dominio que apunte a la dirección IPv4 de tu servidor. En segundo lugar, elige un subdominio como ddns.tu_dominio que se delegará en ns1.tu_dominio.

Tu servidor de DNS dinámico gestionará todos los registros bajo ddns.tu_dominio. El tercer registro, de tipo AAAA, es opcional. Los registros correspondientes tienen el siguiente aspecto:

ns1.your_domain A 192.0.2.2
ddns.your_domain NS ns1.your_domain
ns1.your_domain AAAA 2001:0db8::0db9 (optional)

DNS records example

Debes crear estos registros en el panel de control de tu registrador de dominios. Ten en cuenta que estos registros pueden tardar hasta 24 horas en propagarse bien, pero normalmente tardan unos minutos.

Instalación

Si no utilizas el usuario root, te recomendamos que inicies un shell root temporal, ya que la mayoría de los comandos que se muestran en esta guía requieren privilegios elevados. Para iniciar un shell de root, utiliza uno de los siguientes comandos:

sudo su - root
sudo -s

Paso 1: Actualizar e instalar dependencias.

Siempre es una buena práctica actualizar primero tu sistema:

apt update
apt upgrade -y
reboot

Tras el reinicio, instala los paquetes de software necesarios para esta configuración:

  • certbot se utilizará para obtener los certificados SSL/TLS.
  • make es necesario para construir la imagen docker en la que se ejecutará el servidor DDNS.
  • apt-transport-https, ca-certificates, curl, gnupg2 y software-properties-common son necesarios para instalar el repositorio Docker y su correspondiente clave GPG.
  • dnsutils proporciona dig, que se utilizará para las pruebas.
apt install -y certbot make apt-transport-https curl ca-certificates software-properties-common gnupg2 dnsutils

Paso 2: Instala Docker CE.

Añade la clave GPG de Docker:

curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -

Instala el repositorio de Docker:

add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian buster stable"

Actualiza la caché del repositorio de Debian y luego instala docker y sus dependencias:

apt update
apt install -y docker-ce docker-ce-cli containerd.io

Una vez completada la instalación, asegúrate de que el servicio docker está habilitado y en funcionamiento como se indica a continuación:

systemctl enable --now docker.service

Paso 3: Descarga y construye docker-ddns

Nuestro servidor de DNS dinámico será alimentado por un contenedor docker que utiliza Bind como servidor de DNS y una API de gestión escrita en Go. Primero, clona el repositorio de Github y construye la imagen del contenedor con los siguientes comandos:

git clone https://github.com/dprandzioch/docker-ddns.git
cd docker-ddns
make image

Espera a que termine el proceso, que puede tardar un rato, y luego abre el archivo envfile con un editor de texto:

$EDITOR envfile

E introduce lo siguiente:

SHARED_SECRET=your_secret 
ZONE=ddns.your_domain
RECORD_TTL=60

El secreto compartido es una contraseña que se utilizará para autenticarse con la API de gestión. ZONA indica de qué zona DNS será responsable tu servidor, y el TTL del registro especifica durante cuánto tiempo se pueden almacenar en caché los registros DNS. Se recomienda un TTL de 60 segundos para las IP dinámicas que cambian con frecuencia.

Si es necesario, puedes generar una cadena aleatoria de 40 caracteres para el secreto utilizando el siguiente comando:

cat /dev/urandom | tr -dc "a-zA-Z0-9" | fold -w 40 | head -1

Ahora podemos crear el contenedor:

docker create -it -p 127.0.0.1:8080:8080 -p 53:53 -p 53:53/udp --env-file envfile -v /mnt/ddns-data:/var/cache/bind --name ddns-server davd/docker-ddns

Este comando creará un contenedor llamado ddns-server a partir de la imagen que construimos anteriormente, y asignará los puertos 8080/tcp, 53/tcp y 53/udp del host al contenedor. También montará el directorio /mnt/ddns-data del host, en /var/cache/bind en el sistema de archivos del contenedor. Esto se utiliza para persistir los datos del DNS a través de la recreación del contenedor.

Comprueba que el contenedor se ha creado con el comando:

docker container ls -a

Debería salir una única entrada con el nombre ddns-server.

Paso 4: Servicio Systemd (opcional)

Este paso sirve para simplificar la gestión, pero no es estrictamente necesario. Si decides no utilizar un servicio systemd, tendrás que gestionar el contenedor manualmente o utilizar otra solución de gestión. Ten en cuenta que para despliegues de contenedores más grandes y complejos, se recomienda una solución de orquestación como Kubernetes o Docker Swarm. En este caso, un servicio systemd es perfectamente adecuado, ya que sólo estamos ejecutando un único contenedor.

Para poder gestionar este contenedor como un servicio del sistema, lo envolveremos en una unidad systemd. Crea el archivo /etc/systemd/system/ddns-server-ct. service con tu editor de texto:

$EDITOR /etc/systemd/system/ddns-server-ct.service

Y añade lo siguiente :

[Unit]
Description=DDNS Server Docker Container
After=docker.service
Requires=docker.service
Requires=network.target
[Service]
Type=oneshot
TimeoutStartSec=240
Restart=no
RemainAfterExit=yes
ExecStart=/usr/bin/docker start ddns-server
ExecStop=/usr/bin/docker stop ddns-server
[Install]
WantedBy=multi-user.target

Guarda y sal, y luego establece los permisos correctos en este archivo de unidad:

chmod 664 /etc/systemd/system/ddns-server-ct.service

Carga el nuevo archivo de servicio con el siguiente comando:

systemctl daemon-reload

Ahora deberías poder iniciar y detener este contenedor mediante systemctl como cualquier otro servicio del sistema.

Si quieres que el servidor DDNS se inicie automáticamente al arrancar el sistema, ejecuta lo siguiente:

systemctl enable ddns-server-ct.service

Paso 5: Prueba de tu servidor

Antes de continuar con la configuración, vamos a probar la API de gestión de forma local. Inicia el contenedor:

systemctl start ddns-server-ct.service

Envía una petición GET a la API para crear un nuevo registro:

NOTA: Actualmente la API sólo es accesible localmente (es decir, desde localhost).

curl "http://127.0.0.1:8080/update?secret=your_secret&domain=test1&addr=1.1.1.1"

Curl debería devolver la siguiente respuesta:

{"Success":true,"Message":"Updated A record for test1 to IP address 1.1.1.1","Domain":"test1","Domains":["test1"],"Address":"1.1.1.1","AddrType":"A"}

NOTA: El dominio test1 se refiere a test1.ddns.tu_dominio. ya que el servidor está manejando la zona ddns.tu_dominio.

Realiza una búsqueda DNS para verificar que el registro se ha creado efectivamente y para probar la resolución DNS:

dig +short -t A test1.ddns.your_domain @127.0.0.1

El resultado debería ser 1.1.1.1.

Paso 6: Proxy inverso

Dado que la API funciona a través de HTTP, tu secreto de autenticación puede ser potencialmente olfateado cada vez que envíes una solicitud a través de la red. Un atacante podría entonces manipular tus registros DNS utilizando tu secreto. Configuraremos un proxy inverso utilizando Nginx y lo aseguraremos utilizando HTTPS. Primero, obtén un certificado SSL de Let’s Encrypt utilizando certbot:

certbot certonly --standalone --agree-tos -m [email protected] -d ns1.your_domain

Se verificará la propiedad de tu dominio y se emitirá un certificado. A continuación, instala Nginx y asegúrate de que está habilitado y funcionando:

apt install -y nginx systemctl enable --now nginx.service

A continuación, desactiva el archivo de bloqueo del servidor por defecto, ya que no es necesario:

unlink /etc/nginx/sites-enabled/default

Ahora crearemos un nuevo archivo de configuración para el proxy inverso, por ejemplo:

$EDITOR /etc/nginx/sites-available/ddns-api-proxy.conf

Y pega lo siguiente, asegurándote de sustituir las direcciones IP y los nombres de dominio por los tuyos:

server {
listen 192.0.2.2:8080;
server_name ns1.your_domain;
ssl on;
ssl_certificate /etc/letsencrypt/live/ns1.your_domain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ns1.your_domain/privkey.pem;

location /update {
proxy_pass http://127.0.0.1:8080;
}
location / {
return 404;
}
access_log /var/log/nginx/ddns-api-access.log;
error_log /var/log/nginx/ddns-api-error.log;
}

Opcional: Si quieres que la API sea accesible a través de IPv6, añade la siguiente línea después de la directiva listen existente:

listen [2001:0db8::0db9]:8080;

Activa esta configuración y aplica los cambios recargando Nginx:

ln -s /etc/nginx/sites-available/ddns-api-proxy.conf /etc/nginx/sites-enabled/
systemctl reload nginx.service

Ahora la API debería ser accesible a través de Internet, y sólo aceptará conexiones HTTPS. Para probarlo, emite el comando

curl "https://ns1.your_domain:8080/update?secret=your_secret&domain=test2&addr=1.1.1.2"

Debería devolver lo siguiente:

{"Success":true,"Message":"Updated A record for test2 to IP address 1.1.1.2","Domain":"test2","Domains":["test2"],"Address":"1.1.1.2","AddrType":"A"}

Paso 7: Configuración del cliente

Puedes configurar las actualizaciones automáticas de los registros en cualquier router que admita proveedores de DNS dinámicos personalizados, como Pfsense. También puedes configurarlas en la mayoría de los demás dispositivos de tu red de oficina o doméstica. Para actualizar o crear un registro, hay que enviar una solicitud GET al siguiente punto final:

https://ns1.your_domain:8080/update?secret=your_secret&domain=your_subdomain&addr=your_ip_address

También puedes actualizar los registros de varios subdominios con una sola solicitud. Por ejemplo, para crear/actualizar los registros de sub1.ddns.tu_dominio y sub2.ddns.tu_dominio con la dirección IP 198.51.100.100, debes enviar una solicitud GET a esta URL

https://ns1.your_domain:8080/update?secret=your_secret&domain=sub1,sub2&addr=198.51.100.100

El parámetro addr también puede contener una dirección IPv6 para crear/actualizar registros DNS AAAA, por ejemplo:

https://ns1.your_domain:8080/update?secret=your_secret&domain=cheese&addr=2001:0db8:aaaa::

Para automatizar estas actualizaciones en un cliente Linux, guarda el siguiente script bash como /opt/ddns-update.sh:

#!/bin/bash

while [ -z $CURRENTIP ] do
CURRENTIP=`dig -r +short myip.opendns.com @resolver1.opendns.com 2>/dev/null`
sleep 1
done
curl -s "https://ns1.your_domain:8080/update?secret=your_secret&domain=your_subdomain&addr=${CURRENTIP}"

Este script emplea un bucle while envuelto en un comando dig que obtiene la dirección IP pública del cliente y la almacena en una variable. El bucle asegura que la IP pública se obtiene correctamente. A continuación, se utiliza cURL para enviar una solicitud de API para actualizar el registro DNS con esta IP recién obtenida. Asegúrate de sustituir los valores de tu_secreto y tu_subdominio.

A continuación, haz que este script sea ejecutable:

chmod +x /opt/ddns-update.sh

A continuación, lanza el editor crontab:

crontab -e

Añade la siguiente línea al final de tu crontab:

*/2 * * * * /opt/ddns-update.sh

Guarda y sal. El script se ejecutará ahora cada dos minutos, manteniendo tu registro DNS dinámico actualizado con la última dirección IP pública del cliente.

Más información

También te podría gustar...