Configuración de DNS Split-Horizon en Linux
DNS split-horizon (también llamado split-brain DNS) es una técnica que permite servir diferentes respuestas DNS para el mismo nombre de dominio dependiendo del origen de la consulta, mostrando IPs internas a los clientes de la red corporativa e IPs públicas a los clientes externos. Esta arquitectura es esencial en entornos empresariales donde los servidores tienen tanto una dirección IP privada (para comunicación interna) como una pública, evitando el hairpin NAT innecesario. Esta guía cubre la implementación con BIND9 usando vistas (views).
Requisitos Previos
- Servidor Linux (Ubuntu 22.04/Debian 12 o CentOS 9/Rocky 9)
- BIND9 instalado
- Al menos dos subredes: red interna y tráfico externo
- Comprensión básica de zonas DNS y registros DNS
- Acceso root al servidor
Instalación de BIND9
# Ubuntu/Debian
apt-get update
apt-get install -y bind9 bind9utils bind9-doc
# CentOS/Rocky Linux
dnf install -y bind bind-utils
# Verificar la versión instalada
named -v
# Habilitar el servicio
systemctl enable --now named # CentOS
# o
systemctl enable --now bind9 # Ubuntu/Debian
# Estructura de archivos de BIND
ls /etc/bind/ # Ubuntu/Debian
ls /etc/named.conf* # CentOS/Rocky
Arquitectura de Vistas DNS
Con split-horizon, BIND evalúa la dirección IP origen de cada consulta y aplica la vista correspondiente:
Red Interna (10.0.0.0/8)
→ Vista "internal"
→ empresa.com → 10.0.1.10 (IP privada del servidor web)
Internet (cualquier otra IP)
→ Vista "external"
→ empresa.com → 93.184.216.34 (IP pública del servidor web)
Cada vista tiene su propio conjunto de zonas y puede configurarse con distintos servidores upstream y opciones de resolución.
Configuración de ACLs
Define las ACLs para identificar clientes internos y externos:
# En Ubuntu/Debian: /etc/bind/named.conf
# En CentOS/Rocky: /etc/named.conf
cat > /etc/bind/named.conf.acls << 'EOF'
// Definición de ACLs para split-horizon DNS
// Clientes internos: redes privadas de la empresa
acl "clientes-internos" {
localhost; // El propio servidor DNS
10.0.0.0/8; // Red corporativa privada clase A
172.16.0.0/12; // Redes privadas adicionales
192.168.0.0/16; // Redes de oficinas remotas
};
// Clientes externos: todo lo que no es interno
acl "clientes-externos" {
!10.0.0.0/8; // Excluir red interna
!172.16.0.0/12;
!192.168.0.0/16;
!localhost;
any; // Todos los demás
};
EOF
Incluye las ACLs en la configuración principal:
# /etc/bind/named.conf (Ubuntu) o /etc/named.conf (CentOS)
# Añadir al principio del archivo:
include "/etc/bind/named.conf.acls";
Configuración de Vistas Interna y Externa
cat > /etc/bind/named.conf.views << 'EOF'
// Vista interna: para clientes de la red corporativa
view "internal" {
match-clients { clientes-internos; };
// Recursión habilitada para clientes internos
recursion yes;
// Servidores de reenvío para dominios externos (usando DNS interno de la empresa)
forwarders {
10.0.0.1; // DNS primario corporativo
10.0.0.2; // DNS secundario corporativo
};
// Incluir las zonas internas
include "/etc/bind/named.conf.zones.internal";
// Incluir zonas de resolución inversa privadas
zone "0.0.10.in-addr.arpa" {
type master;
file "/etc/bind/zones/db.10.0.0.reverse";
};
};
// Vista externa: para clientes de Internet
view "external" {
match-clients { clientes-externos; };
// Sin recursión para clientes externos (evitar ser resolver abierto)
recursion no;
// Solo servir nuestras zonas autorizadas a clientes externos
include "/etc/bind/named.conf.zones.external";
// Denegar transferencias de zona desde Internet
allow-transfer { none; };
};
EOF
# Incluir las vistas en la configuración principal
# Añadir a /etc/bind/named.conf:
# include "/etc/bind/named.conf.views";
Definición de Zonas por Vista
Crea los archivos de zona para cada vista:
# Archivo de zonas para la vista INTERNA
cat > /etc/bind/named.conf.zones.internal << 'EOF'
// Zona interna de empresa.com - devuelve IPs privadas
zone "empresa.com" {
type master;
file "/etc/bind/zones/db.empresa.com.internal";
allow-transfer { 10.0.0.2; }; // Solo al servidor DNS secundario
};
EOF
# Archivo de zona interna con IPs privadas
mkdir -p /etc/bind/zones
cat > /etc/bind/zones/db.empresa.com.internal << 'EOF'
$ORIGIN empresa.com.
$TTL 300
@ IN SOA ns1.empresa.com. admin.empresa.com. (
2024011501 ; Serial
3600 ; Refresh
900 ; Retry
604800 ; Expire
300 ) ; TTL mínimo
; Servidores de nombres
@ IN NS ns1.empresa.com.
; APUNTAR A IPS INTERNAS PRIVADAS
ns1 IN A 10.0.0.10 ; El propio servidor DNS
www IN A 10.0.1.10 ; IP interna del servidor web
api IN A 10.0.2.10 ; IP interna del servidor API
mail IN A 10.0.4.10 ; IP interna del servidor de correo
vpn IN A 10.0.5.10 ; Servidor VPN interno
; Alias internos
app IN CNAME api.empresa.com.
intranet IN A 10.0.1.20 ; Solo accesible desde la red interna
gitlab IN A 10.0.1.30 ; GitLab privado interno
EOF
# Archivo de zonas para la vista EXTERNA
cat > /etc/bind/named.conf.zones.external << 'EOF'
// Zona externa de empresa.com - devuelve IPs públicas
zone "empresa.com" {
type master;
file "/etc/bind/zones/db.empresa.com.external";
allow-transfer { none; };
};
EOF
# Archivo de zona externa con IPs públicas
cat > /etc/bind/zones/db.empresa.com.external << 'EOF'
$ORIGIN empresa.com.
$TTL 3600
@ IN SOA ns1.empresa.com. admin.empresa.com. (
2024011501 ; Serial
3600 ; Refresh
900 ; Retry
604800 ; Expire
3600 ) ; TTL mínimo (mayor para externos)
; Servidores de nombres públicos
@ IN NS ns1.empresa.com.
@ IN NS ns2.empresa.com.
; APUNTAR A IPS PÚBLICAS
ns1 IN A 93.184.216.10 ; IP pública del servidor DNS
ns2 IN A 93.184.216.11 ; IP pública del DNS secundario
@ IN A 93.184.216.34 ; IP pública principal de la empresa
www IN A 93.184.216.34 ; Servidor web público
api IN A 93.184.216.35 ; API pública
mail IN A 93.184.216.36 ; Servidor de correo público
@ IN MX 10 mail.empresa.com.
@ IN TXT "v=spf1 mx -all"
; Servicios internos NO visibles externamente (sin registro)
; intranet - NO aparece aquí intencionalmente
; gitlab - NO aparece aquí intencionalmente
EOF
Configura la configuración principal de BIND:
cat > /etc/bind/named.conf << 'EOF'
// Configuración principal de BIND9 con split-horizon
include "/etc/bind/named.conf.options";
include "/etc/bind/named.conf.acls";
include "/etc/bind/named.conf.views";
// No incluir named.conf.local aquí (las zonas van en las vistas)
EOF
# Configuración de opciones globales
cat > /etc/bind/named.conf.options << 'EOF'
options {
directory "/var/cache/bind";
// Escuchar en todas las interfaces
listen-on { any; };
listen-on-v6 { any; };
// No permitir recursión por defecto (se define por vista)
recursion no;
// DNSSEC
dnssec-validation auto;
// Estadísticas
statistics-file "/var/log/named/named_stats.txt";
};
EOF
# Verificar la configuración antes de reiniciar
named-checkconf /etc/bind/named.conf
named-checkzone empresa.com /etc/bind/zones/db.empresa.com.internal
named-checkzone empresa.com /etc/bind/zones/db.empresa.com.external
# Reiniciar BIND con la nueva configuración
systemctl restart bind9 # Ubuntu
# systemctl restart named # CentOS
Estrategias de Prueba
# Verificar la vista interna (simular consulta desde la red interna)
# Desde un cliente en la red 10.0.0.0/8:
dig @IP-servidor-DNS www.empresa.com
# Debe devolver: 10.0.1.10
# Verificar la vista externa (simular consulta desde Internet)
# Usando la IP externa del servidor:
dig @IP-publica-servidor-DNS www.empresa.com
# Debe devolver: 93.184.216.34
# Forzar una vista específica con bind9 (debug desde el servidor)
# Ver qué vista se aplica para una IP origen
rndc status
# Ver estadísticas del servidor
rndc stats
cat /var/log/named/named_stats.txt | grep -A5 "View"
# Prueba completa desde el propio servidor DNS
# (localhost siempre usa la vista interna)
dig @127.0.0.1 www.empresa.com # Debe ser IP interna
dig @IP-pública www.empresa.com # Debe ser IP externa
# Verificar que la intranet NO es visible desde fuera
# Desde una IP externa:
dig @IP-publica-servidor-DNS intranet.empresa.com
# Debe devolver NXDOMAIN
Solución de Problemas
# Ver logs de BIND en tiempo real
journalctl -u bind9 -f # Ubuntu
journalctl -u named -f # CentOS
# Aumentar el nivel de log para debug (temporalmente)
rndc querylog on # Activar log de consultas
tail -f /var/log/syslog | grep named
# Verificar que las zonas se cargaron correctamente
rndc reload
rndc zonestatus empresa.com in internal # Estado de zona en vista interna
rndc zonestatus empresa.com in external # Estado de zona en vista externa
# Reload suave (sin reiniciar el servicio)
rndc reload
# Error común: "no matching view found"
# Verificar que las ACLs cubren todos los clientes y no hay solapamiento
# La directiva match-clients se evalúa en orden
# Error: "zone already defined"
# Cada zona debe definirse dentro de su vista, no fuera
# Comprobar sintaxis de ACL
named-checkconf -p | grep acl # Mostrar configuración parseada
rndc querylog off # Desactivar log de consultas al terminar el debug
Conclusión
El DNS split-horizon con BIND9 es una solución elegante para gestionar la dualidad de direccionamiento interno/externo en infraestructuras empresariales, eliminando el hairpin NAT y mejorando el rendimiento de las comunicaciones internas. Una configuración cuidadosa de las ACLs y el mantenimiento sincronizado de ambas vistas garantizan que los servicios sean accesibles correctamente tanto desde la red corporativa como desde Internet.


