Los problemas de DNS no se anuncian como problemas de DNS. Aparecen como “la página de inicio de sesión va lenta”, “Kubernetes se comporta raro”, “apt no puede descargar” o “nuestro monitor está en verde pero los usuarios están enfadados”. Y cuando finalmente miras de cerca, te das cuenta de que cada host hace sus propias búsquedas recursivas por una ruta de red inestable, o peor, has construido una cadena de resolutores que se devora a sí misma.
Unbound es la herramienta sobria y afilada para este trabajo: un resolver validante y con caché que puedes ejecutar localmente o de forma central, con valores sensatos y comportamiento predecible. Puedes configurarlo en 15 minutos. El truco es evitar la trampa común: crear un bucle con tu resolutor existente (a menudo systemd-resolved), y pasar la siguiente hora preguntándote por qué todo es SERVFAIL.
Por qué Unbound (y qué estás construyendo realmente)
Unbound es un resolver DNS recursivo con caché y validación DNSSEC opcional. “Recursivo” significa que recorrerá la jerarquía DNS por sí mismo (root → TLD → autoritativo), en lugar de pedirle a otro que lo haga. “Con caché” significa que si 5000 procesos solicitan el mismo registro, no haces 5000 búsquedas aguas arriba. Y la validación DNSSEC significa que puedes rechazar respuestas falsificadas—con el coste de complejidad si tu entorno es desordenado.
Lo que realmente estás construyendo es un límite de dependencias:
- Tus hosts dependen de tu resolutor, no de lo que DHCP les dio en una cafetería el año pasado.
- Tus aplicaciones obtienen menor latencia en la cola para flujos de trabajo con mucho DNS (descubrimiento de servicios, CDNs, llamadas API, gestores de paquetes).
- Ganas un lugar para observar DNS: registros, comportamiento de caché, modos de fallo.
El patrón clásico en producción es uno de estos:
- Caché local por nodo: Unbound en cada máquina, escuchando en 127.0.0.1, reenviando a upstream. Ideal para portátiles, servidores únicos o nodos sensibles a la latencia.
- Resolver central: Un pequeño pool de servidores Unbound, los clientes los apuntan a ellos. Ideal para flotas, políticas consistentes y observabilidad más sencilla.
- Híbrido: Unbound local reenviando a Unbound central, que hace la recursión. Ideal cuando quieres caché local + política central.
Elige deliberadamente. “Lo que funcione” es cómo terminas con un bucle de resolutores, DNSSEC inconsistente y una sala de guerra llena de personas que de repente recuerdan que DNS es difícil.
Datos rápidos e historia breve que realmente importa
Los hechos y el contexto no son trivia aquí; explican por qué ciertos valores por defecto existen y por qué algunos modos de fallo se repiten.
- DNS es anterior a la web. El RFC de DNS data de principios de los años 80, diseñado para una red más pequeña, amistosa y menos adversaria.
- TTL es un contrato, no una sugerencia. Se espera comportamiento de caché. Si tu app “necesita” cambios DNS instantáneos, el problema es tu app.
- Existe caché negativa. NXDOMAIN también puede ser cacheado, por eso un error tipográfico puede perseguirte durante minutos.
- DNSSEC se añadió posteriormente. Añade verificación de integridad, pero también más formas de fallar (desajuste de reloj, cadenas rotas, problemas MTU/fragmentación).
- Los resolutores no son solo “un servidor”. Son máquinas de estado: consultas pendientes, reintentos, timeouts, caché y política.
- El archivo de root hints no es opcional para recursión completa. Si haces recursión verdadera, necesitas una forma de encontrar los servidores raíz. Los paquetes modernos lo manejan, pero el concepto importa.
- EDNS0 cambió el juego. Payloads UDP más grandes y registros DNSSEC hicieron las respuestas mayores, lo que expuso dispositivos de red que odian la fragmentación.
- systemd-resolved popularizó un modelo de stub-resolver. Un stub local en 127.0.0.53 puede estar bien—hasta que lo apiles con otro resolver local y crees un bucle.
- Unbound viene del ecosistema NLnet Labs. Lo construyen personas que viven y respiran la corrección de DNS; no es un proyecto de hobby.
Una cita que vale la pena tener en la cabeza mientras haces esto:
“La esperanza no es una estrategia.” — James Cameron
El trabajo de fiabilidad DNS es donde esa frase demuestra su valor. No “esperas” que tu ruta de resolutores sea sensata. La pruebas.
Configuración en 15 minutos: Ubuntu/Debian, segura para producción
Esta configuración está optimizada para “poner en marcha un resolver caché estable rápido” sin encerrarte. Asume que systemd está presente (Ubuntu/Debian), y que estás de acuerdo con que Unbound escuche en localhost al principio. Más tarde puedes enlazarlo a una IP de LAN para clientes.
Paso 1: Instalar Unbound (y herramientas)
cr0x@server:~$ sudo apt-get update
...output...
cr0x@server:~$ sudo apt-get install -y unbound dnsutils
...output...
Qué significa la salida: Quieres ver Unbound instalado y una unidad systemd creada. Si apt instala algo inesperado (como otro resolutor que no planeaste), pausa y lee la lista de dependencias.
Decisión: Procede solo si entiendes qué está proveyendo DNS en el host ahora (systemd-resolved vs Unbound). Dos resolutores pueden coexistir, pero solo si eres explícito sobre roles.
Paso 2: Crear una configuración mínima y segura de Unbound
En Debian/Ubuntu, la configuración principal de Unbound suele estar en /etc/unbound/unbound.conf e incluye fragmentos de /etc/unbound/unbound.conf.d/. Dejaremos un snippet para que las actualizaciones del paquete no te pisen.
cr0x@server:~$ sudo tee /etc/unbound/unbound.conf.d/local-cache.conf >/dev/null <<'EOF'
server:
# Listen only on localhost to start. Expand later.
interface: 127.0.0.1
port: 53
# Allow local queries only (for now).
access-control: 127.0.0.0/8 allow
# Basic hardening and privacy.
hide-identity: yes
hide-version: yes
qname-minimisation: yes
# Cache tuning: safe defaults.
cache-min-ttl: 0
cache-max-ttl: 86400
cache-max-negative-ttl: 300
prefetch: yes
# Reliability.
do-daemonize: no
use-syslog: yes
verbosity: 1
# DNSSEC validation on (default on many distros, but be explicit).
auto-trust-anchor-file: "/var/lib/unbound/root.key"
# Avoid UDP fragmentation pain by advertising a conservative buffer.
edns-buffer-size: 1232
EOF
...output...
Qué significa la salida: Si ves el archivo impreso, se escribió. Si ves “Permission denied”, olvidaste sudo o la ruta está mal.
Decisión: Mantenlo solo en localhost hasta que lo verifiques. Exponer un resolver a la red sin access-control es como terminar con un resolver abierto. Eso acaba mal y de forma ruidosa.
Paso 3: Iniciar y habilitar Unbound
cr0x@server:~$ sudo systemctl enable --now unbound
...output...
cr0x@server:~$ systemctl status unbound --no-pager
● unbound.service - Unbound DNS server
Loaded: loaded (/lib/systemd/system/unbound.service; enabled; vendor preset: enabled)
Active: active (running) ...
...output...
Qué significa la salida: “active (running)” es la línea base. Si está “failed”, lee las últimas líneas; Unbound suele ser franco sobre errores de parseo de configuración.
Decisión: Si Unbound no está en ejecución, no toques resolv.conf todavía. Arregla Unbound primero. De lo contrario te estarás cortando la propia sesión SSH.
Paso 4: Apuntar el host a Unbound (sin romper systemd-resolved)
Aquí es donde nace la trampa común. En muchos sistemas modernos, /etc/resolv.conf es un enlace simbólico al archivo stub de systemd-resolved, y apunta a 127.0.0.53. Si configuras Unbound de forma ingenua para reenviar a “lo que esté en resolv.conf”, puedes crear un bucle: Unbound → stub → Unbound → stub… hasta que todo haga timeout.
Lo haremos de forma limpia:
- Opción A (simple): Deshabilita systemd-resolved y deja que Unbound sea el resolutor.
- Opción B (coexistencia): Mantén systemd-resolved pero configúralo para usar 127.0.0.1 (Unbound) como upstream, y asegúrate de que Unbound no reenvíe de vuelta al stub.
Opción A: Unbound como único resolutor local
cr0x@server:~$ sudo systemctl disable --now systemd-resolved
...output...
cr0x@server:~$ sudo rm -f /etc/resolv.conf
...output...
cr0x@server:~$ printf "nameserver 127.0.0.1\noptions edns0 trust-ad\n" | sudo tee /etc/resolv.conf
nameserver 127.0.0.1
options edns0 trust-ad
Qué significa la salida: /etc/resolv.conf ahora es un archivo real apuntando a Unbound. La opción trust-ad solicita datos autenticados (bit AD) cuando sea posible.
Decisión: Usa la Opción A en servidores donde quieras menos piezas móviles y controles de red. También es más fácil de razonar a las 03:00.
Opción B: Mantener systemd-resolved, usar Unbound como upstream
Esto evita algunos casos con clientes VPN y DNS dividido que dependen de systemd-resolved. Configura resolved para usar Unbound:
cr0x@server:~$ sudo mkdir -p /etc/systemd/resolved.conf.d
...output...
cr0x@server:~$ sudo tee /etc/systemd/resolved.conf.d/unbound-upstream.conf >/dev/null <<'EOF'
[Resolve]
DNS=127.0.0.1
Domains=~.
DNSStubListener=yes
EOF
...output...
cr0x@server:~$ sudo systemctl restart systemd-resolved
...output...
Qué significa la salida: resolved sigue siendo el stub en 127.0.0.53, pero reenvía a Unbound en 127.0.0.1. Eso está bien siempre que Unbound no reenvíe de vuelta.
Decisión: Elige la Opción B cuando necesites las funciones de systemd-resolved (DNS por enlace, integración VPN), pero aun así quieras caché/validación de Unbound.
La trampa común: bucles de resolutores (síntomas, prueba, solución)
El bucle suele ocurrir así:
- El sistema apunta al stub resolver en
127.0.0.53(systemd-resolved). - Instalas Unbound y lo configuras para reenviar a “lo que esté en /etc/resolv.conf”.
- Unbound reenvía a 127.0.0.53.
- systemd-resolved está configurado (explícita o automáticamente) para usar 127.0.0.1 (Unbound) o “el DNS local”.
- Felicidades: has creado un ouroboros DNS.
Patrón de síntomas: breves ráfagas de éxito y luego timeouts; SERVFAIL; alto uso de CPU en Unbound; logs mostrando consultas repetidas; dig tardando ~5 segundos y sin devolver nada útil.
Prueba: identifica a quién está consultando realmente tu sistema
cr0x@server:~$ readlink -f /etc/resolv.conf
/run/systemd/resolve/stub-resolv.conf
Qué significa la salida: Estás usando el stub de systemd-resolved, no Unbound directamente.
Decisión: Si planeabas la Opción A, arregla resolv.conf. Si planeabas la Opción B, asegúrate de que Unbound no reenvíe a 127.0.0.53.
Prueba: ve dónde está enruta Unbound (si lo hace)
cr0x@server:~$ sudo unbound-checkconf
unbound-checkconf: no errors in /etc/unbound/unbound.conf
Qué significa la salida: La configuración se parsea. No significa que tu reenvío sea sensato; solo que Unbound puede leer el archivo sin fallar.
Decisión: Si tienes un forward-zone configurado, verifica que apunte a resolutores upstream reales (no a tu stub local a menos que lo quieras así).
Solución: elige una dirección y hazla unidireccional
Reglas unidireccionales que te mantienen fuera de problemas:
- Si Unbound es recursivo, no reenvíes a systemd-resolved.
- Si Unbound reenvía, reenvía a IPs upstream conocidas (resolutores corporativos o externos)—no a “lo que diga resolv.conf”.
- Si systemd-resolved reenvía a Unbound, Unbound no debe reenvíar a systemd-resolved.
Chiste #1 (corto, relevante): Los bucles DNS son como los organigramas de oficina: parecen bien hasta que te das cuenta de que todos se reportan a sí mismos.
Verificación: demuestra que hace caché y valida
DNS es uno de esos sistemas donde “parece que está bien” es una trampa. Verifica:
- Las consultas son respondidas por Unbound (no se lo saltan).
- La segunda consulta es más rápida (la caché funciona).
- El estado DNSSEC es sensato (si está activado).
- La latencia y los timeouts están en el rango esperado.
Prueba básica de consulta
cr0x@server:~$ dig @127.0.0.1 example.com A +noall +answer +stats
example.com. 86396 IN A 93.184.216.34
;; Query time: 28 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
...output...
Qué significa la salida: La línea SERVER: debe ser 127.0.0.1 (Unbound). Un tiempo de consulta de decenas de milisegundos para caché fría es normal.
Decisión: Si el servidor no es 127.0.0.1, tus clientes no están usando Unbound. Arregla la configuración del resolutor antes de afinar nada.
Prueba de caché: ejecútalo dos veces
cr0x@server:~$ dig @127.0.0.1 example.com A +noall +answer +stats
example.com. 86396 IN A 93.184.216.34
;; Query time: 1 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
...output...
Qué significa la salida: 1–2 ms indica acierto de caché (o al menos un camino muy rápido). Ese es todo el propósito.
Decisión: Si la segunda consulta no es más rápida, revisa si estás evitando Unbound o si la caché está efectivamente deshabilitada por configuración/política.
Señal DNSSEC: solicita el bit AD
cr0x@server:~$ dig @127.0.0.1 dnssec-failed.org A +dnssec +noall +comments +answer
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 12345
...output...
Qué significa la salida: dnssec-failed.org está intencionadamente mal firmado; un resolver validante debería fallar. SERVFAIL aquí es una buena señal.
Decisión: Si necesitas validación DNSSEC por política/seguridad, mantén este comportamiento. Si esto rompe nombres internos críticos (porque tu DNS interno es “creativo”), puede que necesites acotar DNSSEC o reenviar zonas internas.
Tareas prácticas (comandos, significado de salidas, decisiones)
Pediste tareas reales. Aquí hay más de una docena. Cada una te dice algo accionable operativamente.
Tarea 1: Confirma que Unbound está escuchando donde crees
cr0x@server:~$ sudo ss -lntup | grep -E ':(53)\b'
udp UNCONN 0 0 127.0.0.1:53 0.0.0.0:* users:(("unbound",pid=1234,fd=5))
tcp LISTEN 0 128 127.0.0.1:53 0.0.0.0:* users:(("unbound",pid=1234,fd=6))
Qué significa la salida: UDP y TCP 53 en 127.0.0.1. TCP importa para respuestas grandes y comportamiento de fallback.
Decisión: Si ves 0.0.0.0:53 inesperadamente, podrías haber expuesto tu resolver. Arregla interfaces/access-control antes de continuar.
Tarea 2: Confirma qué resolutor usa tu host (y si es un symlink)
cr0x@server:~$ ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 39 Jan 1 10:00 /etc/resolv.conf -> /run/systemd/resolve/stub-resolv.conf
Qué significa la salida: El stub de systemd-resolved está en juego.
Decisión: Decide: deshabilitar resolved (Opción A), o configurarlo para usar Unbound upstream (Opción B). No te quedes a la deriva.
Tarea 3: Comprueba los upstreams actuales de systemd-resolved
cr0x@server:~$ resolvectl status
Global
LLMNR setting: yes
MulticastDNS setting: no
DNSOverTLS setting: no
DNSSEC setting: allow-downgrade
DNSSEC supported: yes
Current DNS Server: 127.0.0.1
DNS Servers: 127.0.0.1
...output...
Qué significa la salida: resolved está reenviando a 127.0.0.1 (Unbound). Eso es coherente solo si Unbound no reenvía de vuelta a resolved.
Decisión: Si el Current DNS Server es 127.0.0.53 o algún servidor DHCP obsoleto que no confías, arregla la configuración de resolved o tu gestor de red.
Tarea 4: Validar la sintaxis de la configuración de Unbound (prevención de fallos rápidos)
cr0x@server:~$ sudo unbound-checkconf /etc/unbound/unbound.conf
unbound-checkconf: no errors in /etc/unbound/unbound.conf
Qué significa la salida: La sintaxis está bien. Si imprime un error, señalará la línea exacta.
Decisión: Nunca recargues/reinicies Unbound en producción sin un paso unbound-checkconf en la automatización. Los humanos cometen errores. DNS no perdona.
Tarea 5: Revisar estadísticas en vivo de Unbound (¿realmente está sirviendo?)
cr0x@server:~$ sudo unbound-control status
version: 1.17.1
verbosity: 1
threads: 1
modules: 2 [ subnetcache validator iterator ]
uptime: 320 seconds
options: control(ssl)
unbound-control statistics not enabled
Qué significa la salida: El control funciona, pero las estadísticas no están habilitadas por defecto en algunas compilaciones.
Decisión: Si quieres observabilidad, habilita stats y remote-control explícitamente (sección posterior). Si el control falla, arregla permisos/certs antes de depender de él.
Tarea 6: Revisar logs por bucle de resolutor u timeouts upstream
cr0x@server:~$ sudo journalctl -u unbound -n 50 --no-pager
...output...
unbound[1234]: info: resolving example.com. A IN
unbound[1234]: info: response for example.com. A IN
...output...
Qué significa la salida: Quieres “resolving” seguido de “response”. Repetidos “timed out” o patrones “SERVFAIL” son tu humo temprano.
Decisión: Si dominan los timeouts, sospecha la ruta de red, firewall o MTU/fragmentación. Si predominan los SERVFAIL, sospecha DNSSEC o bucle.
Tarea 7: Confirma que el host usa Unbound vía la ruta libc (no solo dig)
cr0x@server:~$ getent ahosts example.com | head
93.184.216.34 STREAM example.com
93.184.216.34 DGRAM example.com
93.184.216.34 RAW example.com
Qué significa la salida: El resolvedor de libc está obteniendo una respuesta usable. Esto detecta casos donde dig funciona pero el resolvedor del sistema no (extrañezas de NSS, dominios de búsqueda, etc.).
Decisión: Si getent falla pero dig funciona, probablemente tienes un problema de NSS/nsswitch o resolv.conf, no Unbound.
Tarea 8: Comprobar señales de acierto de caché midiendo búsquedas repetidas
cr0x@server:~$ for i in 1 2 3; do dig @127.0.0.1 www.cloudflare.com A +noall +stats | grep -E 'Query time|SERVER'; done
;; Query time: 24 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; Query time: 1 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; Query time: 1 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
Qué significa la salida: Comportamiento frío → caliente de la caché es visible. Si siempre es lento, no estás cacheando eficazmente.
Decisión: Si siempre es lento, revisa si deshabilitaste prefetch, el tamaño de caché es demasiado pequeño, o estás reenviando a un upstream que deshabilita la semántica de caché (raro pero ocurre con gateways “inteligentes”).
Tarea 9: Detectar fallos relacionados con DNSSEC rápidamente
cr0x@server:~$ dig @127.0.0.1 www.iana.org A +dnssec +noall +comments | head -n 5
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4242
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
...output...
Qué significa la salida: La bandera ad indica datos validados (cuando está disponible). Si nunca ves ad, la validación podría no estar funcionando, o el cliente no la solicita/impresión no la muestra correctamente.
Decisión: Si requieres validación y no ves AD, inspecciona trust anchors, sincronización de tiempo y si accidentalmente deshabilitaste el módulo validador.
Tarea 10: Comprobar sincronización horaria (dependencia silenciosa de DNSSEC)
cr0x@server:~$ timedatectl status
Local time: Wed 2025-12-31 10:00:00 UTC
Universal time: Wed 2025-12-31 10:00:00 UTC
RTC time: Wed 2025-12-31 10:00:01
System clock synchronized: yes
NTP service: active
...output...
Qué significa la salida: La validación DNSSEC puede fallar si tu reloj está lo suficientemente desajustado como para que las firmas parezcan expiradas o no válidas aún.
Decisión: Si el reloj no está sincronizado, arregla NTP antes de culpar a Unbound o a los resolutores upstream.
Tarea 11: Inspeccionar reglas de firewall para DNS (UDP y TCP)
cr0x@server:~$ sudo iptables -S | grep -E 'dport 53|sport 53' || true
-A OUTPUT -p udp -m udp --dport 53 -j ACCEPT
-A OUTPUT -p tcp -m tcp --dport 53 -j ACCEPT
Qué significa la salida: DNS saliente está permitido. Si TCP está bloqueado, verás fallos intermitentes extraños cuando las respuestas no caben en UDP.
Decisión: Permite TCP/53 saliente. Bloquearlo es una política clásica de “funciona hasta que no funciona”.
Tarea 12: Confirmar alcanzabilidad de upstreams (si reenvías)
cr0x@server:~$ dig @1.1.1.1 example.com A +time=2 +tries=1 +noall +stats
;; Query time: 18 msec
;; SERVER: 1.1.1.1#53(1.1.1.1)
...output...
Qué significa la salida: Si tu forwarder elegido es lento/inalcanzable desde este host, Unbound se verá mal incluso si es inocente.
Decisión: Elige forwarders que sean alcanzables, de baja latencia y compatibles con la política (especialmente en redes corporativas con DNS dividido).
Tarea 13: Detectar un bucle de resolutor con observación de paquetes
cr0x@server:~$ sudo tcpdump -ni lo port 53 -c 10
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes
10:00:01.000000 IP 127.0.0.1.55555 > 127.0.0.1.53: 1234+ A? example.com. (28)
10:00:01.000100 IP 127.0.0.1.53 > 127.0.0.1.55555: 1234* 1/0/0 A 93.184.216.34 (44)
...output...
Qué significa la salida: Existe tráfico DNS por loopback (bueno para resolver local). Si ves consultas repetidas sin respuestas, o tráfico rebotando entre 127.0.0.1 y 127.0.0.53 en patrones, construiste el bucle.
Decisión: Si la evidencia sugiere un bucle, deja de adivinar y arregla la cadena. Un resolutor debe ser “la última parada”.
Tarea 14: Comprobar que el archivo de trust anchor raíz de Unbound existe
cr0x@server:~$ sudo ls -l /var/lib/unbound/root.key
-rw-r--r-- 1 root root 1091 Jan 1 10:00 /var/lib/unbound/root.key
Qué significa la salida: Archivo de trust anchor presente. Si falta, la validación se comportará de forma impredecible o fallará.
Decisión: Si falta, instala el paquete que lo provee (a menudo comportamiento de unbound-anchor o hooks específicos de la distro) o genera anchors correctamente, luego reinicia.
Reenvío vs recursión completa: elige con intención
Unbound puede hacer recursión completa, o puede reenviar todo (o zonas específicas) a resolutores upstream. No hay pureza moral aquí. Hay compensaciones.
Recursión completa: autonomía, menos dependencias, más superficie
Con recursión completa, Unbound consulta los servidores raíz y baja por la jerarquía. Beneficios:
- Menos dependencia del comportamiento de resolutores upstream.
- Política de caché y validación predecible.
- A menudo mayor resiliencia si un proveedor upstream tiene un mal día.
Costes:
- Más diversidad de tráfico (muchos servidores autoritativos), lo que puede activar políticas de egreso restrictivas.
- Más exposición a peculiaridades de MTU y “DNS bloqueado salvo a nuestros resolutores corporativos”.
- Más responsabilidad: posees todo el camino de recursión.
Reenvío: simplicidad, cumplimiento y realidad corporativa
El reenvío es común en empresas: Unbound cachea localmente pero envía misses a un resolutor recursivo conocido (interno, ISP, pila de seguridad). A menudo es la elección correcta cuando:
- Necesitas DNS dividido (split-horizon) interno y no puedes replicar las zonas en otro lugar.
- Tienes filtrado o registro obligatorio aguas arriba.
- El egreso está restringido a unas pocas IPs de resolutores.
Configuración de reenvío segura (y cómo no reenviar contra tu propia cara)
Añade un archivo de forward zone. Usa IPs explícitas. No resolv.conf. No 127.0.0.53.
cr0x@server:~$ sudo tee /etc/unbound/unbound.conf.d/forwarders.conf >/dev/null <<'EOF'
forward-zone:
name: "."
forward-addr: 1.1.1.1
forward-addr: 1.0.0.1
EOF
...output...
cr0x@server:~$ sudo unbound-checkconf
unbound-checkconf: no errors in /etc/unbound/unbound.conf
cr0x@server:~$ sudo systemctl restart unbound
...output...
Decisión: Si estás en una red corporativa con zonas internas, probablemente quieras reenviar a resolutores corporativos (no públicos), además de reenvíos explícitos para sufijos internos si hacen falta.
Rendimiento y ajuste de fiabilidad que no te morderá después
La mayoría de instalaciones de Unbound no necesitan ajustes heroicos. DNS es liviano comparado con bases de datos. Los errores vienen de “optimizar” sin un modelo, y de tratar DNS como si fuera sin estado.
Hilos y descriptores de archivo
Si ejecutas un resolver central que sirve muchos clientes, considera múltiples hilos y asegúrate de que los límites del sistema no estén ajustados. En un caché local por host, un hilo es suficiente.
Comprobaciones típicas:
cr0x@server:~$ systemctl show unbound -p LimitNOFILE
LimitNOFILE=1048576
Decisión: Si LimitNOFILE es pequeño (como 1024) en un resolver con mucha carga, súbelo mediante un override de systemd. Si ya es alto, no lo toques solo porque puedes.
edns-buffer-size: la solución silenciosa para fallos “aleatorios”
Pusimos edns-buffer-size: 1232 por una razón: reduce el riesgo de fragmentación en rutas modernas. Esto es particularmente útil en entornos con VPNs, túneles o firewalls que manejan mal los fragmentos.
Cuando esto importa, los síntomas son molestos: algunos dominios funcionan, otros timeout, y reintentos misteriosamente tienen éxito sobre TCP a veces. DNSSEC empeora porque las respuestas son mayores.
Prefetch: bueno cuando tienes tráfico repetido, malo cuando no
prefetch: yes significa que Unbound puede refrescar registros populares antes de que expiren. Esto suaviza la latencia en la cola para nombres calientes. Pero si tienes un resolver que atiende una gran variedad de nombres de una sola vez (piensa en dominios de tracking, ad-tech), prefetch puede generar carga extra hacia upstream.
No lo sobrepienses; mide. Si el tráfico upstream se dispara y no tienes conjuntos calientes estables, desactívalo.
Logging: verbosidad no es observabilidad
Unbound puede registrar mucho. En producción, “mucho” se convierte en “ruido y facturas”. Manténlo bajo por defecto. Cuando necesites detalle, sube temporalmente.
Control de acceso: evita convertirte en parte del botnet de otro
Si enlazas Unbound a una IP de LAN, debes configurar correctamente access-control. Un resolver recursivo abierto será abusado para ataques de amplificación. No quieres que tu servidor DNS protagonice el informe de incidentes de otra organización.
Chiste #2 (corto, relevante): Ejecutar un resolver abierto es una gran forma de hacer nuevos amigos en internet. Desafortunadamente, solo vienen cuando necesitan ancho de banda.
Tres mini-historias corporativas (realistas, anonimizadas)
Incidente #1: la suposición equivocada (la falacia “resolv.conf dice la verdad”)
Una compañía SaaS mediana quería despliegues más rápidos. Sus runners de CI hacían mucho DNS: tirar contenedores, descargar artefactos, llamar APIs internas. Alguien sugirió “simplemente instala Unbound en los runners para caché”. Razonable.
El ingeniero escribió un rol de Ansible que instaló Unbound y luego creó una configuración de forwarder apuntando al resolutor encontrado en /etc/resolv.conf. Esto pretendía “respetar la configuración local de red”. En papel, muy cortés. En realidad, muchos runners tenían /etc/resolv.conf enlazado al stub de systemd-resolved en 127.0.0.53.
Por separado, otro cambio de automatización había configurado systemd-resolved para usar 127.0.0.1 para “usar la caché local”. Nadie conectó los dos cambios porque aterrizaron semanas aparte y eran de equipos diferentes.
El incidente fue sutil al principio. Los trabajos de CI empezaron a timeoutear en pasos “aleatorios”: a veces docker pulls, a veces un curl a una API interna, a veces incluso apt. Unas pocas re-ejecuciones tenían éxito. La cola creció; los ingenieros culparon al sistema de builds.
Lo que finalmente lo rompió fue una captura de paquetes en loopback mostrando peticiones DNS rebotando entre 127.0.0.1 y 127.0.0.53 con IDs repetidos. Bucle de resolutores. La solución fue aburrida: dejar de reenviar basado en resolv.conf, configurar upstreams explícitos y documentar una única cadena de resolutores soportada. Post-incidente, la nueva regla fue: “resolv.conf no es fuente de la verdad; es una salida de política”.
Incidente #2: una optimización que salió mal (caché agresiva y apagones sorpresa)
Un equipo de plataforma empresarial ejecutaba Unbound como un cluster de resolutores centrales. Estaban orgullosos de sus gráficas de rendimiento DNS. Un trimestre decidieron “reducir la carga upstream” aumentando dramáticamente cache-max-ttl y poniendo cache-min-ttl a un valor no cero para que los registros se mantuvieran calientes incluso si los TTL autoritativos eran pequeños.
En días, un equipo descendente rotó un conjunto de IPs de servicio detrás de un nombre DNS como parte de un despliegue blue/green. El TTL autoritativo era intencionalmente bajo. El cluster de resolutores, ahora aplicando un TTL mínimo más alto, siguió sirviendo IPs obsoletas. El tráfico fluyó hacia nodos drenados. El despliegue pareció una caída parcial.
El equipo de plataforma argumentó que “DNS no debería usarse para balanceo de carga de todos modos”. No estaban equivocados en principio, pero sí en la práctica: la organización ya lo hacía, y el cambio en el resolutor silenciosamente cambió el significado del TTL. Eso no es una optimización; es un cambio semántico en una dependencia central.
La solución fue doble: revertir cache-min-ttl a 0, y crear una lista de excepciones de política solo para dominios donde controlaban todo el ciclo de vida y podían aceptar obsolescencia. También añadieron un ítem de revisión previa al cambio: “¿Este cambio alterará el comportamiento de TTL?” Te sorprendería cuántos incidentes comienzan porque alguien olvidó que DNS es parte del contrato.
Incidente #3: la práctica correcta y aburrida que salvó el día (staging + canary + rollback rápido)
Una compañía de servicios financieros ejecutaba un par de resolutores Unbound por datacenter, fronted por anycast en cada sitio. Su DNS era intencionalmente aburrido: mínima personalización, forwarders explícitos para zonas internas, recursión para el resto y controles de acceso estrictos.
Un martes, un equipo de red desplegó una actualización de política de firewall. Bloqueó involuntariamente TCP/53 saliente desde la VLAN de resolutores, dejando UDP/53 permitido. La mayoría de consultas continuaron funcionando. Entonces empezaron las fallas: ciertos dominios (especialmente firmados con DNSSEC y con respuestas grandes) empezaron a timeoutear. Aplicaciones que dependían de esos dominios fallaron en maneras que parecían “problemas de handshake TLS” o “timeouts aleatorios de API”.
Lo que los salvó: tenían un resolutor canario en cada sitio por una ruta de política separada, y rutinariamente corrían una pequeña suite de comprobaciones DNS contra él (incluyendo consultas DNS forzadas por TCP). El canario se encendió en minutos. Pudieron decir, con confianza, “esto es transporte DNS, no aplicación”.
El rollback fue limpio porque su configuración de Unbound se gestionaba con un enfoque de versiones simple, y su equipo de firewall tenía un play probado para revertir. El impacto en servicio se mantuvo contenido. Nadie recibió una medalla de héroe. Ese es el punto.
Guion rápido de diagnóstico
Cuando DNS está “lento” o “caído”, no tienes tiempo para filosofía. Necesitas una secuencia ajustada que encuentre el cuello de botella rápido.
Primero: confirma con quién estás hablando
- Comprueba
/etc/resolv.confy si es un symlink. - Ejecuta
digexplícitamente contra Unbound (@127.0.0.1). - Compara
dig example.com(por defecto) vsdig @127.0.0.1 example.com.
Si las consultas por defecto son lentas pero las directas a Unbound son rápidas, el problema es la ruta de resolutor del sistema (resolved/NSS/VPN), no Unbound.
Segundo: determina si es upstream, DNSSEC o transporte
- Revisa logs de Unbound por patrones de timeouts vs SERVFAIL.
- Prueba dominios conocidos con
+dnssecy buscaado SERVFAIL esperado en un dominio mal firmado. - Fuerza TCP con
+tcppara ver si la fragmentación UDP está implicada.
Si TCP funciona y UDP falla intermitentemente, sospecha MTU/firewall y manejo de fragmentos. Si SERVFAIL aparece mayormente en dominios firmados, sospecha DNSSEC/tiempo/trust anchor.
Tercero: verifica recursos y saturación
- Comprueba si Unbound está limitado por CPU (load, CPU del proceso) o por descriptores de archivo.
- Busca pérdida de paquetes y retransmisiones en la interfaz de egress del resolutor.
- Comprueba la tasa de acierto de caché vía
unbound-control stats_noreset(si está habilitado).
Si no tienes estadísticas habilitadas, habilítalas antes del próximo incidente. “Añadiremos observabilidad después” es cómo el después se convierte en nunca.
Errores comunes: síntomas → causa raíz → solución
1) Síntoma: SERVFAIL en la mayoría de consultas, a veces funciona al reintentar
Causa raíz: Bucle de resolutores (Unbound reenviando al stub de systemd, que reenvía de vuelta), o timeouts upstream.
Solución: Haz la cadena unidireccional. Usa forwarders explícitos en Unbound o deshabilita systemd-resolved. Confirma con readlink -f /etc/resolv.conf y tcpdump -ni lo port 53.
2) Síntoma: Algunos dominios fallan consistentemente; otros están bien
Causa raíz: Fallos de validación DNSSEC, problemas MTU/fragmentación o TCP/53 bloqueado.
Solución: Prueba con dig +dnssec y dig +tcp. Configura edns-buffer-size: 1232. Asegura que TCP/53 saliente esté permitido. Revisa sincronización horaria.
3) Síntoma: DNS “funciona” en el host del resolutor, pero los clientes no pueden usarlo
Causa raíz: Unbound enlazado solo a 127.0.0.1; falta access-control para subredes cliente; firewall bloquea inbound 53.
Solución: Añade interface: 0.0.0.0 o una IP LAN específica, añade access-control: 10.0.0.0/8 allow (o tu subred) y permite UDP/TCP 53 entrante en la interfaz.
4) Síntoma: Picos de latencia altos cada pocos minutos
Causa raíz: Caché demasiado pequeña, comportamiento de prefetch causando picos, rate limiting upstream o pérdida de paquetes.
Solución: Habilita stats e inspecciona hit/miss de caché. Si reenvías, prueba latencia upstream directamente. Considera deshabilitar prefetch si tu conjunto de consultas es extremadamente diverso.
5) Síntoma: Dominios internos fallan, dominios públicos funcionan
Causa raíz: Elegiste recursión completa pero tus zonas internas solo son resolubles vía resolutores corporativos (split-horizon). O reenviás “.” a resolutores públicos que no ven nombres internos.
Solución: Añade reenvíos por zona para sufijos internos hacia servidores DNS corporativos. Mantén recursión o reenvío para el resto según convenga.
6) Síntoma: Todo se rompe al habilitar validación DNSSEC
Causa raíz: Desajuste de reloj, trust anchor faltante o DNSSEC roto en la cadena upstream (común con algunos middleboxes y forwarders antiguos).
Solución: Arregla NTP/tiempo. Verifica /var/lib/unbound/root.key. Si reenvías a upstreams que mutilan DNSSEC, cambia upstream o deshabilita validación para zonas internas específicas en lugar de globalmente.
7) Síntoma: NXDOMAIN aleatorio para nombres que deberían existir
Causa raíz: Manejo incorrecto de dominios de búsqueda, confusión con DNS dividido o caché negativa tras una falla upstream transitoria.
Solución: Inspecciona dominios de búsqueda del resolutor e intenta nombres totalmente calificados con punto final en dig. Considera bajar cache-max-negative-ttl si los NXDOMAIN transitorios te dañan (pero no lo pongas a cero a menos que disfrutes la carga upstream).
Listas de verificación / plan paso a paso
Plan A: Resolver caché en localhost en un servidor único (versión 15 minutos)
- Instala
unboundydnsutils. - Configura Unbound para escuchar solo en 127.0.0.1.
- Habilita DNSSEC, configura
edns-buffer-size: 1232. - Inicia Unbound y confirma que escucha en 127.0.0.1:53 (UDP+TCP).
- Elige: deshabilitar systemd-resolved (más simple) o configurarlo para reenviar a Unbound.
- Verifica con
dig @127.0.0.1ygetent. - Ejecuta la comprobación de bucle: asegúrate de que Unbound no reenvíe a 127.0.0.53.
Plan B: Resolver central para una subred (haz esto si quieres que otras máquinas lo usen)
- Enlaza Unbound a una IP LAN (no 0.0.0.0 a menos que lo quieras).
- Añade entradas
access-controlpara subredes de clientes. - Firewall: permite UDP/TCP 53 entrante desde esas subredes; permite UDP/TCP 53 saliente hacia upstreams o internet (si haces recursión).
- Decide recursión vs reenvío; configura forwarders explícitos si es necesario.
- Prueba canaria desde un cliente:
dig @resolver-ip example.com. - Añade un segundo resolver para redundancia; apunta clientes a ambos.
- Instrumenta: habilita estadísticas de Unbound; asegúrate de que los logs sean sensatos y con limitación de tasa.
Plan C: Gestión de cambios que prevenga incidentes DNS
- Documenta explícitamente la cadena de resolutores: client → stub (opcional) → Unbound → upstream.
- Añade validación de configuración al desplegar (
unbound-checkconfdebe pasar). - Comprobaciones canario: incluye un dominio de prueba DNSSEC y una consulta forzada por TCP.
- Tener un play de rollback: revertir configuración, reiniciar, confirmar escucha y éxito de consultas.
Preguntas frecuentes
1) ¿Debo ejecutar Unbound en cada host o centralmente?
Si quieres la historia de fiabilidad más simple para una flota, ejecuta un pequeño pool central y apunta los clientes a él. Si quieres la mejor latencia por host y resiliencia contra oscilaciones de red, ejecútalo localmente. El híbrido está bien si documentas la cadena y evitas bucles.
2) ¿Unbound es mejor que systemd-resolved?
Resuelven problemas distintos. systemd-resolved es un stub resolver con lógica por enlace; Unbound es un verdadero resolver recursivo con caché. Usa resolved para la plomería de red y Unbound para política/caché/validación DNS. Solo no los dejes persiguiéndose en círculos.
3) ¿Necesito validación DNSSEC?
Si puedes mantener la sincronización horaria sólida y tu red no mutila DNS, la validación es una buena red de seguridad. Si tu entorno incluye middleboxes inestables o DNS interno roto, acótala con cuidado o intercambiarás seguridad por caídas.
4) ¿Por qué necesito TCP/53 si DNS es “UDP”?
Porque existen respuestas grandes (DNSSEC, TXT grandes, conjuntos NS grandes), y la truncación fuerza fallback a TCP. Bloquear TCP/53 crea fallos intermitentes, específicos de dominios, que parecen fantasmas.
5) ¿Cuál es la forma más segura de configurar forwarders?
Usa IPs explícitas en forward-zone. No “reenvíes a resolv.conf”. resolv.conf suele ser un stub, y los stubs son la manera en que ocurren los bucles.
6) ¿Puede Unbound servir también como DNS autoritativo?
Puede manejar algunos local-data, pero no es un servidor autoritativo en el sentido de NSD o BIND en modo autoritativo. Úsalo principalmente como resolver y mantén el DNS autoritativo separado, salvo que hagas un hack intencional pequeño (como sobrescribir algunos nombres).
7) ¿Cómo sé que la caché realmente funciona?
Ejecuta la misma consulta dig dos veces contra Unbound y compara Query time. Para prueba más profunda, habilita estadísticas y observa métricas de hit de caché. Además verifica que los clientes realmente envían consultas a Unbound y no lo saltan.
8) ¿Cuál es un buen TTL de caché negativa?
Cinco minutos (cache-max-negative-ttl: 300) es un valor pragmático. Redúcelo si los NXDOMAIN transitorios te perjudican. Valores más altos pueden hacer que errores y fallos upstream persistan más de lo deseado.
9) Habilité Unbound, pero el split DNS de la VPN dejó de funcionar. ¿Por qué?
Muchos clientes VPN se integran con systemd-resolved para instalar rutas DNS por dominio o por interfaz. Si deshabilitas resolved (Opción A), puedes perder ese comportamiento. Usa Opción B (stub de resolved + Unbound upstream) o gestiona el DNS dividido explícitamente en Unbound con forward zones.
10) ¿Cómo evito convertirme en un resolver abierto?
Enlaza solo a las interfaces necesarias y configura access-control para permitir solo tus redes cliente. No lo expongas a Internet público. Verifica con ss que no estás escuchando en 0.0.0.0 a menos que sea intencionado.
Siguientes pasos que realmente deberías hacer
Puedes poner Unbound en marcha rápidamente. Ponerlo en marcha correctamente es la parte que te ahorra incidentes futuros.
- Asegura la cadena de resolutores: decide si systemd-resolved está en el camino y hazla unidireccional. Sin bucles.
- Verifica con evidencia:
dig @127.0.0.1dos veces para caché,+dnssecpara validación,+tcppara saneamiento de transporte. - Elige recursión vs reenvío intencionalmente: las redes corporativas suelen querer reenvío (al menos para zonas internas).
- Añade una comprobación canario: un test DNSSEC, una consulta forzada por TCP y un nombre interno. Ejecútalo de forma continua.
- Mantén la configuración aburrida: evita sobrescribir TTLs y “optimizar” solo después de medir.
DNS es una dependencia que no puedes evitar. La buena noticia es que puedes hacerlo predecible. Unbound es una de las pocas herramientas en este espacio que se comporta como si quisiera que durmieras por la noche.