Ubuntu 24.04: la VPN rompe DNS — arregla resolutores y rutas en el orden correcto (caso #52)

¿Te fue útil?

Te conectas a la VPN y todo parece “arriba”. El túnel está en verde. El icono sonríe.
Y entonces: nada resuelve. Los nombres internos fallan, tal vez también falla el DNS público, y tu navegador hace
el equivalente corporativo de quedarse mirando una puerta cerrada.

Este es el tipo de fallo que hace que gente inteligente haga cosas tontas —como editar aleatoriamente /etc/resolv.conf
a las 2 a.m. No lo hagas. En Ubuntu 24.04, el comportamiento del resolutor es un sistema por capas (systemd-resolved + NetworkManager + DNS por enlace),
y las VPN son agresivas con las rutas. Arreglar el DNS sin arreglar las rutas es como cambiar las pilas del detector de humo mientras la cocina sigue en llamas.

El modelo mental: qué cambió en Ubuntu 24.04 y por qué las VPN lo activan

Ubuntu 24.04 todavía “usa” /etc/resolv.conf, pero de la misma manera en que un mago de escenario “usa” una baraja de cartas:
no es donde se toman las decisiones reales. La mayoría de instalaciones de escritorio y muchos servidores ejecutan
systemd-resolved con un resolutor stub y configuración DNS por interfaz.
NetworkManager suele ser el motor de políticas que alimenta a systemd-resolved con DNS y dominios.
Los clientes VPN (OpenVPN, WireGuard a través de NetworkManager, agentes propietarios) a menudo modifican:

  • Rutas (ruta por defecto, rutas de política, rutas de túnel dividido)
  • Servidores DNS (empujan resolutores internos, fuerzan “solo DNS de la VPN”)
  • Dominios de búsqueda / DNS dividido (encaminar solo corp.example vía DNS de la VPN)
  • Reglas de firewall (algunos clientes implementan su propio kill-switch)

La trampa: DNS es tráfico de red. Si tu tabla de rutas apunta los paquetes DNS al lugar equivocado,
“arreglar el DNS” a nivel de resolutor no ayuda. Igualmente, una ruta correcta con un resolutor equivocado
hace que parezca que la VPN está caída cuando no lo está. Así que el orden importa:
primero rutas, luego selección del resolutor, luego comportamiento de DNS dividido, y finalmente cachés.

Aquí está el flujo base en un sistema típico Ubuntu 24.04:

  1. glibc llama a la biblioteca de resolución (a menudo usando reglas de nsswitch.conf)
  2. Las consultas van al nameserver listado en /etc/resolv.conf (a menudo 127.0.0.53)
  3. systemd-resolved selecciona servidores DNS por enlace, por dominio, con reglas de enrutamiento
  4. Los paquetes se envían usando la tabla de rutas del kernel (o enrutamiento por políticas) a través de la interfaz elegida

Si una VPN “amablemente” instala una ruta por defecto a través del túnel pero olvida permitir que tu DNS corporativo
sea accesible por ese túnel —o peor, instala servidores DNS que solo son accesibles en la LAN local— obtienes el síntoma clásico: las IP funcionan, los nombres no.
O “nombres públicos funcionan, nombres internos no”.

Una cita que vale la pena pegar en un post-it:
“La esperanza no es una estrategia.” — Gordon R. Dickson. No es una cita de SRE, pero aplica brutalmente bien al depurado de DNS.
(Si no puedes verificarlo con comandos, es esperanza.)

Broma #1: DNS es el único sistema donde “funciona en mi máquina” puede literalmente significar “funciona en mi resolutor.”

Datos interesantes y contexto (rápido, concreto, útil)

  • systemd-resolved ha sido el resolutor por defecto en Ubuntu durante años, pero el ecosistema aún asume que /etc/resolv.conf es la autoridad, lo que crea bugs fantasma cuando es solo un stub.
  • El DNS dividido es más antiguo de lo que la mayoría piensa: las empresas hacían “zonas internas en resolutores internos” mucho antes de que las apps VPN modernas lo vendieran como característica.
  • NetworkManager puede establecer DNS por conexión y por VPN, y también puede decidir si el DNS de la VPN sobreescribe o complementa el DNS existente.
  • Las opciones “push” de OpenVPN pueden entregar servidores DNS y rutas a los clientes; si la configuración del servidor es descuidada, todos los clientes heredan esa descuidada configuración.
  • WireGuard no tiene un plano de control nativo para DNS; el DNS normalmente lo configura el cliente (wg-quick, NetworkManager o scripts personalizados), lo que significa que “el DNS se rompió” suele ser “el cliente no aplicó el DNS.”
  • 127.0.0.53 no es “el servidor DNS”; es un listener local stub para systemd-resolved. Si apuntas aplicaciones a otro lado, puedes evitar por accidente la lógica de DNS dividido.
  • DNS sobre TCP existe por una razón: respuestas grandes, DNSSEC y algunos combos VPN/firewall que manejan mal la fragmentación UDP.
  • Los dominios de búsqueda pueden ser armas accidentales: añadir un sufijo de búsqueda como corp.example puede hacer que nombres cortos se resuelvan de forma diferente y confundir registros y herramientas.

Guion de diagnóstico rápido (qué comprobar primero/segundo/tercero)

Primero: prueba si el problema es de enrutamiento o de resolución

  1. ¿Puedes alcanzar una IP conocida por la VPN?
    Si puedes hacer ping o curl a una IP interna pero los nombres internos fallan, estás en terreno de resolutor.
    Si no puedes alcanzar IPs internas tampoco, empieza por rutas/firewall.
  2. ¿Puedes alcanzar la(s) IP(s) del servidor DNS configurado(s)?
    Si la VPN empujó 10.0.0.53 pero no puedes enrutar hacia ella, el DNS está condenado sin importar lo correctas que parezcan las configuraciones.

Segundo: inspecciona la vista de systemd-resolved, no tus suposiciones

  1. Usa resolvectl status para ver servidores DNS y dominios por enlace.
    Si tu enlace VPN tiene DNS pero la ruta por defecto sigue apuntando al Wi‑Fi, puedes tener túnel dividido sin DNS dividido (o al revés).
  2. Comprueba si /etc/resolv.conf es un stub apuntando a 127.0.0.53 u otra cosa.
    Si fue sobrescrito por un cliente VPN, puedes estar evitando la lógica por enlace de systemd-resolved.

Tercero: valida la resolución desde varios ángulos

  1. Prueba con resolvectl query (pasa por systemd-resolved).
  2. Prueba con dig @SERVER (evita la política local; útil para comprobar alcanzabilidad y corrección del servidor).
  3. Si los resultados difieren, tienes un desajuste de política/configuración en lugar de un fallo DNS puro.

Cuarto: solo entonces toca la configuración

No empieces a “arreglar” codificando 8.8.8.8 o eliminando resolved. Así es como logras que el DNS público funcione
mientras que las zonas internas fallan silenciosamente durante semanas. Arregla las rutas y la política de resolutor para que la máquina se comporte correctamente con la VPN conectada y desconectada.

Tareas prácticas: comandos, salida esperada y decisiones

Estas son las comprobaciones que realmente ejecuto en Ubuntu 24.04 cuando una VPN “conecta” pero el DNS actúa como si estuviera en otra zona horaria.
Cada tarea incluye: el comando, lo que suele significar la salida y la decisión que tomas a partir de ella.
Hazlas en orden a menos que disfrutes del depurado caótico.

Task 1: Confirmar que la interfaz VPN existe y está arriba

cr0x@server:~$ ip -brief link
lo               UNKNOWN        00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP>
enp0s31f6        UP             3c:52:82:ab:cd:ef <BROADCAST,MULTICAST,UP,LOWER_UP>
wg0              UNKNOWN        9a:bc:de:f0:12:34 <POINTOPOINT,NOARP,UP,LOWER_UP>

Significado: wg0 (o tun0) existe y está UP. Si falta, no tienes un problema de DNS; tienes un problema de VPN.
Decisión: Si la interfaz no está presente/UP, arregla la conectividad/autenticación de la VPN primero. Detente aquí.

Task 2: Inspeccionar direcciones en la interfaz VPN

cr0x@server:~$ ip addr show dev wg0
4: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 10.44.0.12/32 scope global wg0
       valid_lft forever preferred_lft forever

Significado: Has recibido una dirección. Un /32 en WireGuard es común. En OpenVPN, puedes ver /24 o /20.
Decisión: No tener dirección significa que el túnel no está correctamente configurado. El DNS no puede funcionar si la VPN no asignó identidad de red.

Task 3: Comprobar la ruta por defecto y rutas de la VPN

cr0x@server:~$ ip route
default via 192.168.1.1 dev enp0s31f6 proto dhcp metric 100
10.44.0.0/16 dev wg0 proto kernel scope link src 10.44.0.12
192.168.1.0/24 dev enp0s31f6 proto kernel scope link src 192.168.1.50 metric 100

Significado: La ruta por defecto sigue siendo la LAN local; la VPN proporciona ruta solo a 10.44/16 (túnel dividido).
Decisión: Si el DNS corporativo está en 10.44/16, bien. Si la VPN empujó DNS como 10.99.0.53 pero no hay ruta a 10.99/16, has encontrado el bug: rutas faltantes.

Task 4: Preguntar “¿cómo enrutamos hacia la IP del servidor DNS?”

cr0x@server:~$ ip route get 10.44.0.53
10.44.0.53 dev wg0 src 10.44.0.12 uid 1000
    cache

Significado: Los paquetes a 10.44.0.53 irán vía wg0. Genial.
Decisión: Si enruta vía la interfaz equivocada (como Wi‑Fi), arregla rutas/política antes de tocar ajustes de DNS.

Task 5: Verificar que puedes alcanzar el servidor DNS (ICMP es opcional; UDP/53 es lo importante)

cr0x@server:~$ nc -uvz -w2 10.44.0.53 53
Connection to 10.44.0.53 53 port [udp/domain] succeeded!

Significado: UDP 53 parece alcanzable. No es garantía perfecta, pero sugiere fuertemente que el enrutamiento/firewall no bloquea DNS básico.
Decisión: Si falla, enfócate en rutas VPN, reglas de firewall o que el servidor DNS esté caído.

Task 6: Inspeccionar qué es realmente /etc/resolv.conf

cr0x@server:~$ ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 39 Apr 25 10:12 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf

Significado: Stub resolver en uso; las apps llaman a 127.0.0.53 y systemd-resolved decide upstream.
Decisión: Si /etc/resolv.conf es un archivo regular apuntando a algún DNS proporcionado por la VPN, puedes haber evitado resolved y roto el DNS dividido. Decide si quieres eso (probablemente no).

Task 7: Comprobar el estado de systemd-resolved y DNS por enlace

cr0x@server:~$ resolvectl status
Global
       Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub

Link 2 (enp0s31f6)
    Current Scopes: DNS
         Protocols: +DefaultRoute
Current DNS Server: 192.168.1.1
       DNS Servers: 192.168.1.1

Link 4 (wg0)
    Current Scopes: DNS
         Protocols: -DefaultRoute
       DNS Servers: 10.44.0.53
        DNS Domain: ~corp.example

Significado: Configuración perfecta de DNS dividido: solo corp.example va al DNS de la VPN (dominio de enrutamiento prefijado por ~).
Decisión: Si el enlace VPN no tiene servidores DNS, NetworkManager no los aplicó (o un cliente los sobreescribió). Arregla en el perfil NM/VPN.

Task 8: Confirmar resolución vía la ruta de systemd-resolved

cr0x@server:~$ resolvectl query app01.corp.example
app01.corp.example: 10.44.20.17                     -- link: wg0

-- Information acquired via protocol DNS in 42.1ms.
-- Data is authenticated: no

Significado: Resolved está eligiendo wg0 y devolviendo el registro correcto.
Decisión: Si esto funciona pero las aplicaciones aún fallan, revisa nsswitch.conf o el comportamiento DNS específico de la app (algunas apps evitan glibc).

Task 9: Comparar con un dig directo contra el DNS de la VPN

cr0x@server:~$ dig @10.44.0.53 app01.corp.example +time=2 +tries=1

; <<>> DiG 9.18.28-0ubuntu0.24.04.1-Ubuntu <<>> @10.44.0.53 app01.corp.example +time=2 +tries=1
; (1 server found)
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48291
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; ANSWER SECTION:
app01.corp.example.  60 IN A 10.44.20.17

;; Query time: 25 msec
;; SERVER: 10.44.0.53#53(10.44.0.53) (UDP)
;; WHEN: Thu Apr 25 10:26:51 UTC 2025
;; MSG SIZE  rcvd: 62

Significado: El servidor DNS en sí está bien.
Decisión: Si dig funciona pero resolvectl falla, el problema es política/local (dominios, DNS por enlace, estado de resolved).

Task 10: Comprobar si la VPN cambió la prioridad del DNS o el manejo de la ruta por defecto

cr0x@server:~$ nmcli dev show wg0 | sed -n '1,120p'
GENERAL.DEVICE:                         wg0
GENERAL.TYPE:                           wireguard
GENERAL.STATE:                          100 (connected)
IP4.ADDRESS[1]:                         10.44.0.12/32
IP4.GATEWAY:                            --
IP4.DNS[1]:                             10.44.0.53
IP4.DOMAIN[1]:                          corp.example
IP4.ROUTE[1]:                           dst = 10.44.0.0/16, nh = 0.0.0.0, mt = 0

Significado: NM cree que wg0 tiene DNS y una ruta. Bien.
Decisión: Si IP4.DNS falta aquí, arregla el perfil de conexión VPN; no manipules resolved directamente como primer paso.

Task 11: Buscar reglas de enrutamiento por política (común con kill-switch o “túnel completo”)

cr0x@server:~$ ip rule show
0:      from all lookup local
32766:  from all lookup main
32767:  from all lookup default

Significado: No hay reglas de política especiales. Se aplica una tabla de enrutamiento simple.
Decisión: Si ves reglas extra dirigiendo tráfico DNS (o todo el tráfico) a una tabla separada, debes inspeccionar esas tablas también. No asumas que ip route por sí solo cuenta toda la historia.

Task 12: Inspeccionar reglas de firewall por bloqueos de DNS (especialmente con kill-switch de VPN)

cr0x@server:~$ sudo nft list ruleset | sed -n '1,160p'
table inet filter {
  chain output {
    type filter hook output priority filter; policy accept;
    ip daddr 10.44.0.0/16 udp dport 53 accept
    ip daddr 10.44.0.0/16 tcp dport 53 accept
  }
}

Significado: El DNS hacia el espacio VPN está permitido. Genial.
Decisión: Si ves una cadena output por defecto-drop o reglas que solo permiten tráfico vía la interfaz del túnel, confirma que el DNS está incluido. Muchos clientes VPN “de privacidad” bloquean UDP/53 a menos que esté explícitamente permitido.

Task 13: Comprobar logs de systemd-resolved cuando parece “configurado pero muerto”

cr0x@server:~$ journalctl -u systemd-resolved -n 80 --no-pager
Apr 25 10:24:10 server systemd-resolved[812]: Using degraded feature set UDP instead of UDP+EDNS0 for DNS server 10.44.0.53.
Apr 25 10:24:12 server systemd-resolved[812]: Failed to send hostname reply: Transport endpoint is not connected
Apr 25 10:24:20 server systemd-resolved[812]: Switching to DNS server 10.44.0.53 for link 4.

Significado: Puedes ver reintentos, retrocesos y mensajes de “degraded feature set” que insinúan problemas de MTU/fragmentación a través de la VPN.
Decisión: Si ves timeouts repetidos o cambios de servidor, pasa a comprobaciones de MTU y prueba consultas DNS por TCP.

Task 14: Probar DNS sobre TCP (porque las VPN y el MTU adoran el drama)

cr0x@server:~$ dig @10.44.0.53 app01.corp.example +tcp +time=2 +tries=1

; <<>> DiG 9.18.28-0ubuntu0.24.04.1-Ubuntu <<>> @10.44.0.53 app01.corp.example +tcp +time=2 +tries=1
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1136
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; ANSWER SECTION:
app01.corp.example.  60 IN A 10.44.20.17

;; Query time: 31 msec
;; SERVER: 10.44.0.53#53(10.44.0.53) (TCP)
;; WHEN: Thu Apr 25 10:28:51 UTC 2025
;; MSG SIZE  rcvd: 62

Significado: TCP funciona. Si UDP falla pero TCP tiene éxito, sospecha de MTU/fragmentación o reglas de firewall que afectan UDP.
Decisión: Ajusta el MTU en el túnel, arregla problemas de path MTU discovery o permite UDP fragmentado. No “arregles” cambiando resolutores a DNS públicos; eso solo oculta el problema.

Task 15: Confirmar qué harán las aplicaciones vía NSS

cr0x@server:~$ grep -E '^(hosts|resolve)' /etc/nsswitch.conf
hosts:          files mdns4_minimal [NOTFOUND=return] dns

Significado: Orden estándar: files, luego mDNS, luego DNS. Bien.
Decisión: Si dns falta o fue reemplazado por algo extraño, puedes tener “resolvectl funciona pero getent falla.” Arregla el orden NSS antes de culpar a la VPN.

Task 16: Probar resolución como una herramienta del sistema (porque los navegadores mienten)

cr0x@server:~$ getent ahosts app01.corp.example
10.44.20.17     STREAM app01.corp.example
10.44.20.17     DGRAM  app01.corp.example
10.44.20.17     RAW    app01.corp.example

Significado: La ruta de resolutor de glibc funciona.
Decisión: Si getent falla pero resolvectl query funciona, el problema está entre glibc y resolved (modo resolv.conf, nsswitch o DNS específico de la app).

Arregla en el orden correcto: rutas → resolutores → DNS dividido → cachés

El tema: no “arregles” el DNS forzando un único resolutor global a menos que estés decidiendo deliberadamente romper el DNS dividido.
Tu objetivo es la corrección por política: los dominios internos se resuelven vía DNS internos cuando la VPN está activa; los dominios públicos se resuelven normalmente; VPN caída te devuelve al estado normal.
Eso es fiabilidad, no heroísmo.

Paso 1: Arregla el enrutamiento hacia los servidores DNS (y las zonas internas)

La mayoría de fallos DNS por VPN no son de DNS. Son de enrutamiento. La VPN empuja una dirección de servidor DNS que solo es accesible vía el túnel,
pero el túnel está configurado con rutas divididas incorrectamente. O al revés: se instaló túnel completo, pero el firewall del cliente bloquea el DNS fuera del túnel.

Si descubres rutas faltantes, decide dónde pertenece la corrección:

  • Lugar correcto: el perfil de la VPN/configuración del servidor que empuja rutas. Arréglalo una vez; todos los clientes se benefician.
  • Solución aceptable: añadir rutas estáticas en el cliente cuando no puedes controlar el servidor VPN.
  • Malísima idea: cambiar a un resolutor público para “recuperar internet”. Eso oculta el enrutamiento interno roto y mata la resolución interna de nombres.

Ejemplo: añadir una ruta temporal (hasta que se corrija el perfil VPN):

cr0x@server:~$ sudo ip route add 10.99.0.0/16 dev wg0

Significado: Has hecho que la red del servidor DNS sea accesible vía el túnel.
Decisión: Si esto “arregla el DNS”, vuelve e implementa la ruta en la configuración de la conexión de NetworkManager o en el servidor VPN. No dejes rutas “snowflake” en portátiles como estrategia permanente.

Paso 2: Asegúrate de que systemd-resolved sea la autoridad (a menos que tengas una razón)

En Ubuntu 24.04, quieres un único tomador de decisiones. Si un cliente VPN reemplaza /etc/resolv.conf con un archivo estático,
puede evitar systemd-resolved y romper el DNS dividido. Restaura el enlace stub si es necesario.

cr0x@server:~$ sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

Significado: Las aplicaciones usan el stub local; resolved puede aplicar DNS por enlace y dominios de enrutamiento.
Decisión: Si ejecutas un servidor sin interfaz gráfica y explícitamente no quieres systemd-resolved, eso es otro diseño. Pero no mezcles modos a la ligera.

Paso 3: Arregla los ajustes DNS y de DNS dividido de la conexión VPN en NetworkManager

Para VPNs corporativas, usualmente quieres DNS dividido: zonas internas vía DNS de la VPN, todo lo demás vía tu resolutor normal.
En Ubuntu con NetworkManager, eso significa que la conexión VPN debe establecer:

  • Servidores DNS de la VPN: resolutores internos
  • Dominios DNS: dominios de enrutamiento como ~corp.example (o configurados en NM para que resolved los reciba)
  • Rutas apropiadas: hacia redes internas y servidores DNS

Inspecciona y ajusta los ajustes de la conexión (el ejemplo muestra valores de lectura; escribir depende del tipo de VPN):

cr0x@server:~$ nmcli -f NAME,TYPE,DEVICE connection show --active
Wired connection 1  ethernet  enp0s31f6
Corp WG            wireguard wg0
cr0x@server:~$ nmcli connection show "Corp WG" | sed -n '1,140p'
connection.id:                          Corp WG
connection.type:                        wireguard
ipv4.method:                            auto
ipv4.dns:                               10.44.0.53
ipv4.dns-search:                        corp.example
ipv4.never-default:                     yes

Significado: ipv4.never-default: yes indica túnel dividido (sin ruta por defecto vía VPN).
Decisión: Si necesitas túnel completo, ajusta never-default a no (y asegúrate de que las rutas DNS/firewall permitan el servidor DNS). Si necesitas túnel dividido, mantenlo en yes pero asegúrate de que existan rutas a DNS y redes internas.

Paso 4: Reparar el enrutamiento por dominio (la parte sutil que la gente se salta)

Puedes tener “servidores DNS configurados” pero aún así enviarles las consultas equivocadas.
Con systemd-resolved, los dominios de enrutamiento importan. Un dominio de enrutamiento (prefijado con ~ en la salida de resolvectl)
le indica a resolved qué dominios deben ser respondidos por qué enlace.

Si tu VPN debe manejar solo corp.example, configura eso, no una lista de búsqueda global que haga que todo parezca interno.
Una lista de búsqueda global es cómo terminas con printer resolviendo a printer.corp.example mientras estás en casa,
y de repente la red doméstica de tu familia es “parte del incidente”.

Paso 5: Vacía cachés después de los cambios (pero solo después)

systemd-resolved cachea. Los navegadores cachean. Algunas apps cachean agresivamente. Vacía la caché correcta en el momento correcto para no perseguir fantasmas.

cr0x@server:~$ sudo resolvectl flush-caches
cr0x@server:~$ resolvectl statistics
DNSSEC supported by current servers: no
Transactions: 58
Cache hits: 11
Cache misses: 47

Significado: Si sigues viendo aciertos de caché para respuestas erróneas, vaciar ayuda, pero no arregla la política.
Decisión: Vacía una vez tras cambios de configuración y vuelve a probar. Si vacías repetidamente durante el depurado, pierdes señal.

Paso 6: Si UDP es inestable, trata el MTU como ciudadano de primera clase

Las VPN cambian el MTU. DNS sobre UDP puede verse afectado por fragmentación, EDNS0 y fallos de path MTU discovery.
Si ves a resolved degradando características o timeouts UDP, prueba TCP (Task 14).
Luego considera reducir el MTU del túnel o arreglar reglas de firewall que descarten fragmentos.

Broma #2: La forma más rápida de aprender sobre MTU es ignorarlo durante una semana y luego “descubrirlo” a las 3 a.m.

Tres micro-historias corporativas desde las trincheras DNS/VPN

Micro-historia 1: El incidente causado por una suposición errónea

Una empresa mediana desplegó Ubuntu 24.04 a un grupo de ingenieros que vivían en tierra de VPN. La migración “fue bien”
hasta la primera semana cuando servicios internos empezaron a “fluctuar”. El síntoma era extraño: apps web internas funcionaban por IP pero fallaban por nombre,
y solo para algunos usuarios, de forma intermitente, dependiendo de dónde estaban sentados.

La suposición fue clásica: “resolv.conf es la configuración DNS”. Un miembro bien intencionado escribió un script que reescribía
/etc/resolv.conf al conectar la VPN para apuntar directamente al DNS corporativo, y lo reescribía de nuevo al desconectar.
Funcionó en sus pruebas en terminal. Declararon victoria y siguieron haciendo despliegues.

En Ubuntu 24.04, ese script evitó la lógica por enlace y de enrutamiento de systemd-resolved.
Usuarios con túnel dividido empezaron a enviar todas las consultas DNS a los resolutores corporativos —incluyendo dominios públicos— por rutas que no estaban enrutadas.
Los servidores DNS corporativos eran accesibles solo por el túnel, y el script a veces se ejecutaba antes de que se instalaran las rutas.
Las consultas DNS no llegaban a ningún lado, luego se cacheaban NXDOMAINs, y la experiencia en el navegador se volvió un juego de azar.

La solución fue aburrida: eliminar el script, restaurar el stub resolver y corregir el perfil VPN para que NetworkManager alimentara a resolved con los servidores y dominios de enrutamiento correctos.
El “incidente” terminó no con un hack heroico, sino con la humildad de admitir que el sistema tenía un motor de políticas por una razón.

Micro-historia 2: La optimización que salió mal

Otra organización tenía un equipo de redes preocupado por el rendimiento. Estaban hartos de quejarse de “DNS lento” y decidieron optimizar:
empujar un único servidor DNS “rápido” a todos los clientes VPN. El servidor era un resolutor bien dotado cerca del concentrador VPN.
También redujeron TTLs para registros internos porque “hace que los cambios se propaguen más rápido.”

Falló de dos maneras. Primero, clientes en modo túnel dividido ahora tenían un resolutor que solo era accesible vía VPN, pero el push del servidor no garantizaba
rutas a ese resolutor en todos los modos de túnel. Segundo, los TTL bajos aumentaron el volumen de consultas y hicieron que la pérdida intermitente de paquetes fuera visible como timeouts para usuarios.
El resolutor era “rápido” en aislamiento y frágil en la red real.

Los ingenieros empezaron a codificar resolutores públicos para “arreglar su internet”, lo que creó una nueva clase de problemas:
dominios internos se filtraban a DNS públicos (inofensivo la mayoría de las veces, pero embarazoso), y los nombres internos dejaron de resolverse por completo.
El playbook de helpdesk se convirtió en un caos de consejos contradictorios.

La remediación eventual: restaurar DNS dividido (zonas internas a resolutores internos solo cuando existen rutas),
mantener TTLs razonables y añadir soporte de fallback TCP para DNS a través del camino VPN.
El rendimiento mejoró porque el sistema era correcto, no porque alguien declarara un resolutor “rápido”.

Micro-historia 3: La práctica aburrida pero correcta que salvó el día

Una empresa muy enfocada en seguridad tenía la costumbre que parecía aburrida en papel: cada cambio de VPN requería correr una lista de comprobación en una imagen limpia de Ubuntu.
No un portátil de desarrollador. No la máquina “ya configurada” de alguien. Una VM desechable que representaba la realidad base.
Probaban enrutamiento, DNS y un conjunto pequeño de búsquedas a servicios internos.

Una semana, planearon añadir una nueva zona DNS interna y actualizar la VPN para empujar el dominio de enrutamiento correspondiente.
Durante la ejecución de la lista de comprobación, resolvectl status mostró que el dominio se añadía como dominio de búsqueda en lugar de dominio de enrutamiento.
Eso significaba que resolved lo trataría de forma diferente, y los nombres cortos empezarían a resolverse de maneras sorprendentes.
Aún no era un incidente de seguridad, pero olía a uno.

El equipo lo detectó antes del despliegue. Ajustaron la generación de perfiles de NetworkManager para que la VPN crease enrutamiento por dominio apropiado.
También verificaron que la IP del servidor DNS fuera alcanzable en modo túnel dividido comprobando ip route get durante la conexión.
Esto previno la oleada de tickets “VPN conectada pero DNS muerto”.

Nada heroico. Ninguna edición a medianoche. Solo una lista de comprobación que trataba al DNS y al enrutamiento como sistemas acoplados.
Los mejores incidentes son aquellos por los que nunca te despiertan.

Errores comunes (síntomas → causa raíz → solución)

1) Síntoma: “La VPN conecta, pero los nombres internos no se resuelven”

