Configuración de DNSDist 2.x y TLS con Let’s Encrypt

dnsdist se utiliza junto con BIND porque este último, por sí solo, no soporta los protocolos modernos de resolución cifrada como DNS sobre HTTPS (DoH) ni DNS sobre TLS (DoT). De esta manera, actúa como un proxy que recibe las consultas seguras en HTTPS o TLS, las descifra y las reenvía a BIND mediante DNS tradicional en el puerto 53. De esta manera, BIND puede seguir cumpliendo su función como servidor autoritativo o recursivo, mientras dnsdist añade cifrado, control de tráfico, balanceo de carga y protección frente a ataques, ofreciendo un servicio DNS más seguro, eficiente y moderno.

Esta guía configura dnsdist 2.x con TLS (DoT y DoH) utilizando certificados Let’s Encrypt, incluye la configuración de AppArmor y los permisos de archivo adecuados. Se asume que no hay un usuario dnsdist dedicado.


1. Instalar dnsdist 2.x

Aunque la instalación desde el repositorio de PowerDNS requiere un poco más de trabajo siempre ofrece la última versión estable (actualmente 2.x), que incluye funciones actualizadas y correcciones de errores.

Primero, necesita crear el archivo /etc/apt/sources.list.d/pdns.list con este contenido:

deb [signed-by=/etc/apt/keyrings/dnsdist-20-pub.asc] http://repo.powerdns.com/debian bookworm-dnsdist-20 main

Poner esto en /etc/apt/preferences.d/dnsdist-20:

Package: dnsdist*
Pin: origin repo.powerdns.com
Pin-Priority: 600

y ejecutar los siguientes comandos:

install -d /etc/apt/keyrings; curl https://repo.powerdns.com/FD380FBB-pub.asc | tee /etc/apt/keyrings/dnsdist-20-pub.asc &&
sudo apt update &&
sudo apt install dnsdist

2. Certificados SSL

Dado que el host no cuenta con servidor web vamos a instalar certbot en modo standalone.

apt install python3-certbot

Verifique la instalación

certbot --version

Obtener el certificado

certbot certonly --standalone -d dns.dominio.edu.ar

Reemplace dns.dominio.edu.ar con su dominio actual. Esto genera los archivos en /etc/letsencrypt/live/dns.midominio.edu.ar/.

Ubicación de los certificados

Certbot deja los certificados y claves en:

/etc/letsencrypt/live/dns.dominio.edu.ar/fullchain.pem
/etc/letsencrypt/live/dns.dominio.edu.ar/privkey.pem

Renovación automática

Certbot instala una tarea cron para renovar certificados automáticamente,

Para que con cada actualización certboot reinicie dnsdist, cree un archivo de hook en /etc/letsencrypt/renewal-hooks/deploy/recarga-dnsdist.sh:

#!/bin/bash
systemctl reload dnsdist

Y dele permisos de ejecución

sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/recarga-dnsdist.sh

2. Permiso, permiso

Averigue con qué usuario se ejecuta dnsdist.

systemctl show -p User --value dnsdist.service

ó

systemctl status dnsdist

en cuya salida ver, por ejemplo

   Main PID: 121471 (dnsdist)

Agregue ese usuario al grupo the ssl-cert (predeterminado en Debian para letsencrypt):

usermod -aG ssl-cert dnsdist

Confirme que la clave sea legible para el grupo:

chmod 640 /etc/letsencrypt/live/dns.daminio.edu.ar/privkey.pem
chgrp ssl-cert /etc/letsencrypt/live/dns.daminio.edu.ar/privkey.pem

3. Configurar AppArmor

Edite el perfil principal de dnsdist:

nano /etc/apparmor.d/usr.sbin.dnsdist

Dentro del bloque de perfil { ... }, agregar los siguientes permisos de lectura para archivos SSL:

# Permitir que dnsdist lea las claves de LetsEncrypt
  /etc/letsencrypt/live/dns.dominio.edu.ar.edu.ar/privkey.pem r,
  /etc/letsencrypt/live/dns.dominio.edu.ar.edu.ar/fullchain.pem r,
  /etc/letsencrypt/archive/dns.dominio.edu.ar.edu.ar/* r,
  /etc/letsencrypt/renewal/dns.dominio.edu.ar.edu.ar.conf r,

Verificar la sintaxis:

apparmor_parser -T /etc/apparmor.d/usr.sbin.dnsdist

Recargar el perfil:

apparmor_parser -r /etc/apparmor.d/usr.sbin.dnsdist

3. Actualizar la configuración de dnsdist

Edite el archivo de configuración de su dnsdist (ej., /etc/dnsdist/dnsdist.conf) y defina:

-- Certificado y llave TLS
local ArchCert = "/etc/letsencrypt/live/dns.dominio.edu.ar/fullchain.pem"
local ArchLlave  = "/etc/letsencrypt/live/dns.dominio.edu.ar/privkey.pem"

-- Upstream backend (BIND)
newServer({
    address="127.0.0.1:53",
    name="bind-backend",
    checkInterval=10
})
setServerPolicy(firstAvailable)

-- DNS-over-TLS (DoT) en puerto 853
addTLSLocal("0.0.0.0:853", ArchCert, ArchLlave)
addTLSLocal("[::]:853", ArchCert, ArchLlave)

-- DNS-over-HTTPS (DoH) en puerto 443
addDOHLocal("0.0.0.0:443", ArchCert, ArchLlave, "/dns-query")
addDOHLocal("[::]:443", ArchCert, ArchLlave), "/dns-query"

-- ACL: permitir a todos (ya que DoH/DoT requieren auth/TLS de todos modos)
setACL({"0.0.0.0/0", "::/0"})

-- Refuerzo de seguridad
setMaxUDPOutstanding(65535)
setMaxTCPClientThreads(50)
setMaxTCPConnectionsPerClient(20)
setMaxTCPQueriesPerConnection(100)

-- Límite de tasa (por IP)
addAction(MaxQPSIPRule(100), DropAction())

-- Socket de control (admin local)
controlSocket("127.0.0.1:5199")

-- Logging
setVerbose(false)
setVerboseHealthChecks(true)

4. Recargar dnsdist

systemctl restart dnsdist

Verificar logs:

journalctl -u dnsdist -f

Probar localmente:

dig @127.0.0.1 dominio.edu.ar