Unbound es un resolvedor DNS recursivo, validador y con caché, desarrollado por NLnet Labs. En lugar de enviar tus consultas a un resolvedor de terceros, Unbound las resuelve él mismo desde los servidores raíz, valida las firmas DNSSEC y guarda las respuestas en caché. En este tutorial lo instalarás en Ubuntu 24.04 como resolvedor para el propio servidor y para las máquinas de tu red privada, con nombres internos propios y, opcionalmente, reenvío cifrado por DNS-over-TLS.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 512 MB de RAM.
- Un usuario no root con privilegios
sudo. - Si otras máquinas van a usar el resolvedor: una red privada entre ellas. En los ejemplos la red es
10.0.0.0/24y el servidor Unbound tiene la IP privada10.0.0.5; sustitúyelas por las tuyas.
Advertencianunca expongas un resolvedor recursivo a todo Internet. Un resolvedor abierto se usa para ataques de amplificación DNS. En esta guía Unbound solo escucha en localhost y en la IP privada, y solo acepta consultas de tu red.
Paso 1: Instalar Unbound
Instala Unbound y las utilidades dig:
sudo apt update
sudo apt install unbound dnsutils
Comprueba la versión y el estado del servicio:
unbound -V | head -n 1
systemctl status unbound --no-pager
Version 1.19.2
...
Active: active (running)
Con la configuración por defecto de Ubuntu, Unbound escucha solo en 127.0.0.1 y ::1, por lo que no choca con systemd-resolved, que usa 127.0.0.53. El paquete ya incluye dos ficheros en /etc/unbound/unbound.conf.d/:
root-auto-trust-anchor-file.conf: activa la validación DNSSEC con el ancla de confianza de la raíz, que el servicio actualiza automáticamente en cada arranque.remote-control.conf: habilitaunbound-controla través de un socket local, sin certificados.
Haz una primera consulta para confirmar que resuelve:
dig @127.0.0.1 cubepath.com +short
Debe devolver una o varias IP.
Paso 2: Configurar el resolvedor
Crea tu configuración en un fichero aparte dentro de unbound.conf.d, así las actualizaciones del paquete no la sobrescriben:
sudo nano /etc/unbound/unbound.conf.d/resolver.conf
server:
# Direcciones en las que escucha (localhost y la IP privada)
interface: 127.0.0.1
interface: ::1
interface: 10.0.0.5
# Permite arrancar aunque la IP privada aún no esté configurada
ip-freebind: yes
# Quién puede hacer consultas. Todo lo demás se rechaza.
access-control: 127.0.0.0/8 allow
access-control: ::1/128 allow
access-control: 10.0.0.0/24 allow
# Un hilo por núcleo de CPU
num-threads: 2
# Caché: rrset-cache-size debe ser el doble de msg-cache-size
msg-cache-size: 64m
rrset-cache-size: 128m
# Renueva las entradas populares antes de que caduquen
prefetch: yes
prefetch-key: yes
# No revela versión ni identidad a quien pregunte
hide-identity: yes
hide-version: yes
# Protección contra DNS rebinding: una respuesta de Internet
# no puede apuntar a direcciones privadas
private-address: 10.0.0.0/8
private-address: 172.16.0.0/12
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: fd00::/8
private-address: fe80::/10
Ajusta num-threads al número de núcleos (nproc) y el tamaño de la caché a la RAM disponible. Valida la sintaxis y reinicia:
sudo unbound-checkconf
sudo systemctl restart unbound
unbound-checkconf: no errors in /etc/unbound/unbound.conf
Comprueba que Unbound escucha en las tres direcciones:
sudo ss -lnup 'sport = :53'
Deben aparecer 127.0.0.1:53, [::1]:53 y 10.0.0.5:53 asociados al proceso unbound, además de 127.0.0.53 de systemd-resolved.
Paso 3: Permitir las consultas de la red privada
Si UFW está activo, permite el puerto 53 solo desde la red privada:
sudo ufw allow from 10.0.0.0/24 to any port 53
sudo ufw status
Desde otra máquina de la red, consulta al resolvedor:
dig @10.0.0.5 cubepath.com +short
Si obtienes status: REFUSED, la IP del cliente no está cubierta por ninguna línea access-control.
Paso 4: Comprobar la validación DNSSEC
Un dominio firmado correctamente debe devolver el flag ad (authenticated data):
dig @127.0.0.1 isc.org SOA +dnssec | grep flags
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
Y un dominio con la firma rota a propósito debe fallar:
dig @127.0.0.1 dnssec-failed.org | grep status
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 41253
Ese SERVFAIL es el comportamiento correcto: Unbound se niega a entregar una respuesta que no puede validar.
Paso 5: Usar Unbound como resolvedor del servidor
systemd-resolved sigue siendo el resolvedor del sistema y reenvía a los DNS que recibe por DHCP. Para que el propio servidor use Unbound, indícaselo con un fichero adicional:
sudo mkdir -p /etc/systemd/resolved.conf.d
sudo nano /etc/systemd/resolved.conf.d/unbound.conf
[Resolve]
DNS=127.0.0.1
Domains=~.
Domains=~. hace que todas las consultas vayan a ese servidor y no a los DNS de la interfaz. Aplica y verifica:
sudo systemctl restart systemd-resolved
resolvectl status | grep 'DNS Server'
Current DNS Server: 127.0.0.1
DNS Servers: 127.0.0.1
Repite este mismo fichero en los demás servidores de la red, cambiando 127.0.0.1 por 10.0.0.5.
Paso 6: Añadir nombres internos
Unbound puede responder por nombres que solo existen en tu red, sin montar un servidor autoritativo. Usa el dominio .internal, reservado para este uso. Crea un fichero para ellos:
sudo nano /etc/unbound/unbound.conf.d/local-zones.conf
server:
local-zone: "internal." static
local-data: "web.internal. 3600 IN A 10.0.0.10"
local-data: "db.internal. 3600 IN A 10.0.0.20"
local-data-ptr: "10.0.0.10 web.internal"
local-data-ptr: "10.0.0.20 db.internal"
# Bloquear un dominio concreto
local-zone: "ads.example.com." always_nxdomain
El tipo static responde con los datos definidos y devuelve NXDOMAIN para cualquier otro nombre bajo internal., sin preguntar a Internet. Aplica y comprueba:
sudo unbound-checkconf
sudo systemctl restart unbound
dig @127.0.0.1 web.internal +short
dig @127.0.0.1 -x 10.0.0.10 +short
10.0.0.10
web.internal.
Paso 7 (opcional): Reenviar por DNS-over-TLS
Por defecto Unbound resuelve de forma recursiva y sus consultas salen sin cifrar hacia los servidores autoritativos. Si prefieres que todo el tráfico salga cifrado hacia un resolvedor de confianza, reenvía por DNS-over-TLS. A cambio, ese proveedor verá tus consultas.
sudo nano /etc/unbound/unbound.conf.d/forward-tls.conf
server:
tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
forward-zone:
name: "."
forward-tls-upstream: yes
forward-addr: 1.1.1.1@853#cloudflare-dns.com
forward-addr: 1.0.0.1@853#cloudflare-dns.com
forward-addr: 9.9.9.9@853#dns.quad9.net
forward-addr: 149.112.112.112@853#dns.quad9.net
El nombre tras # es el que Unbound comprueba en el certificado del servidor, y tls-cert-bundle le indica dónde están las CA del sistema. Sin esa línea no verifica el certificado. Aplica y comprueba:
sudo unbound-checkconf
sudo systemctl restart unbound
sudo unbound-control list_forwards
dig @127.0.0.1 cubepath.com +short
list_forwards debe mostrar la zona . con las cuatro direcciones configuradas, y dig debe seguir devolviendo IP.
La validación DNSSEC sigue funcionando con reenvío: repite las pruebas del paso 4.
Paso 8: Consultar estadísticas y vaciar la caché
unbound-control permite ver el estado del resolvedor sin reiniciarlo. Muestra las estadísticas principales sin ponerlas a cero:
sudo unbound-control stats_noreset | grep -E '^total\.num\.(queries|cachehits|cachemiss)='
total.num.queries=1532
total.num.cachehits=1204
total.num.cachemiss=328
Para forzar que un dominio se vuelva a resolver (por ejemplo, tras cambiar sus registros):
sudo unbound-control flush_zone your_domain
Para ver por dónde resolvería Unbound un nombre:
sudo unbound-control lookup cubepath.com
Solución de problemas
- Unbound no arranca con "can't bind socket: Address already in use": has puesto
interface: 0.0.0.0y choca consystemd-resolved. Usa direcciones concretas como en el paso 2. status: REFUSEDdesde un cliente: falta su red enaccess-control.status: SERVFAILen todos los dominios: suele ser la hora del sistema (DNSSEC depende de ella) o que el servidor no llega a Internet por el puerto 53. Compruebatimedatectlydig @1.1.1.1 cubepath.com.- Depurar una consulta concreta: sube temporalmente el nivel de log con
sudo unbound-control verbosity 3, reproduce el fallo, revisasudo journalctl -u unbound -n 100y vuelve asudo unbound-control verbosity 1.
Conclusión
Unbound ya resuelve DNS para el servidor y para tu red privada, valida DNSSEC, responde por los nombres internos de .internal y, si lo activaste, cifra el tráfico de salida con DNS-over-TLS. Como siguientes pasos puedes ofrecer DNS-over-TLS y DNS-over-HTTPS a tus propios clientes con Unbound, o combinarlo con Pi-hole o AdGuard Home como resolvedor de origen para bloquear publicidad sin depender de un proveedor externo.