Causa raíz: El servidor DNS de la VPN está configurado, pero no hay ruta hacia él (túnel dividido sin incluir la subred del resolutor).
Solución: Añadir/reparar rutas hacia la(s) IP(s) del servidor DNS en el perfil VPN o en la configuración push del servidor. Verificar con ip route get DNS_IP.

2) Síntoma: “El DNS público funciona, el DNS interno falla”

Causa raíz: No está configurado el DNS dividido; los dominios internos no se enrutan al resolutor de la VPN (o faltan dominios de enrutamiento).
Solución: Configurar dominios de enrutamiento (ej., ~corp.example) para el enlace VPN en resolved/NM, y asegurarse de que los servidores DNS de la VPN estén en ese enlace.

3) Síntoma: “Lo interno funciona, pero internet muere cuando la VPN está arriba”

Causa raíz: Se instaló una ruta de túnel completo (o reglas kill-switch) pero el DNS de la VPN apunta a resolutores internos que no resuelven dominios públicos, o el egress está restringido.
Solución: Decidir la política: túnel completo con resolutores recursivos corporativos que resuelvan públicos, o túnel dividido con resolutor no-VPN para público. No hacerlo a medias.

4) Síntoma: “resolvectl query funciona, pero navegadores/apps aún fallan”

Causa raíz: La aplicación evita el resolutor del sistema (DoH, resolutor propio, DNS en contenedor o nameserver codificado).
Solución: Confirmar con getent ahosts y ajustes de la app. Desactivar DoH de la app temporalmente o alinearla con la política DNS corporativa.

