DNS Anycast con BIRD2 y BGP: configuración multi-ubicación
Despliega DNS anycast en múltiples ubicaciones VPS con BIRD2 y BIND9. Anuncio de rutas BGP, sincronización de zonas con TSIG, failover por health-check y hardening con nftables.
El DNS anycast asigna la misma dirección IP a servidores DNS en múltiples ubicaciones. Cada servidor anuncia la IP mediante BGP. Los routers dirigen las consultas al servidor más cercano según la topología de red. Si un servidor falla y retira su ruta BGP, las consultas alcanzan automáticamente el siguiente servidor más cercano. Esto proporciona resolución DNS de baja latencia y failover automático sin cambios en el lado del cliente.
Esta guía despliega un DNS anycast en producción en dos nodos VPS de Virtua usando BIRD2 como demonio de enrutamiento y BIND9 como servidor DNS autoritativo. La seguridad está integrada en cada paso, no añadida al final.
Lo que vas a construir:
- Dos nodos VPS en ubicaciones distintas, cada uno anunciando el mismo prefijo /24 vía BGP
- BIND9 como servidor DNS autoritativo escuchando en la IP anycast
- Sincronización de zonas entre nodos mediante transferencias autenticadas con TSIG
- Un script de health-check que retira las rutas BGP cuando el DNS falla
- Reglas de firewall nftables asegurando cada nodo
¿Cuáles son los requisitos previos para DNS anycast?
Antes de empezar, necesitas estos recursos ya aprovisionados. Si nunca has configurado BGP en un VPS, lee primero Configuración de BGP con BIRD2 en un VPS Linux. Para crear registros ROA, consulta RPKI ROA para BGP: crear ROAs, validar rutas en BIRD2 y FRR.
| Requisito | Detalles |
|---|---|
| ASN | Tu propio ASN (ej. AS212345), registrado en un RIR |
| Prefijo IPv4 | Al menos un /24 (ej. 198.51.100.0/24). Los prefijos más largos que /24 son filtrados por la mayoría de proveedores de tránsito. |
| Prefijo IPv6 | Al menos un /48 (ej. 2001:db8:abcd::/48) |
| Registros ROA | Creados en el portal de tu RIR para ambos prefijos, autorizando tu ASN |
| Nodos VPS | 2+ VPS Virtua en ubicaciones distintas, cada uno con sesión BGP al router upstream |
| Info BGP upstream | IP y ASN del vecino para cada nodo, proporcionados por Virtua |
| Debian 12 | Esta guía usa Debian Bookworm. Adapta los nombres de paquetes para otras distribuciones. |
A lo largo de esta guía usamos estos valores de ejemplo. Reemplázalos con los tuyos:
| Variable | Valor |
|---|---|
| Tu ASN | 212345 |
| IPv4 anycast | 198.51.100.1 |
| Prefijo anycast | 198.51.100.0/24 |
| IPv6 anycast | 2001:db8:abcd::1 |
| Prefijo IPv6 anycast | 2001:db8:abcd::/48 |
| Nodo A (Fráncfort) IP principal | 203.0.113.10 |
| Nodo A vecino upstream | 169.254.169.1 AS 64496 |
| Nodo B (Ámsterdam) IP principal | 203.0.113.20 |
| Nodo B vecino upstream | 169.254.169.1 AS 64496 |
| Zona DNS | example.com |
¿Cómo es la topología de red?
┌─────────────┐
│ Internet │
└──────┬───────┘
│
┌────────────┴────────────┐
│ │
┌────────┴────────┐ ┌────────┴────────┐
│ Router Virtua │ │ Router Virtua │
│ Fráncfort │ │ Ámsterdam │
│ AS 64496 │ │ AS 64496 │
└────────┬────────┘ └────────┬────────┘
│ eBGP │ eBGP
┌────────┴────────┐ ┌────────┴────────┐
│ Nodo A (VPS) │ │ Nodo B (VPS) │
│ AS 212345 │ │ AS 212345 │
│ BIRD2 + BIND9 │ │ BIRD2 + BIND9 │
│ anycast0: │ │ anycast0: │
│ 198.51.100.1/24 │ │ 198.51.100.1/24 │
│ 2001:db8:abcd::1 │ │ 2001:db8:abcd::1 │
└──────────────────┘ └──────────────────┘
│ │
│ AXFR + TSIG (vía │
│ IPs principales) │
└─────────────────────────┘
Ambos nodos anuncian 198.51.100.0/24 y 2001:db8:abcd::/48 vía BGP. Los clientes alcanzan el nodo topológicamente más cercano. Las transferencias de zona se realizan por las IP principales (unicast), no por la dirección anycast.
¿Cómo creo la interfaz loopback anycast?
Cada nodo necesita la IP anycast asignada a una interfaz local. Una interfaz dummy es la mejor opción porque siempre está activa y no tiene dependencia física. Configúrala con systemd-networkd para que persista entre reinicios.
Crea el archivo netdev:
cat > /etc/systemd/network/10-anycast.netdev << 'EOF'
[NetDev]
Name=anycast0
Kind=dummy
EOF
Crea el archivo network para asignar las IP anycast. Usa la longitud de prefijo completa (/24 para IPv4, /48 para IPv6) para que el protocolo direct en BIRD2 vea rutas conectadas que coincidan con los prefijos que quieres anunciar:
cat > /etc/systemd/network/10-anycast.network << 'EOF'
[Match]
Name=anycast0
[Network]
Address=198.51.100.1/24
Address=2001:db8:abcd::1/48
EOF
Habilita e inicia systemd-networkd. Si tu VPS usa ifupdown para la interfaz principal (comprueba /etc/network/interfaces), no uses systemctl restart systemd-networkd ya que puede interrumpir la red principal. El inicio inicial es seguro porque systemd-networkd solo gestiona interfaces que tienen archivos de configuración en /etc/systemd/network/:
systemctl enable --now systemd-networkd
ip addr show anycast0
Salida esperada:
3: anycast0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether a2:b4:c6:d8:e0:f2 brd ff:ff:ff:ff:ff:ff
inet 198.51.100.1/24 brd 198.51.100.255 scope global anycast0
valid_lft forever preferred_lft forever
inet6 2001:db8:abcd::1/48 scope global
valid_lft forever preferred_lft forever
El estado de la interfaz muestra UNKNOWN, lo cual es normal para interfaces dummy. Significa que la capa de enlace siempre está activa. Ambas direcciones IPv4 e IPv6 deben aparecer.
Repite esto en cada nodo anycast. La configuración es idéntica.
¿Qué configuración de BIRD2 se necesita para el anuncio de rutas anycast?
BIRD2 anuncia el prefijo anycast al router upstream mediante eBGP. El diseño clave: BIRD2 solo exporta rutas aprendidas de la interfaz anycast0 a través del protocolo direct. Cuando la interfaz cae (o el script de health-check la desactiva), BIRD2 retira la ruta automáticamente.
Instala BIRD2:
apt update && apt install -y bird2
Debian 12 incluye BIRD 2.0.12. Para funcionalidades más recientes (mejoras en BFD, filtros avanzados), añade el repositorio oficial de BIRD.
Configuración del nodo A (/etc/bird/bird.conf)
# /etc/bird/bird.conf -- Node A (Frankfurt)
log syslog all;
router id 203.0.113.10;
# Scan interfaces every 10 seconds
protocol device {
scan time 10;
}
# Learn routes from the anycast dummy interface
protocol direct anycast {
ipv4;
ipv6;
interface "anycast0";
}
# Define the prefixes we are authorized to announce
define ANYCAST_V4 = [ 198.51.100.0/24 ];
define ANYCAST_V6 = [ 2001:db8:abcd::/48 ];
# Export filter: only announce our anycast prefixes
filter export_anycast_v4 {
if net ~ ANYCAST_V4 then accept;
reject;
}
filter export_anycast_v6 {
if net ~ ANYCAST_V6 then accept;
reject;
}
# IPv4 BGP session to upstream
protocol bgp upstream4 {
description "Virtua Frankfurt upstream IPv4";
local 203.0.113.10 as 212345;
neighbor 169.254.169.1 as 64496;
password "your-bgp-md5-secret";
hold time 90;
keepalive time 30;
graceful restart on;
ipv4 {
import none;
export filter export_anycast_v4;
};
}
# IPv6 BGP session to upstream
protocol bgp upstream6 {
description "Virtua Frankfurt upstream IPv6";
local 2001:db8:1::10 as 212345;
neighbor 2001:db8:1::1 as 64496;
password "your-bgp-md5-secret";
hold time 90;
keepalive time 30;
graceful restart on;
ipv6 {
import none;
export filter export_anycast_v6;
};
}
Puntos clave:
protocol direct anycastaprende rutas solo de la interfazanycast0. Ninguna otra interfaz se filtra a la tabla de enrutamiento.- Los filtros de exportación restringen los anuncios a exactamente tu /24 y /48. Esto previene fugas de rutas accidentales.
passwordhabilita autenticación TCP MD5 (RFC 2385) en la sesión BGP. Obtén el secreto compartido de tu proveedor upstream.import nonesignifica que este nodo no acepta ninguna ruta del upstream. Los nodos anycast solo anuncian; usan la ruta por defecto del VPS para el tráfico saliente.graceful restart onreduce la inestabilidad de rutas (route flap) durante reinicios de BIRD2.
Configuración del nodo B
Copia el mismo archivo al nodo B. Modifica solo:
router id 203.0.113.20;
# In protocol bgp upstream4:
local 203.0.113.20 as 212345;
# neighbor IP and AS may differ per location, use the values Virtua provides
Inicio de BIRD2
Habilita e inicia BIRD2:
systemctl enable --now bird
enable asegura la persistencia tras reinicios. --now lo inicia inmediatamente.
Comprueba el estado:
systemctl status bird
Comprueba el estado de las sesiones BGP:
birdc show protocols
Salida esperada:
BIRD 2.0.12 ready.
Name Proto Table State Since Info
device1 Device --- up 2026-03-19
anycast Direct --- up 2026-03-19
upstream4 BGP --- up 2026-03-19 Established
upstream6 BGP --- up 2026-03-19 Established
Ambas sesiones BGP deben mostrar Established. Si ves Active o Connect en su lugar, verifica la IP del vecino, el ASN y la contraseña. Revisa los logs con journalctl -u bird -f.
Comprueba las rutas exportadas:
birdc show route export upstream4
Esperado:
BIRD 2.0.12 ready.
Table master4:
198.51.100.0/24 unicast [anycast 2026-03-19] * (240)
dev anycast0
La ruta proviene del protocolo direct anycast y se exporta a upstream4. Repite en el nodo B.
¿Cómo configuro BIND9 para escuchar en la IP anycast?
BIND9 funciona como servidor DNS exclusivamente autoritativo en cada nodo, escuchando en la IP anycast. Los clientes consultan la IP anycast; BGP se encarga de que alcancen el nodo más cercano.
Instala BIND9:
apt update && apt install -y bind9 bind9-utils
Generar una clave TSIG para transferencias de zona
Las transferencias de zona entre nodos deben estar autenticadas. Genera una clave TSIG con tsig-keygen:
tsig-keygen -a hmac-sha256 anycast-transfer > /etc/bind/anycast-transfer.key
La salida se ve así:
cat /etc/bind/anycast-transfer.key
key "anycast-transfer" {
algorithm hmac-sha256;
secret "base64-encoded-secret-here";
};
Copia este archivo exacto a todos los nodos. El nombre de la clave y el secreto deben coincidir en todas partes.
Establece permisos restrictivos:
chown root:bind /etc/bind/anycast-transfer.key
chmod 640 /etc/bind/anycast-transfer.key
El archivo debe mostrar propiedad root:bind y permisos 640:
ls -la /etc/bind/anycast-transfer.key
-rw-r----- 1 root bind 113 Mar 19 12:00 /etc/bind/anycast-transfer.key
Configuración del nodo A (primario)
Edita /etc/bind/named.conf.options:
options {
directory "/var/cache/bind";
// Listen only on the anycast IP and localhost
listen-on { 198.51.100.1; 127.0.0.1; };
listen-on-v6 { 2001:db8:abcd::1; ::1; };
// Authoritative only, no recursion
recursion no;
allow-recursion { none; };
// Hide version to avoid targeted exploits
version "not disclosed";
// Disable zone transfers by default
allow-transfer { none; };
// Rate limiting to mitigate DNS amplification
rate-limit {
responses-per-second 10;
window 5;
};
dnssec-validation auto;
};
Incluye la clave TSIG. Edita /etc/bind/named.conf.local:
include "/etc/bind/anycast-transfer.key";
// Allow transfers only to Node B using TSIG
acl "secondaries" {
key "anycast-transfer";
};
zone "example.com" {
type primary;
file "/var/lib/bind/db.example.com";
allow-transfer { secondaries; };
also-notify { 203.0.113.20; };
notify yes;
};
Crea el archivo de zona /var/lib/bind/db.example.com:
$TTL 300
@ IN SOA ns1.example.com. admin.example.com. (
2026031901 ; Serial (YYYYMMDDNN)
3600 ; Refresh (1 hour)
900 ; Retry (15 minutes)
604800 ; Expire (1 week)
300 ; Negative cache TTL (5 minutes)
)
@ IN NS ns1.example.com.
@ IN NS ns2.example.com.
; NS records point to the anycast IP, same IP, different names
ns1 IN A 198.51.100.1
ns1 IN AAAA 2001:db8:abcd::1
ns2 IN A 198.51.100.1
ns2 IN AAAA 2001:db8:abcd::1
; Your records
@ IN A 198.51.100.10
@ IN AAAA 2001:db8:abcd::10
www IN CNAME example.com.
mail IN A 198.51.100.25
@ IN MX 10 mail.example.com.
Establece el propietario:
chown bind:bind /var/lib/bind/db.example.com
chmod 640 /var/lib/bind/db.example.com
Configuración del nodo B (secundario)
En el nodo B, /etc/bind/named.conf.options es idéntico al nodo A.
/etc/bind/named.conf.local difiere:
include "/etc/bind/anycast-transfer.key";
server 203.0.113.10 {
keys { "anycast-transfer"; };
};
zone "example.com" {
type secondary;
file "/var/lib/bind/db.example.com";
primaries { 203.0.113.10 key "anycast-transfer"; };
};
La directiva server le indica a BIND9 que autentique toda comunicación con el nodo A usando la clave TSIG. Las transferencias de zona son automáticas una vez configurado esto.
Inicio de BIND9
Habilita e inicia en ambos nodos:
systemctl enable --now named
Comprueba el estado:
systemctl status named
Prueba la resolución DNS contra la IP anycast:
dig @198.51.100.1 example.com A +short
Esperado:
198.51.100.10
En el nodo B, comprueba que la zona se transfirió:
dig @198.51.100.1 example.com SOA +short
ns1.example.com. admin.example.com. 2026031901 3600 900 604800 300
Revisa los logs de BIND9 para la transferencia:
journalctl -u named --no-pager | grep "transfer of"
Esperado:
transfer of 'example.com/IN' from 203.0.113.10#53: Transfer status: success
El número de serie debe coincidir en ambos nodos. Si el secundario muestra un número diferente, la transferencia falló. Verifica la consistencia de la clave TSIG y las reglas de firewall.
¿Qué reglas de firewall necesita un nodo DNS anycast BGP?
Asegura cada nodo con nftables. Solo permite lo necesario: SSH para administración, BGP desde el router upstream y DNS desde cualquier lugar.
Crea /etc/nftables.conf:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# Connection tracking
ct state established,related accept
ct state invalid drop
# Loopback
iif lo accept
# ICMP and ICMPv6 (needed for path MTU discovery and diagnostics)
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
# SSH (restrict to your management IPs in production)
tcp dport 22 accept
# BGP from upstream router only (IPv4)
ip saddr 169.254.169.1 tcp dport 179 accept
ip saddr 169.254.169.1 tcp sport 179 accept
# BGP from upstream router only (IPv6)
ip6 saddr 2001:db8:1::1 tcp dport 179 accept
ip6 saddr 2001:db8:1::1 tcp sport 179 accept
# DNS on anycast IP
ip daddr 198.51.100.1 udp dport 53 accept
ip daddr 198.51.100.1 tcp dport 53 accept
ip6 daddr 2001:db8:abcd::1 udp dport 53 accept
ip6 daddr 2001:db8:abcd::1 tcp dport 53 accept
# Zone transfers from the other node (TSIG-authenticated at app layer,
# but we also restrict at network layer)
ip saddr 203.0.113.20 tcp dport 53 accept
ip saddr 203.0.113.10 tcp dport 53 accept
# Log and drop everything else
log prefix "nftables-drop: " limit rate 5/minute
drop
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
Adapta las líneas ip saddr e ip6 saddr en el nodo B (intercambia las IP del peer de transferencia de zona y usa el vecino IPv6 upstream del nodo B). En producción, restringe SSH a tu CIDR de administración.
Aplica y habilita:
systemctl enable --now nftables
Lista las reglas activas para confirmar que se cargaron:
nft list ruleset
Después comprueba que el DNS sigue funcionando desde fuera del servidor:
# Run this from your local machine, NOT the server
dig @198.51.100.1 example.com A +short
Si el DNS deja de funcionar tras aplicar las reglas de firewall, verifica que el daddr coincide exactamente con tu IP anycast y que la interfaz anycast0 está activa.
¿Cómo verifico el DNS por health-check y retiro rutas BGP en caso de fallo?
Un script de health-check monitoriza BIND9 y señala a BIRD2 que retire la ruta anycast cuando el DNS cae. El método: desactivar la interfaz anycast0, lo que hace que BIRD2 retire el prefijo automáticamente ya que el protocolo direct deja de ver la ruta.
¿Por qué no confiar solo en los timers BGP?
Con los timers BGP por defecto (90 s hold, 30 s keepalive), el failover tarda hasta 90 segundos. Un health-check detecta el fallo DNS en segundos y activa la retirada inmediatamente. La tabla siguiente muestra el impacto:
| Método | Tiempo de detección | Convergencia |
|---|---|---|
| Expiración del hold timer BGP | 90 segundos | 90-180 segundos |
| BFD (si disponible) | <1 segundo | 1-3 segundos |
| Script de health-check | 5-15 segundos | 35-45 segundos (+ propagación BGP) |
BFD solo detecta fallos de enlace/sesión, no fallos de aplicación. El script de health-check detecta crashes de BIND9, errores de configuración y discos llenos que BFD no puede ver.
Crear un usuario dedicado
El health-check se ejecuta como un usuario dedicado con privilegios mínimos:
useradd --system --no-create-home --shell /usr/sbin/nologin anycast-healthcheck
Concédele permiso para controlar la interfaz anycast. Crea una regla sudoers:
cat > /etc/sudoers.d/anycast-healthcheck << 'EOF'
anycast-healthcheck ALL=(root) NOPASSWD: /usr/sbin/ip link set anycast0 up, /usr/sbin/ip link set anycast0 down
EOF
chmod 440 /etc/sudoers.d/anycast-healthcheck
Escribir el script de health-check
Crea /usr/local/bin/anycast-healthcheck.sh:
#!/bin/bash
# Anycast DNS health check with hysteresis
# Withdraws BGP route by downing anycast0 when BIND9 is unhealthy
set -euo pipefail
ANYCAST_IF="anycast0"
CHECK_IP="127.0.0.1"
CHECK_DOMAIN="example.com"
CHECK_TYPE="SOA"
# Hysteresis: 3 failures to withdraw, 2 successes to re-announce
FAIL_THRESHOLD=3
RECOVER_THRESHOLD=2
CHECK_INTERVAL=5
fail_count=0
recover_count=0
is_withdrawn=false
log() {
logger -t anycast-healthcheck "$1"
}
check_dns() {
dig +time=2 +tries=1 @"${CHECK_IP}" "${CHECK_DOMAIN}" "${CHECK_TYPE}" > /dev/null 2>&1
}
withdraw_route() {
if [ "$is_withdrawn" = false ]; then
sudo /usr/sbin/ip link set "${ANYCAST_IF}" down
is_withdrawn=true
log "WITHDRAW: ${ANYCAST_IF} down after ${FAIL_THRESHOLD} consecutive failures"
fi
}
announce_route() {
if [ "$is_withdrawn" = true ]; then
sudo /usr/sbin/ip link set "${ANYCAST_IF}" up
is_withdrawn=true # will be set to false after verification
# Verify the interface came back
sleep 1
if ip link show "${ANYCAST_IF}" | grep -q "UP"; then
is_withdrawn=false
recover_count=0
log "ANNOUNCE: ${ANYCAST_IF} up after ${RECOVER_THRESHOLD} consecutive successes"
else
log "ERROR: failed to bring ${ANYCAST_IF} up"
fi
fi
}
log "Starting anycast health check for ${CHECK_DOMAIN} on ${CHECK_IP}"
while true; do
if check_dns; then
fail_count=0
recover_count=$((recover_count + 1))
if [ "$recover_count" -ge "$RECOVER_THRESHOLD" ]; then
announce_route
fi
else
recover_count=0
fail_count=$((fail_count + 1))
log "DNS check failed (${fail_count}/${FAIL_THRESHOLD})"
if [ "$fail_count" -ge "$FAIL_THRESHOLD" ]; then
withdraw_route
fi
fi
sleep "${CHECK_INTERVAL}"
done
Establece los permisos:
chmod 755 /usr/local/bin/anycast-healthcheck.sh
Crear un servicio systemd
Crea /etc/systemd/system/anycast-healthcheck.service:
[Unit]
Description=Anycast DNS health check
After=bird.service named.service
Wants=bird.service named.service
[Service]
Type=simple
User=anycast-healthcheck
ExecStart=/usr/local/bin/anycast-healthcheck.sh
Restart=always
RestartSec=5
# Hardening
NoNewPrivileges=no
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadOnlyPaths=/
ReadWritePaths=/run
[Install]
WantedBy=multi-user.target
NoNewPrivileges=no es necesario aquí porque el script usa sudo para conmutar la interfaz. La regla sudoers restringe qué comandos puede ejecutar el usuario.
La sección [Unit] usa Wants= en lugar de Requires=. Con Requires=, systemd detendría el health-check cuando named se detenga. Eso es exactamente lo contrario de lo deseado: el health-check debe seguir ejecutándose para detectar el fallo DNS y retirar la ruta.
Habilita e inicia:
systemctl daemon-reload
systemctl enable --now anycast-healthcheck
Comprueba que está ejecutándose:
systemctl status anycast-healthcheck
Los logs deben mostrar el mensaje de inicio:
journalctl -u anycast-healthcheck -f
Starting anycast health check for example.com on 127.0.0.1
¿Cómo pruebo el failover de DNS anycast?
Ejecuta estas pruebas desde una máquina externa, no desde ninguno de los nodos.
Paso 1: confirmar que ambos nodos anuncian
Desde tu máquina local:
dig @198.51.100.1 example.com A +short
Confirma que recibes una respuesta. Ejecuta un traceroute para ver qué nodo alcanzas:
traceroute -n 198.51.100.1
La ruta muestra qué ubicación está más cerca de ti.
Paso 2: simular fallo DNS en el nodo A
En el nodo A, detén BIND9:
systemctl stop named
Observa los logs del health-check:
journalctl -u anycast-healthcheck -f
Secuencia esperada:
DNS check failed (1/3)
DNS check failed (2/3)
DNS check failed (3/3)
WITHDRAW: anycast0 down after 3 consecutive failures
Después de 15 segundos (3 comprobaciones a intervalos de 5 segundos), el health-check desactiva anycast0. BIRD2 detecta el cambio de interfaz y retira la ruta.
Comprueba que la ruta ha desaparecido:
birdc show route export upstream4
La tabla debe estar vacía. El router upstream elimina 198.51.100.0/24 del nodo A y redirige todo el tráfico al nodo B.
Paso 3: comprobar el failover del cliente
Desde tu máquina externa, consulta de nuevo:
dig @198.51.100.1 example.com A +short +time=5
La primera consulta tras la retirada puede expirar (la ruta antigua sigue en caché en algunos routers). Las consultas siguientes alcanzan el nodo B. La convergencia BGP tarda normalmente entre 30 y 90 segundos dependiendo de la configuración de tu upstream.
Paso 4: restaurar el nodo A
systemctl start named
El health-check detecta que el DNS vuelve tras 2 éxitos consecutivos (10 segundos) y reactiva anycast0. BIRD2 vuelve a anunciar la ruta.
journalctl -u anycast-healthcheck --no-pager | tail -5
Esperado:
ANNOUNCE: anycast0 up after 2 consecutive successes
Comprueba las sesiones BGP:
birdc show protocols
Ambas sesiones BGP deben mostrar Established de nuevo.
Paso 5: medir el tiempo de convergencia
Para una medición precisa, ejecuta un bucle dig continuo desde una máquina externa:
while true; do
echo -n "$(date +%H:%M:%S) "
dig @198.51.100.1 example.com A +short +time=1 +tries=1 || echo "TIMEOUT"
sleep 1
done
Detén BIND9 en el nodo que estás alcanzando actualmente. Cuenta los segundos entre la última respuesta exitosa y la primera respuesta exitosa del otro nodo. En infraestructura Virtua con timers BGP por defecto, espera entre 30 y 60 segundos de indisponibilidad parcial.
¿Cómo se compara BIRD2 con otros demonios de enrutamiento para anycast?
| Característica | BIRD2 | FRRouting | ExaBGP |
|---|---|---|---|
| Lenguaje de configuración | Filtros declarativos | CLI tipo Cisco | JSON/API |
| Integración anycast | Protocolo direct sobre interfaz dummy | Ruta estática + redistribución | Script externo anuncia/retira |
| Método de health-check | Up/down de interfaz dispara cambio de ruta | Comandos vtysh desde script | Gestor de procesos integrado |
| Soporte IPv6 | Configuración unificada (canales ipv4/ipv6) | Familias de direcciones separadas | Manual por familia |
| Complejidad de filtros | Lenguaje de filtros completo con funciones | Route maps, prefix lists | Solo lógica externa |
| Huella de memoria | Baja (~10 MB para tabla pequeña) | Media (~30 MB) | Muy baja (~5 MB) |
| Madurez en producción | IXPs, DNS a gran escala (Cloudflare, RIPE) | ISPs, centros de datos | Despliegues pequeños, monitorización |
La ventaja de BIRD2 para anycast: el protocol direct sobre una interfaz dummy ofrece retirada automática de ruta cuando la interfaz cae. Sin necesidad de scripting externo para la parte BGP. El health-check solo necesita conmutar la interfaz.
¿Cómo sincronizo zonas DNS entre nodos anycast?
La sincronización de zonas usa la replicación primario/secundario integrada en BIND9 mediante AXFR, autenticada con TSIG. La configuración se realizó en la sección BIND9 anterior. Aquí los detalles operativos.
Las actualizaciones de zona fluyen en una dirección: edita el archivo de zona en el nodo A (primario), incrementa el número de serie y recarga:
# On Node A after editing the zone file
named-checkzone example.com /var/lib/bind/db.example.com
zone example.com/IN: loaded serial 2026031902
OK
Ejecuta siempre named-checkzone antes de recargar. Detecta errores de sintaxis.
rndc reload example.com
El nodo B recibe un NOTIFY del nodo A y descarga la zona actualizada vía AXFR. En el nodo B, comprueba el número de serie:
dig @127.0.0.1 example.com SOA +short
El número de serie debe coincidir. Si no coincide, comprueba:
- El firewall permite TCP 53 del nodo A al nodo B
- La clave TSIG es idéntica en ambos nodos
journalctl -u nameden el nodo B para errores de transferencia
Añadir un tercer nodo: configúralo como otro secundario. Añade su IP a la lista also-notify y a las reglas nftables del nodo A. No se necesitan cambios en los secundarios existentes.
¿Qué hay del BGP anycast en IPv6?
La configuración BIRD2 anterior ya incluye IPv6. El bloque protocol direct anycast tiene ambos canales ipv4 e ipv6. La sesión BGP upstream6 exporta el prefijo /48.
Comprueba la exportación de rutas IPv6:
birdc show route export upstream6
BIRD 2.0.12 ready.
Table master6:
2001:db8:abcd::/48 unicast [anycast 2026-03-19] * (240)
dev anycast0
Prueba DNS sobre IPv6:
dig @2001:db8:abcd::1 example.com AAAA +short
Resolución de problemas
Sesión BGP atascada en Active/Connect:
journalctl -u bird -f
Causas comunes: IP de vecino incorrecta, ASN incorrecto, contraseña MD5 incorrecta, firewall bloqueando TCP 179. Ejecuta birdc show protocols all upstream4 para mensajes de error detallados.
BIND9 no escucha en la IP anycast:
ss -tlnp | grep ':53'
Si solo ves 127.0.0.1:53, verifica que listen-on incluye la IP anycast y que la interfaz anycast0 estaba activa antes de iniciar BIND9. Reinicia BIND9 después de activar la interfaz: systemctl restart named.
Fallo en la transferencia de zona:
journalctl -u named | grep -i "tsig\|transfer\|refused"
Comprueba que el archivo de clave es legible por el usuario bind: ls -la /etc/bind/anycast-transfer.key. Ejecuta una prueba de transferencia manual: dig @203.0.113.10 example.com AXFR -k /etc/bind/anycast-transfer.key.
El health-check no retira las rutas:
journalctl -u anycast-healthcheck -f
Comprueba permisos sudo: sudo -u anycast-healthcheck sudo -l. El usuario debe poder ejecutar /usr/sbin/ip link set anycast0 up y down sin contraseña.
Consultas con timeout tras el failover:
Los clientes DNS cachean las respuestas. El TTL en el archivo de zona (300 segundos / 5 minutos) determina cuánto tiempo los clientes usan datos obsoletos. La convergencia BGP añade 30-90 segundos. Tiempo total de failover desde la perspectiva del cliente: hasta el TTL más el tiempo de convergencia. Reduce el TTL mínimo del SOA si un failover más rápido es necesario, pero no bajes de 60 segundos para zonas autoritativas.
Checklist de producción
Antes de pasar a producción:
- Registros ROA creados y validados (
rpki-cliento portal de tu RIR) - Sesiones BGP establecidas en todos los nodos (
birdc show protocols) - Prefijo anycast visible en tablas de enrutamiento globales (usa un looking glass)
- BIND9 respondiendo en la IP anycast desde redes externas
- Número de serie de zona coincide en todos los nodos
- Permisos de clave TSIG en 640, propietario root:bind
- Reglas nftables activas en todos los nodos
- Servicio de health-check habilitado y ejecutándose
- Failover probado: BIND9 detenido, retirada de ruta confirmada, recuperación confirmada
- Monitorización configurada: alertas por sesión BGP caída, proceso BIND9, fallos del health-check
-
journalctl -u birdyjournalctl -u namedno muestran errores
Para la guía principal sobre fundamentos BGP y traer tu propio espacio IP, consulta BGP y Bring Your Own IP en un VPS: la guía completa. Para filtrado de rutas BGP y hardening de seguridad, consulta Filtrado de rutas BGP: Prefix Lists, Filtros AS-Path, Rechazo de Bogons y GTSM.
Copyright 2026 Virtua.Cloud. Todos los derechos reservados. Este contenido es una obra original del equipo de Virtua.Cloud. La reproducción, republicación o redistribución sin permiso escrito está prohibida.
¿Listo para probarlo?
Despliega tu propio servidor en segundos. Linux, Windows o FreeBSD.
Ver planes VPS