El log que te describe mejor que tu historial del navegador
Antes de que tu máquina pueda enviar un solo byte cifrado a un sitio web, tiene que preguntarle a alguien dónde vive ese sitio. Esa pregunta viaja en claro, hacia un resolver que casi con toda seguridad no elegiste tú, y la respuesta a quién la conserva decide cuánto de tu vida queda por escrito. Un historial de navegador es una lista de páginas que decidiste conservar. El log de un resolver lo es todo: cada sitio, cada app que comprueba si hay actualizaciones, cada servicio en la nube con el que habla tu teléfono mientras duermes, cada dominio de cada correo que abriste, en orden, con marca de tiempo, hubiera o no un humano mirando la pantalla.
Lee una semana de eso y puedes reconstruir a una persona. El banco que usa, la aerolínea que acaba de reservar, la farmacia, la app de citas, el sistema de seguimiento de candidatos del reclutador, la hora a la que se despierta y la hora a la que se detiene. Nada de eso requiere romper ninguna clase de cifrado. Los nombres por sí solos lo revelan, y los nombres son la única parte de la transacción que, en la mayoría de las configuraciones, sigue entregándose a un tercero por diseño.
Hoy ese tercero es quienquiera que te haya asignado tu concesión DHCP — tu ISP, tu empresa, la cafetería. La gente que se da cuenta de esto suele cambiarse a un resolver público, lo cual es una mejora real en integridad y un movimiento lateral en privacidad: no has eliminado al observador, has cambiado de qué empresa se trata, cambiando una teleco regulada por una CDN cercana a la publicidad o una organización sin ánimo de lucro cuya política podría cambiar con su financiación. Las buenas publican políticas de retención honestas y algunas de verdad las cumplen. Pero una política es una promesa sobre el comportamiento, y un archivo de configuración es una afirmación sobre la capacidad. No puedes auditar una promesa. Puedes auditar veinte líneas que escribiste tú mismo, en una máquina que alquilas, en un país que elegiste.
SP·02El reenvío traslada el log. La recursión lo fragmenta.
Casi todo tutorial que dice "monta tu propio servidor DNS" termina construyendo un forwarder: una pequeña caché en tu red que reenvía a 1.1.1.1 o 9.9.9.9 cualquier cosa que no conoce. Es algo genuinamente útil — es rápido, son cinco líneas, y evita que tu red local te vigile. También deja el log agregado exactamente donde estaba. Cada nombre que buscas sigue llegando a una sola empresa, ahora convenientemente pre-etiquetado con la única dirección IP de tu resolver, lo cual hace que el flujo sea más fácil de atribuir, no más difícil.
Un resolver recursivo hace el trabajo en lugar de delegarlo. Al preguntar por news.example.io, consulta a un servidor raíz quién gestiona .io, después consulta a los servidores de .io quién gestiona example.io, y solo entonces le pregunta directamente a ese operador. Tres conversaciones con tres partes no relacionadas entre sí, ninguna de las cuales se dedica a la agregación, y — esta es la parte que importa — ninguna de ellas ve jamás tu flujo completo de consultas. Los operadores de la raíz ven un goteo de peticiones de TLD. Verisign ve que alguien en tu dirección tocó algo bajo .com. El servidor autoritativo de un dominio ve el tráfico que de todos modos iba a ver, porque estás a punto de conectarte a él en cualquier caso.
La minimización de QNAME agudiza esto de forma considerable, y el Unbound moderno la aplica por defecto. En lugar de enviar el nombre completo a cada servidor de la cadena — que es lo que hicieron los resolvers durante treinta años — envía a cada servidor solo la etiqueta que necesita para responder: io. a la raíz, example.io. a los servidores de .io, y solo entonces el news.example.io. completo al operador que tiene derecho a conocerlo. La jerarquía deja de ser una emisión pública de tus intenciones y se convierte en lo que siempre se dibujó que era: una delegación.
El compromiso es real y merece la pena decirlo sin rodeos. Una consulta recursiva en frío da varios saltos de ida y vuelta donde un forwarder da uno solo, así que la primera visita a un dominio desconocido es perceptiblemente más lenta. Heredas la responsabilidad de una caché que antes era problema de otro. Y ya no te beneficias de una caché compartida, calentada por millones de otras personas. A cambio, el registro completo, ordenado y con marca de tiempo de lo que buscaste deja de existir en cualquier sitio que no sea tu propio disco.
SP·03Lo que esto oculta, y lo que claramente no oculta
El alcance honesto es más estrecho de lo que sugiere el marketing en torno al DNS privado, y conocer esos límites es lo que te impide tomar una mala decisión apoyado solo en una buena sensación.
Elimina una cosa: el log agregado de nombres que mantiene un único observador. Eso es mucho, porque el agregado es lo que tiene valor comercial y lo que se solicita a granel. No es todo.
No oculta la conexión. Después de que la consulta se resuelve, tu dispositivo sigue abriendo una sesión hacia esa dirección, y cualquiera que observe tu enlace de subida ve la IP de destino. Para un sitio en infraestructura dedicada, la IP es la identidad. Tampoco oculta el hostname en el cable: a menos que ambos extremos admitan Encrypted Client Hello, el handshake de TLS sigue llevando el nombre del servidor en texto plano, que es la misma información que habría tenido tu resolver.
No oculta las consultas frente a tu proveedor de hosting. Este es el punto que la gente suele entender mal, así que merece decirse sin rodeos: el tráfico saliente de tu resolver — las preguntas que hace a la raíz, al TLD y a los servidores autoritativos — sale del VPS por el puerto UDP 53, sin cifrar, y la red en la que vive tu servidor puede leerlo todo. No hay DoT hacia la raíz. No has eliminado al observador tanto como lo has trasladado: de un ISP de consumo que vende datos y responde a citaciones judiciales en tu propio país, a una red de hosting en una jurisdicción que elegiste a propósito, que ve un flujo fragmentado gracias a la minimización de QNAME. Eso es una mejora genuina, y es un argumento sobre qué ley rige el cable, no algo que un archivo de configuración pueda resolver.
Y no te da una multitud. Un resolver usado por un solo hogar atribuye cada consulta a ese hogar sin ninguna ambigüedad. Frente a un adversario pasivo global, un resolver compartido y concurrido es, de verdad, un mejor escondite. Frente a tu ISP, tu empresa, los data brokers que compran telemetría de resolvers, y las solicitudes masivas rutinarias que de verdad le ocurren a la gente corriente, el tuyo es mejor — siempre que el último salto desde tus dispositivos esté dentro de un túnel. Usa esto junto con tu propio túnel WireGuard, no en su lugar. Por sí solo, un resolver privado sobre todo reubica tus metadatos. Detrás de un túnel, cierra el único canal que el túnel deja abierto.
SP·04El error que convierte tu resolver en el arma de otro
Hay exactamente una forma de equivocarse gravemente con esto, es fácil hacerlo por accidente, y las consecuencias caen sobre desconocidos antes de caer sobre ti.
Un resolver que responde a cualquiera es un resolver abierto, y un resolver abierto es un amplificador. El DNS corre sobre UDP, las direcciones de origen UDP se falsifican con trivial facilidad, y una consulta pequeña puede producir una respuesta muchas veces mayor. Un atacante le envía a tu máquina una pregunta de 60 bytes con la dirección de una víctima falsificada como remitente; tu máquina, obedientemente, le envía a la víctima una respuesta varias docenas de veces mayor. Haz eso desde unos pocos miles de resolvers abiertos a la vez y la víctima queda fuera de internet, tras recibir una inundación que parece — con precisión, a nivel de paquete — venir de ti. Tú no eres el objetivo. Tú eres el arma, y el tráfico del informe del incidente es tuyo.
Lo que viene después no tiene nada de glamuroso: informes de abuso de redes de las que nunca has oído hablar, un proveedor que aplica un null route a tu dirección para proteger su propio tránsito, y una conversación sobre tu cuenta que preferirías no tener. Terminas en el extremo causante exactamente del tipo de evento descrito en nuestro runbook de la primera hora ante un DDoS, y no existe ninguna versión de esa historia en la que tu uptime sobreviva.
Dos cerrojos independientes lo evitan, y quieres los dos, porque cada uno cubre el fallo del otro. El primero es el propio access-control de Unbound, que debería refuse a todo internet en ambas familias de IP y después allow explícitamente el loopback y la subred de tu túnel — una lista de denegación por defecto, no una lista de permisos con una cola permisiva. El segundo es dónde escucha el daemon siquiera: vincúlalo a 127.0.0.1 y a la dirección del túnel, nunca a 0.0.0.0, y mantén el puerto 53 cerrado en la interfaz pública a nivel de firewall.
La trampa es hacer solo lo primero. Una regla de access-control no hace que el puerto desaparezca; una consulta rechazada sigue siendo un paquete recibido y un paquete enviado, tu dirección sigue apareciendo en los escaneos que mapean resolvers abiertos, y una edición posterior al bloque equivocado convierte un rechazo en una respuesta. Vincular el servicio a esas direcciones y cerrarlo en el firewall convierten el error en algo estructuralmente imposible, en lugar de dejarlo a una línea de configuración de distancia. Si la máquina también corre contenedores, vuelve a leer cómo Docker publica puertos rodeando tu firewall antes de asumir que la regla que escribiste es la regla que está en vigor.
Los valores por defecto de Unbound son sensatos. No son privados.
Unbound se entrega afinado para la corrección y la estabilidad, que es el valor por defecto correcto para un software que en su mayoría despliegan ISPs. Un puñado de ajustes lo convierten en algo pensado para quien lo administra. Ninguno es exótico; simplemente vienen apagados, o sin una postura definida, de fábrica.
Deja de responder preguntas sobre ti mismo. Por defecto, un resolver informa alegremente de la versión de su software y su hostname a través de la clase CHAOS — version.bind y hostname.bind — lo cual es reconocimiento gratuito para cualquiera que esté decidiendo si tu máquina merece atención. hide-identity y hide-version no cuestan nada y eliminan una huella identificable.
Falla cerrado, no abierto. La validación DNSSEC es la diferencia entre un resolver que detecta una respuesta falsificada y uno que la sirve. harden-dnssec-stripped se niega a aceptar una respuesta sin firmar para una zona que debería estar firmada, harden-glue y harden-below-nxdomain cierran dos vías clásicas de envenenamiento de caché, y aggressive-nsec permite que el resolver responda nombres inexistentes directamente desde los registros de denegación en caché, en lugar de volver a preguntar. use-caps-for-id añade una capitalización aleatoria como entropía adicional contra el spoofing ciego — barato, y en ocasiones incompatible con un servidor autoritativo mal construido, lo cual conviene saber antes de perder una tarde con un dominio que no resuelve.
No escribas nada. Unbound no registra las consultas a menos que se le pida, pero los ajustes que harían eso están a una sola línea sin comentar de distancia, y algunos paquetes de distribución vienen con un valor por defecto más locuaz. Fija verbosity: 0 y declara log-queries: no explícitamente, para que la intención sea visible en el archivo en lugar de inferirse de su ausencia. Luego recuerda la parte que no es un ajuste: la propia caché es un registro. unbound-control dump_cache, en una máquina en marcha, imprime un historial reciente de lo que esa máquina ha buscado, y vive en la memoria de una máquina que otras manos pueden alcanzar. Se expira por sí sola, que es la razón por la que los tiempos de vida cortos en caché y la privacidad están en ligera tensión, y es la razón para no mantener un resolver vivo durante meses en un host en el que no confías en absoluto.
Almacena en caché deliberadamente. Cada respuesta servida desde la caché es una observación que nunca llega a producirse aguas arriba, así que una caché saludable es una característica de privacidad, y no solo de velocidad. prefetch renueva los registros populares antes de que caduquen, así que el caso común deja de tocar la red por completo; serve-expired te mantiene en línea cuando un servidor autoritativo está brevemente inalcanzable. Subir cache-min-ttl reduce todavía más la charla aguas arriba, pero anula decisiones deliberadas de los operadores de dominios — los TTL bajos son cómo funcionan las CDN y el failover — así que un mínimo de uno o dos minutos es razonable, y una hora acabará dejándote varado en una dirección muerta.
Dos formas de entrar: el túnel, o DNS sobre TLS
Tus dispositivos tienen que llegar al resolver de alguna manera, y la elección entre las dos opciones sensatas es sobre todo una cuestión de qué estás dispuesto a publicar.
El túnel es la mejor respuesta para casi todo el mundo. Si tu portátil y tu teléfono ya mantienen una sesión WireGuard con ese servidor, el resolver puede escuchar en la dirección del túnel y hablar DNS normal por el puerto 53. El tráfico ya está cifrado y ya autenticado por el túnel, así que no hay ningún certificado que obtener, ningún puerto nuevo abierto a internet, ninguna pila TLS expuesta a desconocidos y — la parte que se subestima — ningún hostname en ninguna parte. La guía de WireGuard de este sitio deja a los clientes apuntando a DNS = 9.9.9.9 precisamente porque todavía no había nada mejor que poner ahí. Esto es lo que va en esa línea en su lugar: la dirección del túnel de tu propio servidor.
DNS sobre TLS es para el dispositivo que no puede mantener un túnel. El campo de DNS privado de Android es el mejor argumento a su favor — se aplica a todo el sistema, sobrevive a los reinicios, y cubre apps a las que no puedes llegar de otro modo. El coste es un hostname con un certificado válido, y un certificado significa una entrada pública y permanente en los registros de Certificate Transparency que vincula ese nombre al momento en que lo creaste. El DNS pasivo atará después ese nombre a la dirección del servidor. Si el objetivo del ejercicio era mantener tu nombre fuera de la infraestructura, usa un hostname que no lleve a nada cercano a ti, y regístralo con el cuidado descrito en registrar un dominio de forma anónima — no un subdominio del dominio que usas para todo lo demás, que ligaría los dos de forma permanente.
Hay un segundo coste, más sutil. Un endpoint DoT al que puedan llegar teléfonos en itinerancia no se puede restringir por dirección de origen, así que es, por definición, un resolver que cualquier desconocido puede usar si averigua el nombre. No es un amplificador — TLS sobre TCP exige un handshake completo, así que el origen no se puede falsificar y no hay nada que reflejar — pero es capacidad que estás regalando, y un servicio que merece la pena escanear en busca de él. Mantén activos los límites de tasa por IP, elige un hostname que nadie vaya a adivinar, y trátalo como una excepción deliberada para uno o dos dispositivos, no como la puerta por defecto.
DNS sobre HTTPS es una tercera opción, y normalmente la equivocada aquí. Necesita un servidor web delante del resolver, lo cual son más piezas en movimiento y una superficie de ataque mayor por el único beneficio de ser indistinguible del tráfico web. Ese beneficio es decisivo si estás esquivando una red que bloquea DoT, e irrelevante si no es tu caso.
SP·07Bloquear es un producto distinto; decide antes de añadirlo a la ligera
Tarde o temprano alguien sugiere añadir blocklists, y el argumento de venta es genuinamente atractivo: filtrar en el resolver cubre todos los dispositivos del túnel, incluida la smart TV y las apps del teléfono a las que ninguna extensión puede llegar. En móvil, en particular, es el único lugar práctico donde intervenir. Es también la funcionalidad con más probabilidades de hacer que desconfíes en silencio de tu propia infraestructura, y merece la pena entender por qué antes de que pase, no mientras pasa.
El primer coste es que los fallos no parecen fallos. Un resolver roto se anuncia a sí mismo; un dominio bloqueado se presenta como un botón de pago que no hace nada, una app atascada en una rueda de carga, un correo que nunca llega. El síntoma aparece semanas después de que instalaste la lista, en un dispositivo en el que no estabas pensando, y nada lo conecta con una decisión de DNS que tomaste otro mes. Cada blocklist que instalas es una política escrita por un desconocido y aplicada en silencio sobre tu hogar. Si añades una, apunta que lo hiciste, mantenla pequeña y de confianza, y conserva una forma de desactivarla con un solo comando — el primer paso de depuración para cualquier cosa inexplicada en tu red se convierte en "saltarse el filtro y reintentar", y ese paso tiene que ser barato.
El segundo coste es que no hace lo que la gente espera. Una aplicación con la dirección del resolver fijada en el código, o con su propio cliente DoH incluido, nunca le pregunta nada a tu resolver; abre una conexión a una IP fijada en el código, y se acabó. Los navegadores cada vez más resuelven por su propio DoH salvo que se les indique lo contrario. El bloqueo de DNS es una capa de higiene que elimina mucho ruido de rastreo y publicidad de bajo esfuerzo, y no es un control de seguridad, porque cualquier cosa hostil lo rodea por diseño.
Si de todas formas la quieres, prefiere una pequeña zona local en Unbound antes que un segundo daemon — local-zone: "tracker.example." always_nxdomain no necesita software adicional, ninguna interfaz web en un puerto que después tengas que defender, ni ningún servicio nuevo que pueda caerse y arrastrar contigo la resolución de nombres. Mantén la descripción del trabajo de la máquina en una sola línea: resuelve nombres. Cada responsabilidad adicional que le das es una forma más de que todo deje de funcionar a la vez.
Un resolver que administras tú es una dependencia tuya
Cuando se cae un sitio web que alojas, algunas personas no pueden leer algo. Cuando se cae tu resolver, no funciona nada — ni el navegador, ni el correo, ni el gestor de paquetes, ni la app que iba a avisarte de que el servidor está caído. Es el servicio con más carga que puedes poner en una máquina barata, y falla de formas que no parecen DNS.
Merece la pena conocer de antemano los modos de fallo realistas, porque cada uno tiene una firma distinta. El VPS se reinicia y Unbound nunca se activó al arranque, así que todo funciona hasta el primer reinicio no planeado. Una blocklist grande empuja la caché al swap en una instancia de 1 GB, y el OOM killer elige al resolver. Una zona en algún lugar rompe sus propias firmas DNSSEC, y tu resolver correctamente configurado rechaza la respuesta mientras todo el mundo con un resolver que no valida sigue navegando — tu máquina tiene razón, y aun así el sitio te parece roto, lo cual son cinco minutos confusos si te has olvidado de que tú validas. O el túnel se cae, y como DNS = 10.66.0.1 solo existe dentro del túnel, el dispositivo no tiene ningún resolver en absoluto y reporta que está sin conexión.
Las mitigaciones son afortunadamente baratas. Activa el servicio al arranque y pruébalo de verdad con un reinicio, en lugar de asumirlo. Dales a los clientes un resolver secundario para que un túnel muerto degrade el servicio en lugar de detenerlo — un resolver público en esa ranura es un compromiso de privacidad pequeño y explícito que solo se aplica mientras el tuyo es inalcanzable, y suele ser el cambio correcto. Mantén activado serve-expired para que una interrupción breve aguas arriba no se convierta en tu propia interrupción. Comprueba el resolver desde otro sitio, no desde la propia máquina, que es la única forma de notar la diferencia entre "caído" e "inalcanzable".
Y conserva una copia de la configuración. Todo el asunto son unas pocas docenas de líneas que te costó una tarde dejar bien, y que no recordarás dentro de un año; pertenece en tus copias de seguridad cifradas fuera de sitio, junto a las claves de WireGuard, para que reconstruirlo sean veinte minutos y no una segunda tarde. Ese es el resumen honesto de todo este ejercicio: una máquina pequeña haciendo un solo trabajo, por el precio del plan más barato de la lista, sustituyendo una promesa sobre retención por un arreglo en el que el registro sencillamente nunca llega a crearse.
SP·09Paso a paso
-
01
Empieza desde una máquina que ya esté bien asegurada
Un resolver es un servicio pequeño y silencioso, lo cual tienta a instalarlo en lo primero que haya a mano. No lo hagas — esta máquina va a ver cada nombre que busque cada dispositivo de tu túnel, así que merece el mismo trato que cualquier otra cosa que guarde secretos. Despliega el plan más pequeño que quieras; 1 GB de RAM es de sobra para un hogar, ya que los tamaños de caché de más abajo se miden en decenas de megabytes. Repasa la primera hora tras el despliegue antes de nada: un usuario con nombre, SSH solo por clave, denegación por defecto en ambas familias de IP, actualizaciones de seguridad desatendidas.
Después instala Unbound. El paquete de la distribución incluye los root hints y el ancla de confianza raíz de DNSSEC, y configura la rotación automática del ancla, que es uno de los pocos casos en los que la versión empaquetada de verdad te ahorra una clase entera de problemas futuros.
sudo apt update && sudo apt install -y unbound dnsutils unbound -V | head -n 3 # the packaged trust anchor the resolver will validate against sudo ls -l /var/lib/unbound/root.key
Si falta
root.key, tu paquete no ejecutóunbound-anchor, y la validación fallará cerrada en todo. Genéralo una vez consudo -u unbound unbound-anchor -a /var/lib/unbound/root.keyantes de continuar. -
02
Recupera el puerto 53 de systemd-resolved
En la mayoría de las distribuciones actuales, algo ya es dueño del puerto 53:
systemd-resolvedejecuta un listener stub en127.0.0.53, y/etc/resolv.confes un symlink que apunta a él. Unbound se negará a arrancar, o arrancará sin vincular nada útil, hasta que eso se resuelva. Mira antes de editar.ss -ulpn 'sport = :53' ls -l /etc/resolv.conf
Lee este párrafo antes de ejecutar el siguiente bloque: entre desactivar el stub y arrancar Unbound, esta máquina no tiene ningún DNS funcional en absoluto. Termina los pasos 3 y 4 de una sola sentada, y no lances antes una operación de
apten otra ventana — se quedará colgada en una búsqueda de nombre, y la vas a diagnosticar mal, pensando que es el resolver que todavía no has arrancado.# stop resolved from holding the port (appends inside the [Resolve] section) printf 'DNSStubListener=no\n' | sudo tee -a /etc/systemd/resolved.conf sudo systemctl restart systemd-resolved # point the host at the resolver it is about to run sudo rm -f /etc/resolv.conf printf 'nameserver 127.0.0.1\noptions edns0 trust-ad\n' | sudo tee /etc/resolv.conf # the port should now be free ss -ulpn 'sport = :53'
-
03
Escribe una configuración que recursa y olvida
Deja intacta la configuración empaquetada y añade tu propio archivo en el directorio drop-in, para que una actualización del paquete nunca revierta tus decisiones en silencio. Cada línea de abajo trata sobre rechazar a desconocidos, recursar desde la raíz, o no guardar registros. Sustituye
10.66.0.1y10.66.0.0/24por la dirección y la subred del túnel de tu propio servidor WireGuard si son distintas.sudo tee /etc/unbound/unbound.conf.d/private-resolver.conf > /dev/null <<'EOF' server: # listen for the host itself and for the tunnel — never on 0.0.0.0 interface: 127.0.0.1 interface: 10.66.0.1 port: 53 # default-deny: refuse the internet, then allow what you trust access-control: 0.0.0.0/0 refuse access-control: ::/0 refuse access-control: 127.0.0.0/8 allow access-control: 10.66.0.0/24 allow # recurse from the root and send each server only the label it needs qname-minimisation: yes harden-dnssec-stripped: yes harden-below-nxdomain: yes harden-glue: yes aggressive-nsec: yes use-caps-for-id: yes # answer nothing about the software or the host hide-identity: yes hide-version: yes # keep no query log, and say little to the journal verbosity: 0 log-queries: no log-replies: no # a warm cache is an upstream observation that never happens cache-min-ttl: 120 cache-max-ttl: 86400 prefetch: yes prefetch-key: yes serve-expired: yes # belt and braces if a rule above is ever loosened ratelimit: 1000 ip-ratelimit: 100 # sizing for a small instance num-threads: 2 so-reuseport: yes msg-cache-size: 32m rrset-cache-size: 64m EOFFíjate en lo que no está en el archivo: no hay ningún
forward-zone. Su ausencia es lo que convierte esto en un resolver recursivo en lugar de una caché delante de la de otro. Si más adelante pegas un fragmento de un tutorial que añade uno, has deshecho en silencio todo el sentido del ejercicio. -
04
Arráncalo, y después demuestra que DNSSEC valida de verdad
Comprueba la sintaxis antes de reiniciar nada — en una máquina cuyo propio
resolv.confahora apunta a Unbound, un error de configuración significa cero resolución de nombres mientras lo depuras.sudo unbound-checkconf sudo systemctl enable --now unbound systemctl --no-pager status unbound | head -n 5
Ahora verifica los dos comportamientos que importan, porque un resolver que responde no es lo mismo que un resolver que valida. Un nombre firmado debe volver con el flag
ad— datos autenticados — y un nombre con firmas rotas a propósito debe fallar cerrado conSERVFAILen lugar de resolver.# should show: flags: qr rd ra ad dig @127.0.0.1 example.com +dnssec | grep -E '^;; flags' # should show: status: SERVFAIL (not NOERROR, not an address) dig @127.0.0.1 dnssec-failed.org | grep -E '^;; ->>HEADER' # and a normal name should simply work dig @127.0.0.1 +short cloudflare.com
Si la zona rota resuelve a una dirección, la validación no está funcionando: comprueba que
/var/lib/unbound/root.keyexiste y es legible por el usuariounbound. Si todo esSERVFAIL, la causa habitual es un reloj que está muy desincronizado — las firmas tienen ventanas de validez, y una máquina desincronizada varias horas rechaza todo internet. -
05
Cierra la puerta pública, abre solo el túnel
La configuración ya rechaza a los desconocidos. Este paso hace que, para empezar, no haya nada que un desconocido pueda alcanzar — el segundo de los dos cerrojos, y el que sobrevive a una futura edición del primero.
# DNS is reachable from the tunnel interface only sudo ufw allow in on wg0 to any port 53 proto udp sudo ufw allow in on wg0 to any port 53 proto tcp sudo ufw status verbose
Después verifícalo de la única forma que cuenta, desde una máquina distinta. Probarlo desde el propio servidor no demuestra absolutamente nada — el loopback está permitido a propósito. Ejecuta esto desde tu portátil con el túnel caído, o desde cualquier otro host que tengas.
# must time out. an answer here means you are running an open resolver. dig @SERVER_PUBLIC_IP example.com +time=3 +tries=1 # same question over IPv6, which is the half people forget dig -6 @SERVER_IPV6 example.com +time=3 +tries=1
Si cualquiera de las dos devuelve una respuesta, detente y arréglalo ahora, no después del informe de abuso. Las causas habituales son un
interface: 0.0.0.0que quedó en un archivo de configuración empaquetado, una regla de firewall que permite el 53 globalmente desde un experimento anterior, o Docker publicando el puerto de un contenedor que se saltaufwpor completo. -
06
Apunta tus dispositivos hacia él
En el lado del cliente, este es un cambio de una sola línea. En la configuración del cliente WireGuard, la línea
DNSpasa a ser la dirección del túnel de tu servidor en lugar de un resolver público — esta es la edición que jubilaDNS = 9.9.9.9de la guía de WireGuard.[Interface] PrivateKey = <paste client.key> Address = 10.66.0.2/32, fd86:ea04:1115::2/128 DNS = 10.66.0.1 [Peer] PublicKey = <paste server.pub> Endpoint = YOUR_SERVER_IP:51820 AllowedIPs = 0.0.0.0/0, ::/0 PersistentKeepalive = 25
Levanta el túnel y confirma dos cosas por separado desde el cliente: que las respuestas llegan desde tu resolver, y que la recursión de verdad sale al exterior desde tu VPS y no desde otro sitio. La segunda comprobación es la útil —
whoami.akamai.netdevuelve la dirección de cualquier resolver que haya preguntado, así que debería imprimir la IP pública de tu servidor y nada más.# answers should come from the tunnel address dig example.com | grep -E 'SERVER:' # should print your VPS public IP — this is the leak test dig +short whoami.akamai.net # and the ad flag should still be there, end to end dig example.com +dnssec | grep -E '^;; flags'
-
07
Añade DNS sobre TLS solo para un dispositivo que no pueda mantener el túnel
Sáltate este paso a menos que tengas un dispositivo concreto — normalmente un teléfono Android, a través de su ajuste de DNS privado a nivel de sistema — que quieras cubrir sin un túnel permanente. Requiere un hostname y un certificado, y ese hostname se convierte en un registro público permanente en los registros de Certificate Transparency, así que elige uno que no lleve a nada cerca de tus otras identidades.
sudo apt install -y certbot sudo ufw allow 80/tcp comment 'certbot, temporarily' sudo certbot certonly --standalone -d dns.example.net sudo ufw delete allow 80/tcp # unbound must be able to read the key sudo usermod -a -G ssl-cert unbound 2>/dev/null || true sudo chgrp -R ssl-cert /etc/letsencrypt/live /etc/letsencrypt/archive sudo chmod -R g+rX /etc/letsencrypt/live /etc/letsencrypt/archive
Añade el listener TLS como su propio archivo drop-in, para poder borrarlo de una sola vez si cambias de opinión.
# NOTE: drop-in files are read in alphabetical order and the last # access-control line for a given prefix wins — so this file must # sort AFTER private-resolver.conf, or its refuse rule overrides # the allow below and every DoT client gets REFUSED. sudo tee /etc/unbound/unbound.conf.d/zz-dot.conf > /dev/null <<'EOF' server: interface: 0.0.0.0@853 interface: ::0@853 tls-service-key: "/etc/letsencrypt/live/dns.example.net/privkey.pem" tls-service-pem: "/etc/letsencrypt/live/dns.example.net/fullchain.pem" # roaming clients have no fixed address, so this endpoint must accept any. # safe only because port 53 stays bound to loopback + wg0 and blocked at # the firewall — TLS over TCP cannot be spoofed, so it cannot be reflected. access-control: 0.0.0.0/0 allow access-control: ::/0 allow EOF sudo ufw allow 853/tcp sudo unbound-checkconf && sudo systemctl restart unboundPruébalo desde fuera del túnel antes de confiar en él, y después pon el hostname en el campo de DNS privado del teléfono. La renovación es la parte que se rompe en silencio tres meses más tarde — certbot sustituye el certificado, pero Unbound mantiene el antiguo en memoria, así que añade un deploy hook que lo recargue.
kdig -d @dns.example.net +tls-ca +tls-host=dns.example.net example.com echo 'systemctl reload unbound' | sudo tee /etc/letsencrypt/renewal-hooks/deploy/unbound.sh sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/unbound.sh