5) Síntoma: “dig @DNS funciona, pero resolvectl query hace timeout”

Causa raíz: systemd-resolved está usando servidores DNS diferentes o seleccionando el enlace equivocado debido a dominios de enrutamiento faltantes.
Solución: Inspeccionar resolvectl status y asegurarse de que el enlace VPN tenga servidores DNS y dominios de enrutamiento ~domain.

6) Síntoma: “El DNS funciona por un tiempo y luego falla después de suspender/reanudar”

Causa raíz: La VPN se reconecta pero no se re-aplican ajustes DNS (race entre VPN up y actualización NM/resolved), o quedan rutas obsoletas.
Solución: Confirmar después de reanudar con las tareas 3, 7, 10. Si faltan, arreglar scripts dispatcher o integración del cliente VPN; vaciar cachés no es una solución, es un parche.

7) Síntoma: “Solo fallan respuestas DNS grandes (algunos dominios cargan, otros no)”

Causa raíz: Problemas de MTU/fragmentación, EDNS0, o fragmentos UDP descartados a través del túnel.
Solución: Probar DNS por TCP. Ajustar MTU del túnel, permitir fragmentos o configurar tamaños UDP más seguros.

8) Síntoma: “NXDOMAIN aleatorio para nombres internos”

Causa raíz: Un resolutor equivocado está respondiendo consultas internas (resolutor público o router doméstico), a veces por falta de dominio de enrutamiento o prioridad DNS incorrecta.
Solución: Forzar DNS dividido: zonas internas exclusivamente a resolutores internos vía el enlace VPN.

