Instalación de AdGuard Home y Unbound (1)
AdGuard Home es un programa que actúa como DNS y que nos permitirá bloquear anuncios, rastreadores o malware a nivel de red, esto significa que cualquier dispositivo conectado a nuestra red se podrá beneficiar de este plus de privacidad.
Por otro lado, Unbound es un servidor DNS recursivo y validante lo que quiere decir que puede responder a las solicitudes DNS desde su caché y, en caso de no tenerla almacenada, pregunta directamente a los servidores raíz de internet.
Entiendo que te pueda parecer un batiburrillo de términos pero aquí te dejo una explicación (creo que) sencilla de como sería el funcionamiento de ambos programas:
(Clica para desplegar)
- Desde un equipo quieres acceder a masto.es. La navegación a través de internet no entiende de nombre, solo de IP's, así que necesita saber en qué IP se encuentra y esto se lo pregunta al DNS.
- Esta pregunta la recibe AdGuard que revisa las listas de bloqueo configuradas por si se encuentra en alguna de ellas.
- En caso de estar bloqueado, le devuelve al navegador una IP falsa como 0.0.0.0 y ahí acabaría la navegación, con un bonito mensaje de error.
- En caso de no estar bloqueado, AdGuard le reenvía la consulta a Unbound que la recibe y si tiene la respuesta en su caché entrega la IP a AdGuard que se la entrega al navegador y accederíamos a la página.
- Si Unbound no tiene la respuesta en su caché, reenvía la consulta a uno de los 13 servidores raíz de DNS para saber quien tiene autoridad sobre los dominios ".es".
- Alguno de estos servidores le envía la respuesta a Unbound. Ahora le pregunta a ese servidor autoritativo de los dominios ".es" que quién es autoritativo del dominio "masto.es".
- Cuando recibe esta respuesta es cuando le pregunta al servidor autoritativo de "masto.es" cual es la dirección IP correspondiente. Cuando recibe la respuesta la valida y la guarda en su caché para agilizar futuras consultas a la misma dirección.
- Ahora es cuando le responde a AdGuard con la dirección en la que se encuentra masto.es.
- AdGuard registra la consulta, devuelve la dirección al navegador y accederíamos correctamente a la página web.
Aunque se puede instalar perfectamente a través de un contenedor de Docker, en este caso me gusta realizar la instalación sobre el propio equipo directamente. Este equipo puede ser un ordenador, un minipc o una raspberry.
En esta primera parte vamos a centrarnos en instalar y configurar Unbound:
sudo apt install unbound
Ahora nos situamos en la carpeta donde se encuentran los archivos de configuración de Unbound y creamos un nuevo archivo de configuración con el nombre que queramos. Debe acabar en .conf. eso sí (en este ejemplo voy a usar genscorp):
cd /etc/unbound/unbound.conf.d/
sudo nano genscorp.conf
Y añadimos la siguiente configuración al archivo:
server:
# If no logfile is specified, syslog is used
# logfile: "/var/log/unbound/unbound.log"
verbosity: 0
interface: 127.0.0.1
port: 5335
do-ip4: yes
do-udp: yes
do-tcp: yes
# May be set to yes if you have IPv6 connectivity
do-ip6: no
# You want to leave this to no unless you have *native* IPv6. With 6to4 and
# Terredo tunnels your web browser should favor IPv4 for the same reasons
prefer-ip6: no
# Maximum number of queries per second
ratelimit: 5000
# Defend against and print warning when reaching unwanted reply limit.
unwanted-reply-threshold: 10000
# Use this only when you downloaded the list of primary root servers!
# If you use the default dns-root-data package, unbound will find it automatically
root-hints: "/var/lib/unbound/root.hints"
# Trust glue only if it is within the server's authority
harden-glue: yes
# Require DNSSEC data for trust-anchored zones, if such data is absent, the zone becomes BOGUS
harden-dnssec-stripped: yes
harden-algo-downgrade: yes
harden-large-queries: yes
harden-short-bufsize: yes
# Don't use Capitalization randomization as it known to cause DNSSEC issues sometimes
# see https://discourse.pi-hole.net/t/unbound-stubby-or-dnscrypt-proxy/9378 for further details
use-caps-for-id: no
rrset-roundrobin: yes
qname-minimisation: yes
minimal-responses: yes
hide-identity: yes
identity: "Server"
hide-version: yes
# Reduce EDNS reassembly buffer size.
# IP fragmentation is unreliable on the Internet today, and can cause
# transmission failures when large DNS messages are sent via UDP. Even
# when fragmentation does work, it may not be secure; it is theoretically
# possible to spoof parts of a fragmented DNS message, without easy
# detection at the receiving end. Recently, there was an excellent study
# >>> Defragmenting DNS - Determining the optimal maximum UDP response size for DNS <<<
# by Axel Koolhaas, and Tjeerd Slokker (https://indico.dns-oarc.net/event/36/contributions/776/)
# in collaboration with NLnet Labs explored DNS using real world data from the
# the RIPE Atlas probes and the researchers suggested different values for
# IPv4 and IPv6 and in different scenarios. They advise that servers should
# be configured to limit DNS messages sent over UDP to a size that will not
# trigger fragmentation on typical network links. DNS servers can switch
# from UDP to TCP when a DNS response is too big to fit in this limited
# buffer size. This value has also been suggested in DNS Flag Day 2020.
edns-buffer-size: 1232
# Perform prefetching of close to expired message cache entries
# This only applies to domains that have been frequently queried
prefetch: yes
# One thread should be sufficient, can be increased on beefy machines. In reality for most users running on small networks or on a single machine, it should be unnecessary to seek performance enhancement by increasing num-threads above 1.
num-threads: 4
# Ensure kernel buffer is large enough to not lose messages in traffic spikes
so-rcvbuf: 4m
so-sndbuf: 4m
# Time To Live and Cache
cache-max-ttl: 86400
cache-min-ttl: 300
serve-expired: yes
neg-cache-size: 4M
prefetch-key: yes
msg-cache-size: 50m
rrset-cache-size: 100m
module-config: "validator iterator"
# Ensure privacy of local IP ranges
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: 172.16.0.0/12
private-address: 10.0.0.0/8
private-address: fd00::/8
private-address: fe80::/10
Puedes ver aquí cada uno de los puntos de este archivo de configuración:
(Clica para desplegar)
- logfile: si está descomentado, guarda los logs en el archivo indicado. Si no, usa syslog (el sistema de registro del sistema operativo). Lo dejamos comentado.
- verbosity: nivel de detalle de los logs:
0: Solo errores
1: Información operativa
2: Información operativa detallada, incluyendo información breve por consulta
3: Información a nivel de consulta y salida por consulta
4: Información a nivel de algoritmo
5: Registra la identificación del cliente para fallos de caché
- interface: dirección IP donde Unbound escucha.
Que solo escuche direcciones locales. - port: puerto en el que escucha. Le ponemos 5335 porque el 53 será usado por AdGuard
- do-ip4/do-ip6: habilitan IPv4 o IPv6. Aquí solo IPv4.
- do-udp/do-tcp: habilitan transporte UDP (rápido, por defecto en DNS) y TCP (para respuestas grandes o fallos UDP).
- prefer-ip6: si hay IPv6 disponible, decide si usarlo preferentemente. Desactivado.
- ratelimit: máximo de consultas por segundo para evitar abusos o ataques DDoS.
- unwanted-reply-threshold: si se llega al número de respuestas indeseadas establecido, se registra el incidente y se vacía la caché para evitar su envenenamiento.
- root-hints: archivo con las direcciones IP de los servidores raíz del DNS.
- harden-glue: solo acepta glue records (información adicional en respuestas DNS) si pertenecen al dominio autorizado, lo que hace que se eviten inyecciones o respuestas falsas.
- harden-dnssec-stripped: si una zona debería tener DNSSEC pero la respuesta viene sin firmas, se marca como falsa.
- harden-algo-downgrade: evita ataques que intentan forzar algoritmos de firma más débiles en DNSSEC.
- harden-large-queries / harden-short-bufsize: si las consultas son demasiado largas o demasiado cortas, las ignora.
- use-caps-for-id: no usa capitalización aleatoria en los nombres de dominio (técnica de validación antigua que puede causar problemas con DNSSEC).
- rrset-roundrobin: alterna el orden de respuestas con múltiples registros A/AAAA. Su propósito es que, cuando un dominio tiene varias direcciones (por ejemplo, varios registros A o AAAA), Unbound cambie su orden en cada respuesta, distribuyendo la carga entre servidores o IPs de manera equilibrada.
- qname-minimisation: envía solo la parte necesaria del nombre en cada consulta (protege la privacidad al evitar revelar dominios completos a cada servidor de consulta).
- minimal-responses: Unbound no inserta las secciones de authority (autoridad) ni additional (adicional) en los mensajes de respuesta cuando dichas secciones no son necesarias.
- hide-identity / hide-version: ocultan información del servidor en las respuestas. Pone un texto genérico en caso de que se consulte.
- edns-buffer-size: Define el tamaño máximo de los mensajes UDP DNS antes de pasar a TCP.
1232 bytes es recomendado para evitar fragmentación en Internet moderna (según estudios de NLnet Labs y DNS Flag Day 2020).
- prefetch: renueva en segundo plano las entradas de caché que están por expirar, si son consultadas con frecuencia.
- num-threads: número de hilos de ejecución de Unbound para poder procesar consultas en paralelo.
4 puede aprovechar CPUs multinúcleo, aunque en equipos pequeños 1 suele bastar.
Estas opciones serían las recomendadas
- Red doméstica: 1 hilo
- De 50 a 200 clientes: 2 hilos
- Servidor DNS público: 4–8 hilos, según CPU
- so-rcvbuf / so-sndbuf: tamaño de buffers de socket del kernel para evitar pérdida de paquetes en picos de tráfico.
- cache-max-ttl / cache-min-ttl: límites máximo (1 día) y mínimo (5 min) de vida de registros en caché.
- serve-expired: permite servir respuestas caducadas mientras se actualizan (mejora disponibilidad).
- neg-cache-size: tamaño de caché para respuestas negativas (dominios inexistentes).
- prefetch-key: pre-valida claves DNSSEC antes de expirar (menos latencia).
- msg-cache-size / rrset-cache-size: tamaño de caché de mensajes y conjuntos de registros.
- module-config: orden de módulos internos de Unbound:
- Validator: valida DNSSEC.
- Iterator: realiza las consultas recursivas.
- Listado de IP's privadas: Estas redes privadas no deben ser enviadas ni anunciadas externamente.
Evita que direcciones internas aparezcan en respuestas públicas, reforzando privacidad y seguridad.
Guardamos el archivo y ya podemos iniciar el servicio de Unbound:
sudo systemctl start unbound
Revisando el estado del servicio con:
sudo systemctl status unbound
Me he encontrado con un par de alertas:
warning: so-rcvbuf 4194304 was not granted. Got 360448. To fix: start with root permissions(linux) or sysctl bigger net.core.rmem_max(linux) or kern.ipc.maxsockbuf(bsd) values.
warning: so-sndbuf 4194304 was not granted. Got 360448. To fix: start with root permissions(linux) or sysctl bigger net.core.wmem_max(linux) or kern.ipc.maxsockbuf(bsd) values.
Si estás siguiendo esta guía y te aparece algo similar, yo lo solucioné con estos comandos (adáptalo al número necesario en caso de que hayas modificado el tamaño de las cachés):
sudo sysctl -w net.core.rmem_max=4194304
sudo sysctl -w net.core.wmem_max=4194304
Otro posible problema es que Unbound no añada automáticamente el archivo root.hints que indica los servidores raíz DNS.
En este caso podemos descargarlo directamente con:
wget -S https://www.internic.net/domain/named.cache -O /var/lib/unbound/root.hints
O crear directamente el archivo root.hints en la ubicación /var/lib/unbound/root.hints con el siguiente contenido:
; This file holds the information on root name servers needed to
; initialize cache of Internet domain name servers
; (e.g. reference this file in the "cache . <file>"
; configuration file of BIND domain name servers).
;
; This file is made available by InterNIC
; under anonymous FTP as
; file /domain/named.cache
; on server FTP.INTERNIC.NET
; -OR- RS.INTERNIC.NET
;
; last update: October 14, 2025
; related version of root zone: 2025101401
;
; FORMERLY NS.INTERNIC.NET
;
. 3600000 NS A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET. 3600000 A 198.41.0.4
A.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
. 3600000 NS B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET. 3600000 A 170.247.170.2
B.ROOT-SERVERS.NET. 3600000 AAAA 2801:1b8:10::b
;
; FORMERLY C.PSI.NET
;
. 3600000 NS C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET. 3600000 A 192.33.4.12
C.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2::c
;
; FORMERLY TERP.UMD.EDU
;
. 3600000 NS D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET. 3600000 A 199.7.91.13
D.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2d::d
;
; FORMERLY NS.NASA.GOV
;
. 3600000 NS E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET. 3600000 A 192.203.230.10
E.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:a8::e
;
; FORMERLY NS.ISC.ORG
;
. 3600000 NS F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET. 3600000 A 192.5.5.241
F.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:2f::f
;
; FORMERLY NS.NIC.DDN.MIL
;
. 3600000 NS G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET. 3600000 A 192.112.36.4
G.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:12::d0d
;
; FORMERLY AOS.ARL.ARMY.MIL
;
. 3600000 NS H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET. 3600000 A 198.97.190.53
H.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:1::53
;
; FORMERLY NIC.NORDU.NET
;
. 3600000 NS I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET. 3600000 A 192.36.148.17
I.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fe::53
;
; OPERATED BY VERISIGN, INC.
;
. 3600000 NS J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET. 3600000 A 192.58.128.30
J.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:c27::2:30
;
; OPERATED BY RIPE NCC
;
. 3600000 NS K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET. 3600000 A 193.0.14.129
K.ROOT-SERVERS.NET. 3600000 AAAA 2001:7fd::1
;
; OPERATED BY ICANN
;
. 3600000 NS L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET. 3600000 A 199.7.83.42
L.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:9f::42
;
; OPERATED BY WIDE
;
. 3600000 NS M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET. 3600000 A 202.12.27.33
M.ROOT-SERVERS.NET. 3600000 AAAA 2001:dc3::35
; End of file
Reiniciamos el servicio para que se apliquen las correcciones:
sudo systemctl restart unbound
Ya tenemos configurado y en funcionamiento Unbound. Podemos hacer un par de pruebas para comprobar el funcionamiento correcto incluyendo el DNSSEC con:
dig @127.0.0.1 -p 5335 dnssec-tools.org
Lo que nos debería arrojar un resultado de NOERROR:
; <<>> DiG 9.18.39-0ubuntu0.24.04.1-Ubuntu <<>> @127.0.0.1 -p 5335 dnssec-tools.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46610
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;dnssec-tools.org. IN A
;; ANSWER SECTION:
dnssec-tools.org. 300 IN A 185.199.111.153
dnssec-tools.org. 300 IN A 185.199.108.153
dnssec-tools.org. 300 IN A 185.199.109.153
dnssec-tools.org. 300 IN A 185.199.110.153
;; Query time: 386 msec
;; SERVER: 127.0.0.1#5335(127.0.0.1) (UDP)
;; WHEN: Sun Oct 19 11:11:12 UTC 2025
;; MSG SIZE rcvd: 109
O en un dominio erroneo:
dig @127.0.0.1 -p 5335 dnssec-failed.org
Para que nos arroje un resultado de SERVFAILED:
; <<>> DiG 9.18.39-0ubuntu0.24.04.1-Ubuntu <<>> @127.0.0.1 -p 5335 dnssec-failed.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 58079
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;dnssec-failed.org. IN A
;; Query time: 1094 msec
;; SERVER: 127.0.0.1#5335(127.0.0.1) (UDP)
;; WHEN: Sun Oct 19 11:11:29 UTC 2025
;; MSG SIZE rcvd: 46
Hasta aquí la instalación de Unbound. Próximamente veremos la instalación y configuración de AdGuard Home para tener nuestro sistema DNS completo.