--- title: "Cómo configurar DNS split-horizon con BIND9 en Ubuntu 24.04" description: "Configura DNS split-horizon con vistas de BIND9 en Ubuntu 24.04 para responder con IPs privadas a tu red interna y con IPs públicas al resto de Internet." category: "DNS Avanzado" language: es source: https://cubepath.com/es/docs/dns-avanzado/configuracion-de-dns-split-horizon updated: 2026-09-25 --- # Cómo configurar DNS split-horizon con BIND9 en Ubuntu 24.04 El DNS split-horizon (también llamado split-brain) consiste en que un mismo nombre, por ejemplo `www.example.com`, devuelva una IP privada a los equipos de tu red interna y la IP pública a cualquier otro cliente. Así el tráfico interno viaja por la red privada en lugar de salir a Internet y volver (hairpin NAT), y los nombres puramente internos no se publican. En este tutorial configurarás BIND9 en Ubuntu 24.04 con dos vistas, `internal` y `external`, y comprobarás desde dentro y desde fuera que cada cliente recibe la respuesta correcta. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS que actuará como servidor DNS, por ejemplo un VPS de CubePath, con una IP pública y una IP en una red privada. - Un usuario no root con privilegios `sudo`. - Al menos otro equipo en la red privada y un equipo fuera de ella (tu ordenador, por ejemplo) para las pruebas. - Conocimientos básicos de registros DNS (A, NS, SOA). A lo largo de la guía se usan estos valores de ejemplo. Sustitúyelos por los tuyos: | Elemento | Valor de ejemplo | |---|---| | Dominio | `example.com` | | Red privada | `10.10.0.0/24` | | Servidor DNS (`ns1`) | privada `10.10.0.10`, pública `203.0.113.10` | | Servidor web (`www`) | privada `10.10.0.20`, pública `203.0.113.20` | | Intranet (solo interna) | `10.10.0.30` | ## Paso 1: Instalar BIND9 Instala el servidor BIND y las utilidades de diagnóstico (`dig`, `named-checkconf`, `named-checkzone`): ```bash sudo apt update sudo apt install bind9 bind9-utils bind9-dnsutils ``` Comprueba la versión instalada: ```bash named -v ``` ```output BIND 9.18.x-0ubuntu0.24.04.x-Ubuntu (Extended Support Version) ``` El paquete arranca el servicio automáticamente. En Ubuntu 24.04 la unidad se llama `named` (`bind9` es un alias): ```bash systemctl status named --no-pager ``` ```output ● named.service - BIND Domain Name Server Loaded: loaded (/usr/lib/systemd/system/named.service; enabled; preset: enabled) Active: active (running) ``` ## Paso 2: Preparar named.conf para usar vistas En Ubuntu, `/etc/bind/named.conf` incluye tres archivos: `named.conf.options`, `named.conf.local` y `named.conf.default-zones`. Cuando se usan vistas, BIND exige que **todas** las zonas estén dentro de alguna vista, y `named.conf.default-zones` define zonas (la raíz, `localhost`, etc.) a nivel global. Si lo dejas así, `named-checkconf` fallará con `when using 'view' statements, all zones must be in views`. Abre el archivo principal: ```bash sudo nano /etc/bind/named.conf ``` Deja solo estas dos líneas. El archivo de zonas por defecto lo incluirás más adelante dentro de la vista interna: ```text include "/etc/bind/named.conf.options"; include "/etc/bind/named.conf.local"; ``` ## Paso 3: Configurar las opciones globales Las opciones globales se aplican a todas las vistas salvo que una vista las redefina. Desactiva la recursión a nivel global para que el servidor no sea un resolver abierto; la activarás solo para los clientes internos. ```bash sudo nano /etc/bind/named.conf.options ``` Sustituye el contenido por el siguiente bloque: ```text options { directory "/var/cache/bind"; listen-on { any; }; listen-on-v6 { any; }; dnssec-validation auto; // Sin recursión por defecto; la vista interna la activa recursion no; // Nadie puede pedir transferencias de zona allow-transfer { none; }; // No revelar la versión de BIND version "none"; }; ``` ## Paso 4: Definir la ACL y las vistas Una ACL agrupa las direcciones que BIND considera internas. Las vistas se evalúan **en orden** y cada consulta entra en la primera cuya condición `match-clients` coincida, así que la vista interna va primero y la externa, con `any`, recoge todo lo demás. Abre `named.conf.local`: ```bash sudo nano /etc/bind/named.conf.local ``` Añade la configuración: ```text acl "internos" { localhost; 10.10.0.0/24; }; view "internal" { match-clients { internos; }; recursion yes; allow-recursion { internos; }; zone "example.com" { type primary; file "/etc/bind/zones/db.example.com.internal"; }; // Zonas por defecto (raíz, localhost, etc.) necesarias para resolver include "/etc/bind/named.conf.default-zones"; }; view "external" { match-clients { any; }; recursion no; zone "example.com" { type primary; file "/etc/bind/zones/db.example.com.external"; }; }; ``` La ACL predefinida `localhost` incluye **todas** las direcciones del propio servidor, también la pública. Tenlo en cuenta al hacer pruebas en el paso 7. > Nota: si tu red interna usa otras subredes (varias redes privadas, una VPN), añádelas a la ACL `internos`. Si los clientes internos salen a Internet a través de un NAT y llegan al servidor con una IP pública, esa IP también debe estar en la ACL. ## Paso 5: Crear los archivos de zona Cada vista necesita su propio archivo de zona, aunque el nombre de la zona sea el mismo. Crea el directorio: ```bash sudo mkdir -p /etc/bind/zones ``` Crea la zona interna, con las IPs privadas y el registro `intranet`, que solo existirá aquí: ```bash sudo nano /etc/bind/zones/db.example.com.internal ``` ```text $TTL 300 @ IN SOA ns1.example.com. hostmaster.example.com. ( 2026092501 ; serial 3600 ; refresh 900 ; retry 604800 ; expire 300 ) ; TTL negativo IN NS ns1.example.com. ns1 IN A 10.10.0.10 @ IN A 10.10.0.20 www IN A 10.10.0.20 intranet IN A 10.10.0.30 ``` Crea la zona externa, con las IPs públicas y sin los nombres internos: ```bash sudo nano /etc/bind/zones/db.example.com.external ``` ```text $TTL 3600 @ IN SOA ns1.example.com. hostmaster.example.com. ( 2026092501 ; serial 3600 ; refresh 900 ; retry 604800 ; expire 3600 ) ; TTL negativo IN NS ns1.example.com. ns1 IN A 203.0.113.10 @ IN A 203.0.113.20 www IN A 203.0.113.20 ``` Un TTL bajo en la vista interna (300 segundos) permite cambiar IPs internas con rapidez; en la externa un TTL más alto reduce consultas desde Internet. ## Paso 6: Validar la configuración y reiniciar BIND Antes de reiniciar, comprueba la sintaxis. `named-checkconf` no muestra nada si todo es correcto: ```bash sudo named-checkconf ``` Valida las dos zonas: ```bash sudo named-checkzone example.com /etc/bind/zones/db.example.com.internal sudo named-checkzone example.com /etc/bind/zones/db.example.com.external ``` ```output zone example.com/IN: loaded serial 2026092501 OK zone example.com/IN: loaded serial 2026092501 OK ``` Reinicia BIND y confirma que ha cargado la zona en ambas vistas: ```bash sudo systemctl restart named sudo rndc zonestatus example.com IN internal sudo rndc zonestatus example.com IN external ``` Cada comando debe mostrar el nombre de la zona, el archivo correspondiente y el serial `2026092501`. Si alguno falla, revisa el registro del servicio: ```bash sudo journalctl -u named -n 30 --no-pager ``` Abre el puerto 53 en UFW, tanto UDP como TCP (TCP se usa para respuestas grandes): ```bash sudo ufw allow 53/udp sudo ufw allow 53/tcp ``` ## Paso 7: Probar las dos vistas Prueba primero desde un equipo de la red privada. Debe recibir la IP interna: ```bash dig @10.10.0.10 www.example.com +short ``` ```output 10.10.0.20 ``` El nombre `intranet` también debe resolver, y la recursión debe funcionar para dominios ajenos: ```bash dig @10.10.0.10 intranet.example.com +short dig @10.10.0.10 cubepath.com +short ``` Ahora prueba desde un equipo **fuera** de la red privada, contra la IP pública del servidor: ```bash dig @203.0.113.10 www.example.com +short ``` ```output 203.0.113.20 ``` El nombre interno no debe existir para clientes externos: ```bash dig @203.0.113.10 intranet.example.com ``` ```output ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 41522 ``` Y el servidor no debe resolver dominios ajenos para clientes externos: ```bash dig @203.0.113.10 cubepath.com ``` ```output ;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 18830 ;; WARNING: recursion requested but not available ``` > Importante: no hagas la prueba externa desde el propio servidor DNS. Aunque consultes su IP pública, el origen de la consulta es una dirección local, coincide con la ACL `localhost` y recibirás siempre la respuesta interna. Para que los equipos internos usen este servidor, configura `10.10.0.10` como servidor DNS en ellos (en Ubuntu, en la sección `nameservers` de Netplan) y comprueba con `resolvectl query www.example.com`. ## Paso 8: Mantener las zonas Con split-horizon mantienes dos copias de la zona. Cada vez que cambies un registro: 1. Edita el archivo de la vista correspondiente (o los dos, si el registro existe en ambas). 2. Incrementa el serial del SOA en cada archivo modificado. 3. Valida con `named-checkzone` y recarga solo esa zona y esa vista: ```bash sudo rndc reload example.com IN internal ``` ```output zone reload queued ``` ## Solución de problemas **`when using 'view' statements, all zones must be in views`**: alguna zona está definida fuera de las vistas. Normalmente es el `include` de `named.conf.default-zones` en `named.conf`, o una zona antigua en `named.conf.local`. Muévela dentro de una vista. **Un cliente recibe la vista equivocada**: activa el registro de consultas, que muestra la IP de origen y la vista que se aplicó: ```bash sudo rndc querylog on sudo journalctl -u named -f ``` ```output client @0x7f... 198.51.100.7#53211 (www.example.com): view external: query: www.example.com IN A +E(0)K (203.0.113.10) ``` Si la IP de origen no es la que esperabas (por ejemplo, llega por NAT), ajusta la ACL. Desactiva el registro al terminar con `sudo rndc querylog off`. **`file not found` o `permission denied` al cargar la zona**: revisa la ruta en `named.conf.local` y que los archivos sean legibles por el usuario `bind` (`ls -l /etc/bind/zones`). **Los cambios no se ven**: casi siempre falta incrementar el serial o la respuesta anterior sigue en la caché del cliente hasta que expire el TTL. ## Conclusión Tienes un servidor BIND9 que responde con direcciones privadas a tu red interna, con direcciones públicas al resto de Internet y que solo hace de resolver recursivo para los clientes internos. Como siguientes pasos, puedes añadir un servidor secundario con transferencias firmadas con TSIG, delegar el dominio en tu registrador apuntando a `ns1` para que la vista externa sea la autoritativa pública, o firmar la zona externa con DNSSEC.