Listas de comprobación / plan paso a paso

Lista A: La secuencia “Necesito que funcione ahora” (10–15 minutos)

  1. Confirmar que la interfaz VPN está arriba: ip -brief link.
  2. Confirmar que la VPN tiene IP: ip addr show dev wg0 o ip addr show dev tun0.
  3. Comprobar rutas: ip route.
  4. Verificar ruta hacia el servidor DNS: ip route get DNS_IP.
  5. Probar alcanzabilidad del puerto DNS: nc -uvz DNS_IP 53.
  6. Inspeccionar estado del resolutor: resolvectl status.
  7. Consultar nombre interno: resolvectl query host.corp.example.
  8. Comparar con dig directo: dig @DNS_IP host.corp.example.
  9. Vaciar caché una vez tras cambios: sudo resolvectl flush-caches.
  10. Si UDP es inestable, probar TCP: dig @DNS_IP host.corp.example +tcp.

Lista B: Hacer la corrección duradera (para no recibir este ticket otra vez)

  1. Dejar de editar /etc/resolv.conf manualmente; restaurar el stub si es necesario.
  2. Corregir rutas push del perfil VPN/servidor para incluir la subred del servidor DNS y redes internas.
  3. Configurar dominios de enrutamiento para DNS dividido (no solo dominios de búsqueda) para zonas internas.
  4. Validar con resolvectl status que el enlace VPN posee ~corp.example.
  5. Confirmar que nmcli dev show muestra DNS y dominios en la interfaz VPN.
  6. Auditar reglas de firewall/kill-switch para DNS sobre UDP y TCP.
  7. Documentar el estado esperado: qué interfaz posee qué dominio y qué servidores DNS.
  8. Añadir una prueba de regresión: conectar la VPN en una imagen limpia y ejecutar resolvectl query para al menos un nombre interno y uno público.

