El DNS es una de las seis capas independientes que tienen que decir que sí antes de que una petición llegue a algún sitio, y aquí tiene una única fuente de verdad autoritativa. Cada archivo de zona (cada registro de cada nombre sobre el que esta infraestructura es autoritativa) vive en un único primario. Los servidores secundarios no tienen sus propias copias mantenidas a mano de esos registros; replican desde el primario mediante transferencia de zona, autenticada de forma que un secundario solo acepta una transferencia de un primario que pueda verificar. Cualquier cosa sobre la que esta infraestructura no sea autoritativa (un dominio público que no posee, un nombre por ahí en el resto de internet) se resuelve de la forma habitual, recursando hacia fuera. El resolver solo se extiende hacia fuera para los nombres sobre los que no tiene autoridad; para sus propias zonas, la respuesta siempre traza de vuelta al único lugar donde esos registros se mantienen.
Cómo es realmente la sincronización manual una vez que la has hecho
Antes de esto, mantener sincronizado más de un servidor DNS significaba editar un archivo de zona (o un registro en la herramienta que gestione eso) y luego acordarse de hacer el mismo cambio en todos los demás sitios donde esos datos de ese servidor necesitaban existir. Ese “acordarse” es todo el problema. Funciona bien para el primer cambio, funciona bien para el segundo, y para el décimo cambio en dos o tres servidores ya te has olvidado de uno de ellos o has cometido una errata que no coincide entre copias. El modo de fallo no es dramático (nada se cae), es simplemente inconsistencia silenciosa: un servidor responde a una consulta de una forma, otro responde de forma ligeramente distinta, y ahora qué respuesta recibe un cliente depende de qué servidor haya atendido esa resolución en concreto. Es una propiedad genuinamente mala para una infraestructura cuyo único trabajo es dar respuestas consistentes.
La transferencia de zona elimina el “acordarse”. Hay un único sitio donde hacer un cambio (el archivo de zona del primario) y el mecanismo que lo lleva a cada secundario es un protocolo, no la memoria de una persona.
Cómo funciona realmente el mecanismo de transferencia
El DNS tiene un mecanismo de transferencia de zona desde las especificaciones originales de DNS, y el patrón usado en esta configuración se acerca al modelo estándar de DNS secundario más que a nada personalizado. Un secundario comprueba periódicamente el número de serie del registro SOA (start-of-authority) del primario; si el serial se ha incrementado, la zona ha cambiado y toca una transferencia. En lugar de esperar a que pase ese intervalo de sondeo, el primario también puede enviar un mensaje NOTIFY en el momento en que una zona cambia, avisando a los secundarios para que comprueben de inmediato: ese mecanismo está definido en la RFC 1996, “A Mechanism for Prompt Notification of Zone Changes (DNS NOTIFY)”.
La transferencia en sí puede ocurrir de dos formas. Una transferencia de zona completa (AXFR), formalmente especificada en la RFC 5936, envía al secundario la zona entera, todos los registros, cada vez. Eso es simple y siempre correcto, pero derrochador para una zona grande donde solo ha cambiado un puñado de registros. Una transferencia de zona incremental (IXFR), definida en la RFC 1995, envía solo las diferencias entre la versión que el secundario ya tiene y la versión actual en el primario: el primario lleva el registro de los deltas entre versiones recientes de la zona precisamente para poder responder “esto es lo que ha cambiado desde el serial N” sin reenviar la zona entera. En la práctica, un secundario pide una IXFR cuando tiene un serial suficientemente reciente, y cae de vuelta a una AXFR completa cuando no lo tiene (un secundario recién añadido, o uno cuyo serial es demasiado antiguo como para que el primario haya conservado deltas para él).
La autenticación importa aquí tanto como el propio mecanismo de transferencia, y no es algo que se pueda saltar: una transferencia de zona sin autenticar le entregará tu zona completa (cada nombre de host, cada nombre interno que preferirías no publicar) a cualquier cosa que pregunte amablemente por el puerto correcto. TSIG, las firmas de transacción definidas en la RFC 2845, permite a un secundario y a un primario compartir una clave secreta de forma que el secundario solo acepta una transferencia que esté firmada por un primario que posea esa clave, y el primario solo sirve una transferencia a una solicitud firmada de la misma manera. Sin eso, “transferencia de zona” es en realidad solo “cualquiera que pueda alcanzar este puerto puede volcar tu DNS”, lo cual anula buena parte de la razón para centralizar los datos de zona en primer lugar: habrías sustituido la inconsistencia manual por una filtración automatizada.
Por qué el resolver solo recursa para los nombres que no posee
La otra mitad de esta configuración es qué ocurre cuando llega una consulta para un nombre sobre el que esta infraestructura no tiene autoridad: algún sitio público por ahí en internet, por ejemplo. El resolver no intenta tener una opinión sobre eso; recursa, recorriendo la jerarquía normal de DNS hacia el internet público igual que lo haría cualquier resolver recursivo, y cachea la respuesta durante el TTL habitual. Los dos comportamientos se mantienen deliberadamente separados: las respuestas autoritativas para las propias zonas de esta infraestructura vienen solo de datos replicados desde el único primario, y todo lo demás se resuelve de nuevo (o desde caché) mediante recursión ordinaria. Un resolver que difuminara esos dos papeles (que estuviera dispuesto a responder de forma autoritativa por zonas para las que no se mantenía sincronizado, o que cacheara respuestas con apariencia autoritativa para nombres que no posee) es un resolver que puede empezar a dar respuestas incorrectas sobre su propia infraestructura sin ninguna señal clara de que algo va mal. Mantener “de qué soy autoritativo” y “para qué simplemente recurso” como dos categorías claramente separadas es lo que permite razonar sobre qué respuestas están garantizadas como correctas (respaldadas por los datos de zona del primario) y cuáles son simplemente lo que diga en ese momento la jerarquía DNS más amplia.
Lo que esto aporta, en concreto
La propiedad que realmente quería conseguir con esto no es sofisticada: cambiar un registro una vez, y saber que todos los servidores que respondan por esta infraestructura acabarán (normalmente en segundos, gracias a NOTIFY) respondiendo con ese mismo cambio, sin ningún paso en el que tenga que acordarme de actualizar también el servidor dos y el servidor tres. Añadir un nuevo secundario ahora es “apuntarlo al primario con la clave correcta” en lugar de “copiar el archivo de zona de hoy y esperar que no se desvíe a partir de aquí”. Y como la transferencia está autenticada, añadir ese secundario no tiene como coste entregar la zona a cualquier cosa en la red que pida por ella.
No es una configuración sofisticada según los estándares de infraestructura DNS: es exactamente el modelo primario/secundario para el que se diseñó el DNS desde el principio, no algo novedoso. Lo que sustituyó era peor específicamente porque era manual, y la sincronización manual de cualquier cosa que necesite ser consistente en más de un sitio es un problema que empeora, no mejora, cuanto más tiempo lo dejas sin automatizar.
El serial del SOA es lo único de lo que depende todo este modelo
Todo lo anterior depende de un detalle pequeño y fácil de estropear: el número de serie del registro SOA tiene que aumentar cada vez que la zona cambia, y tiene que aumentar de forma monótona. Ese serial es lo que un secundario compara contra su propio último valor conocido para decidir “¿hay algo nuevo que buscar?”. Increméntalo de forma inconsistente (olvida incrementarlo tras una edición, o retrocédelo al restaurar un archivo de zona antiguo desde un backup) y todo el modelo de replicación deja de funcionar en silencio mientras parece que todo va bien. Un secundario que ya tiene un serial igual o mayor que el actual del primario no tiene ninguna razón para pedir una transferencia, así que un cambio de zona hecho sin incrementar el serial puede quedarse sin replicar indefinidamente, sin error, sin línea de log, nada salvo secundarios respondiendo en silencio con datos obsoletos. Este es exactamente el tipo de fallo que esta configuración se suponía que iba a eliminar, solo que reubicado un nivel más abajo: en lugar de olvidar sincronizar un registro, es posible olvidar sincronizar la señal de que un registro cambió. La mayoría del tooling que gestiona archivos de zona maneja el incremento automáticamente hoy en día, que es la razón principal por la que esto no es un riesgo operativo mayor de lo que suena sobre el papel, pero merece la pena nombrarlo como el único punto donde la corrección de todo el mecanismo descansa sobre un detalle que no se autoverifica.
Lo que realmente vigilo
Dado ese modo de fallo, lo que importa monitorizar aquí no es “¿está el primario activo?”: es “¿están los secundarios de acuerdo con el primario?”. Una comprobación periódica que consulta el serial del SOA en cada secundario y lo compara contra el serial actual del primario detecta exactamente el fallo descrito arriba: un secundario atascado en un serial antiguo porque una transferencia falló en silencio, quedó bloqueada por un cambio de firewall, o nunca se disparó porque el NOTIFY no le llegó. Esa comprobación es aburrida y no la ejecuto tan a menudo como probablemente debería, pero es la única validación que confirma la propiedad que toda esta configuración existe para garantizar: no “el mecanismo está configurado”, sino “el mecanismo está funcionando ahora mismo y la respuesta de cada secundario coincide realmente con la del primario”.
La alternativa que no quería recuperar
Merece la pena ser explícito sobre lo que significaría “sincronización manual” si alguna vez tuviera que volver a ella, porque esa comparación es lo que justifica las piezas móviles extra de aquí. Sincronización manual significa: cada cambio de registro se aplica a mano en cada servidor autoritativo para esa zona, en el mismo orden, sin una errata, cada vez, mientras exista más de un servidor. No hay ningún protocolo que garantice que las copias coincidan: solo hay disciplina, y la disciplina es lo único que se degrada de forma fiable bajo presión de tiempo, cansancio, o simplemente no ser lo que tienes en la cabeza en el momento en que estás haciendo un cambio no relacionado en otro sitio. La transferencia de zona no es un objetivo más sofisticado del que perseguía la sincronización manual: es el mismo objetivo, “todo servidor autoritativo está de acuerdo”, conseguido mediante un mecanismo en lugar de mediante el acordarse. Ese es un cambio técnico pequeño con un efecto desproporcionado en cuánto confío en la respuesta que da cualquier servidor dado.