Lista C: Cuando sospechas que el cliente VPN “está ayudando” demasiado

  1. Comprobar si /etc/resolv.conf fue reemplazado por un archivo regular.
  2. Buscar entradas extra en ip rule.
  3. Revisar la salida de nft para reglas que filtren UDP/53.
  4. Buscar múltiples gestores de DNS ejecutándose (cliente propietario + NetworkManager + resolved).
  5. Elegir un gestor como fuente de la verdad; deshabilitar la manipulación DNS de los otros si es posible.

Preguntas frecuentes

1) ¿Por qué la VPN rompe el DNS en Ubuntu 24.04 específicamente?

No es “específico”, pero Ubuntu 24.04 lo hace más visible porque systemd-resolved y el DNS por enlace son comunes.
Clientes VPN que asumen que /etc/resolv.conf es la única perilla pueden pelearse con la pila real de resolutores.

2) ¿Debería desactivar systemd-resolved?

Normalmente no. Resolved es bueno para DNS dividido cuando recibe ajustes por enlace correctos. Desactívalo solo si tienes un plan alternativo claro
(como un resolutor local en caché que administras) y entiendes cómo los clientes VPN lo actualizarán.

3) Mi /etc/resolv.conf apunta a 127.0.0.53. ¿Eso está mal?

No. Ese es el stub local. Los servidores upstream reales son visibles en resolvectl status.
Si apuntas /etc/resolv.conf directamente a un servidor upstream, puedes evitar el comportamiento de DNS dividido.

4) ¿Qué significa ~corp.example en resolvectl status?

La tilde indica un dominio de enrutamiento: las consultas para ese dominio van a los servidores DNS de ese enlace.
Sin la tilde, es más bien un dominio de búsqueda, lo que cambia cómo se expanden los nombres cortos.

5) ¿Por qué dig funciona pero mis apps siguen fallando?

dig @server evita tu política de resolutor y pregunta directamente a un servidor. Prueba la alcanzabilidad y corrección del servidor,
no que tu SO esté usando ese servidor para ese dominio. Además, las apps pueden usar DoH o su propia pila de resolución.

6) ¿Por qué el DNS funciona para registros pequeños pero falla para algunos dominios?

Respuestas más grandes (múltiples A/AAAA, DNSSEC, TXT grandes) pueden chocar con problemas de fragmentación UDP a través de la VPN.
Prueba con dig +tcp. Si TCP funciona pero UDP no, aborda MTU/fragmentación o comportamiento del firewall.

7) ¿Puedo simplemente añadir DNS público como 1.1.1.1 como fallback?

Puedes, pero es una decisión de política con consecuencias. A menudo hace que consultas internas se filtren a DNS público o fallen de forma impredecible,
y puede enmascarar rutas VPN faltantes. Si la empresa exige resolución solo interna, no lo hagas.

8) ¿Cómo sé si la VPN está forzando una ruta por defecto?

Revisa ip route para la ruta por defecto y nmcli connection show para ipv4.never-default.
También revisa ip rule por enrutamiento por políticas que pueda sobreescribir la ruta por defecto.

9) ¿Por qué solo se rompe después de reconectar la VPN?

Condiciones de carrera y estado obsoleto: rutas y dominios DNS pueden no eliminarse completamente al desconectar,
o no re-aplicarse al conectar. Confirma el estado tras reconectar con ip route y resolvectl status.

10) ¿Cuál es la forma más segura de validar una solución?

Valida tres cosas: (1) ip route get DNS_IP usa la interfaz esperada, (2) resolvectl status muestra DNS por enlace y dominios de enrutamiento correctos,
(3) getent ahosts resuelve nombres internos y públicos como se espera con la VPN arriba y abajo.

Conclusión: pasos prácticos siguientes

Cuando la VPN rompe el DNS en Ubuntu 24.04, la jugada ganadora es el orden. Rutas primero. Política de resolutor segundo. DNS dividido tercero. Cachés al final.
Si lo arreglas al revés, conseguirás algo que “funciona” hasta que no lo haga —por lo general cuando estás presentando, de guardia, o ambas cosas.

Siguientes pasos que puedes hacer hoy:

  1. Ejecuta el guion de diagnóstico rápido y captura las salidas de ip route y resolvectl status.
  2. Verifica la alcanzabilidad al servidor DNS de la VPN con ip route get y nc -uvz.
  3. Restaura el stub /etc/resolv.conf si un cliente VPN lo sobrescribió.
  4. Arregla el perfil VPN/servidor para que rutas y dominios de enrutamiento coincidan con los servidores DNS que empuja.
  5. Añade una lista de comprobación de regresión en una imagen limpia de Ubuntu 24.04, porque el estado del portátil de ayer no es un entorno de prueba.

Los fallos DNS rara vez son glamurosos. Sin embargo son extremadamente honestos: el sistema hará exactamente lo que tus rutas y la política de resolutor le digan.
Haz que esos dos sean correctos y lo demás tiende a ponerse en línea.

← Anterior
Ataques a la cadena de suministro: cuando los atacantes apuntan a tu proveedor en lugar de a ti
Siguiente →
IKEv2/IPsec: Cuando es una mejor opción que WireGuard u OpenVPN

Deja un comentario