DNS Anycast con BIRD2 e BGP: configurazione multi-sede
Implementa DNS anycast su più sedi VPS con BIRD2 e BIND9. Annuncio di rotte BGP, sincronizzazione zone con TSIG, failover tramite health-check e hardening nftables.
Il DNS anycast assegna lo stesso indirizzo IP a server DNS in più sedi. Ogni server annuncia l'IP tramite BGP. I router indirizzano le query al server più vicino in base alla topologia di rete. Se un server va in errore e ritira la sua rotta BGP, le query raggiungono automaticamente il server successivo più vicino. Il risultato: risoluzione DNS a bassa latenza e failover automatico senza modifiche lato client.
Questa guida implementa un DNS anycast in produzione su due nodi VPS Virtua usando BIRD2 come demone di routing e BIND9 come server DNS autoritativo. La sicurezza è integrata in ogni passaggio, non aggiunta alla fine.
Cosa costruirai:
- Due nodi VPS in sedi diverse, ciascuno che annuncia lo stesso prefisso /24 via BGP
- BIND9 come server DNS autoritativo in ascolto sull'IP anycast
- Sincronizzazione delle zone tra nodi tramite trasferimenti autenticati TSIG
- Uno script di health-check che ritira le rotte BGP quando il DNS fallisce
- Regole firewall nftables per blindare ogni nodo
Quali sono i prerequisiti per il DNS anycast?
Prima di iniziare, queste risorse devono essere già provisionate. Se non hai mai configurato BGP su un VPS, leggi prima Configurazione BGP con BIRD2 su VPS Linux. Per creare record ROA, consulta RPKI ROA per BGP: creare ROA, validare le rotte in BIRD2 e FRR.
| Requisito | Dettagli |
|---|---|
| ASN | Il tuo ASN (es. AS212345), registrato presso un RIR |
| Prefisso IPv4 | Almeno un /24 (es. 198.51.100.0/24). I prefissi più lunghi di /24 vengono filtrati dalla maggior parte dei provider di transito. |
| Prefisso IPv6 | Almeno un /48 (es. 2001:db8:abcd::/48) |
| Record ROA | Creati nel portale del tuo RIR per entrambi i prefissi, che autorizzano il tuo ASN |
| Nodi VPS | 2+ VPS Virtua in sedi diverse, ciascuno con sessione BGP verso il router upstream |
| Info BGP upstream | IP e ASN del neighbor per ogni nodo, forniti da Virtua |
| Debian 12 | Questa guida usa Debian Bookworm. Adatta i nomi dei pacchetti per altre distribuzioni. |
In questa guida usiamo i seguenti valori di esempio. Sostituiscili con i tuoi:
| Variabile | Valore |
|---|---|
| Il tuo ASN | 212345 |
| IPv4 anycast | 198.51.100.1 |
| Prefisso anycast | 198.51.100.0/24 |
| IPv6 anycast | 2001:db8:abcd::1 |
| Prefisso IPv6 anycast | 2001:db8:abcd::/48 |
| Nodo A (Francoforte) IP principale | 203.0.113.10 |
| Nodo A neighbor upstream | 169.254.169.1 AS 64496 |
| Nodo B (Amsterdam) IP principale | 203.0.113.20 |
| Nodo B neighbor upstream | 169.254.169.1 AS 64496 |
| Zona DNS | example.com |
Com'è la topologia di rete?
┌─────────────┐
│ Internet │
└──────┬───────┘
│
┌────────────┴────────────┐
│ │
┌────────┴────────┐ ┌────────┴────────┐
│ Router Virtua │ │ Router Virtua │
│ Francoforte │ │ Amsterdam │
│ 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 (tramite │
│ IP principali) │
└─────────────────────────┘
Entrambi i nodi annunciano 198.51.100.0/24 e 2001:db8:abcd::/48 via BGP. I client raggiungono il nodo topologicamente più vicino. I trasferimenti di zona avvengono sugli IP principali (unicast), non sull'indirizzo anycast.
Come creo l'interfaccia loopback anycast?
Ogni nodo ha bisogno dell'IP anycast assegnato a un'interfaccia locale. Un'interfaccia dummy è la scelta migliore perché è sempre attiva e non ha dipendenze fisiche. Configurala con systemd-networkd per renderla persistente ai riavvii.
Crea il file netdev:
cat > /etc/systemd/network/10-anycast.netdev << 'EOF'
[NetDev]
Name=anycast0
Kind=dummy
EOF
Crea il file network per assegnare gli IP anycast. Usa la lunghezza completa del prefisso (/24 per IPv4, /48 per IPv6) affinché il protocollo direct in BIRD2 veda rotte connesse che corrispondono ai prefissi da annunciare:
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
Abilita e avvia systemd-networkd. Se il tuo VPS usa ifupdown per l'interfaccia principale (controlla /etc/network/interfaces), non usare systemctl restart systemd-networkd perché potrebbe interrompere la rete principale. L'avvio iniziale è sicuro perché systemd-networkd gestisce solo le interfacce che hanno file di configurazione in /etc/systemd/network/:
systemctl enable --now systemd-networkd
ip addr show anycast0
Output atteso:
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
Lo stato dell'interfaccia mostra UNKNOWN, che è normale per le interfacce dummy. Significa che il livello di collegamento è sempre attivo. Entrambi gli indirizzi IPv4 e IPv6 devono apparire.
Ripeti su ogni nodo anycast. La configurazione è identica.
Quale configurazione BIRD2 serve per l'annuncio di rotte anycast?
BIRD2 annuncia il prefisso anycast al router upstream via eBGP. Il design chiave: BIRD2 esporta solo le rotte apprese dall'interfaccia anycast0 tramite il protocollo direct. Quando l'interfaccia va giù (o lo script di health-check la disattiva), BIRD2 ritira automaticamente la rotta.
Installa BIRD2:
apt update && apt install -y bird2
Debian 12 include BIRD 2.0.12. Per funzionalità più recenti (miglioramenti BFD, filtri avanzati), aggiungi il repository ufficiale BIRD.
Configurazione 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;
};
}
Punti chiave:
protocol direct anycastapprende rotte solo dall'interfacciaanycast0. Nessun'altra interfaccia finisce nella tabella di routing.- I filtri di esportazione limitano gli annunci esattamente al tuo /24 e /48. Questo previene leak di rotte accidentali.
passwordabilita l'autenticazione TCP MD5 (RFC 2385) sulla sessione BGP. Ottieni il segreto condiviso dal tuo provider upstream.import nonesignifica che questo nodo non accetta rotte dall'upstream. I nodi anycast annunciano soltanto; usano la rotta predefinita del VPS per il traffico in uscita.graceful restart onriduce il route flap durante i riavvii di BIRD2.
Configurazione del nodo B
Copia lo stesso file sul 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
Avvio di BIRD2
Abilita e avvia BIRD2:
systemctl enable --now bird
enable lo rende persistente ai riavvii. --now lo avvia immediatamente.
Controlla lo stato:
systemctl status bird
Controlla lo stato delle sessioni BGP:
birdc show protocols
Output atteso:
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
Entrambe le sessioni BGP devono mostrare Established. Se vedi Active o Connect, controlla l'IP del neighbor, l'ASN e la password. Consulta i log con journalctl -u bird -f.
Controlla le rotte esportate:
birdc show route export upstream4
Atteso:
BIRD 2.0.12 ready.
Table master4:
198.51.100.0/24 unicast [anycast 2026-03-19] * (240)
dev anycast0
La rotta proviene dal protocollo direct anycast ed è esportata verso upstream4. Ripeti sul nodo B.
Come configuro BIND9 per ascoltare sull'IP anycast?
BIND9 funziona come server DNS esclusivamente autoritativo su ogni nodo, in ascolto sull'IP anycast. I client interrogano l'IP anycast; BGP assicura che raggiungano il nodo più vicino.
Installa BIND9:
apt update && apt install -y bind9 bind9-utils
Generare una chiave TSIG per i trasferimenti di zona
I trasferimenti di zona tra nodi devono essere autenticati. Genera una chiave TSIG con tsig-keygen:
tsig-keygen -a hmac-sha256 anycast-transfer > /etc/bind/anycast-transfer.key
L'output è così:
cat /etc/bind/anycast-transfer.key
key "anycast-transfer" {
algorithm hmac-sha256;
secret "base64-encoded-secret-here";
};
Copia questo file identico su tutti i nodi. Il nome della chiave e il segreto devono corrispondere ovunque.
Imposta permessi restrittivi:
chown root:bind /etc/bind/anycast-transfer.key
chmod 640 /etc/bind/anycast-transfer.key
Il file deve mostrare proprietà root:bind e permessi 640:
ls -la /etc/bind/anycast-transfer.key
-rw-r----- 1 root bind 113 Mar 19 12:00 /etc/bind/anycast-transfer.key
Configurazione del nodo A (primario)
Modifica /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;
};
Includi la chiave TSIG. Modifica /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 il file di 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.
Imposta la proprietà:
chown bind:bind /var/lib/bind/db.example.com
chmod 640 /var/lib/bind/db.example.com
Configurazione del nodo B (secondario)
Sul nodo B, /etc/bind/named.conf.options è identico al nodo A.
/etc/bind/named.conf.local è diverso:
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 direttiva server indica a BIND9 di autenticare ogni comunicazione con il nodo A usando la chiave TSIG. I trasferimenti di zona sono automatici una volta configurato.
Avvio di BIND9
Abilita e avvia su entrambi i nodi:
systemctl enable --now named
Controlla lo stato:
systemctl status named
Testa la risoluzione DNS sull'IP anycast:
dig @198.51.100.1 example.com A +short
Atteso:
198.51.100.10
Sul nodo B, controlla che la zona sia stata trasferita:
dig @198.51.100.1 example.com SOA +short
ns1.example.com. admin.example.com. 2026031901 3600 900 604800 300
Controlla i log BIND9 per il trasferimento:
journalctl -u named --no-pager | grep "transfer of"
Atteso:
transfer of 'example.com/IN' from 203.0.113.10#53: Transfer status: success
Il numero di serie deve corrispondere su entrambi i nodi. Se il secondario mostra un numero diverso, il trasferimento è fallito. Controlla la coerenza della chiave TSIG e le regole del firewall.
Quali regole firewall servono per un nodo DNS anycast BGP?
Blinda ogni nodo con nftables. Permetti solo il necessario: SSH per la gestione, BGP dal router upstream e DNS da ovunque.
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;
}
}
Adatta le righe ip saddr e ip6 saddr sul nodo B (scambia gli IP dei peer per il trasferimento di zona e usa il neighbor IPv6 upstream del nodo B). In produzione, limita SSH al tuo CIDR di gestione.
Applica e abilita:
systemctl enable --now nftables
Elenca le regole attive per confermare il caricamento:
nft list ruleset
Poi testa che il DNS funzioni ancora dall'esterno:
# Run this from your local machine, NOT the server
dig @198.51.100.1 example.com A +short
Se il DNS smette di funzionare dopo l'applicazione delle regole firewall, controlla che daddr corrisponda esattamente al tuo IP anycast e che l'interfaccia anycast0 sia attiva.
Come faccio a monitorare il DNS con health-check e ritirare le rotte BGP in caso di guasto?
Uno script di health-check monitora BIND9 e segnala a BIRD2 di ritirare la rotta anycast quando il DNS è giù. Il metodo: disattivare l'interfaccia anycast0, che fa sì che BIRD2 ritiri automaticamente il prefisso poiché il protocollo direct non vede più la rotta.
Perché non affidarsi solo ai timer BGP?
Con i timer BGP predefiniti (90 s hold, 30 s keepalive), il failover richiede fino a 90 secondi. Un health-check rileva il guasto DNS in pochi secondi e attiva il ritiro immediatamente. La tabella seguente mostra l'impatto:
| Metodo | Tempo di rilevamento | Convergenza |
|---|---|---|
| Scadenza hold timer BGP | 90 secondi | 90-180 secondi |
| BFD (se disponibile) | <1 secondo | 1-3 secondi |
| Script di health-check | 5-15 secondi | 35-45 secondi (+ propagazione BGP) |
BFD rileva solo guasti di collegamento/sessione, non guasti applicativi. Lo script di health-check cattura crash di BIND9, errori di configurazione e disco pieno che BFD non può vedere.
Creare un utente dedicato
L'health-check viene eseguito come utente dedicato con privilegi minimi:
useradd --system --no-create-home --shell /usr/sbin/nologin anycast-healthcheck
Concedigli il permesso di controllare l'interfaccia anycast. Crea una regola 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
Scrivere lo script di 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
Imposta i permessi:
chmod 755 /usr/local/bin/anycast-healthcheck.sh
Creare un servizio 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 è richiesto qui perché lo script usa sudo per commutare l'interfaccia. La regola sudoers limita quali comandi l'utente può eseguire.
La sezione [Unit] usa Wants= invece di Requires=. Con Requires=, systemd fermerebbe l'health-check quando named si ferma. È esattamente il contrario di ciò che serve: l'health-check deve continuare a funzionare per rilevare il guasto DNS e ritirare la rotta.
Abilita e avvia:
systemctl daemon-reload
systemctl enable --now anycast-healthcheck
Controlla che sia in esecuzione:
systemctl status anycast-healthcheck
I log devono mostrare il messaggio di avvio:
journalctl -u anycast-healthcheck -f
Starting anycast health check for example.com on 127.0.0.1
Come testo il failover del DNS anycast?
Esegui questi test da una macchina esterna, non da nessuno dei nodi.
Passo 1: confermare che entrambi i nodi annunciano
Dalla tua macchina locale:
dig @198.51.100.1 example.com A +short
Conferma di ricevere una risposta. Esegui un traceroute per vedere quale nodo raggiungi:
traceroute -n 198.51.100.1
Il percorso mostra quale sede è più vicina a te.
Passo 2: simulare un guasto DNS sul nodo A
Sul nodo A, ferma BIND9:
systemctl stop named
Osserva i log dell'health-check:
journalctl -u anycast-healthcheck -f
Sequenza attesa:
DNS check failed (1/3)
DNS check failed (2/3)
DNS check failed (3/3)
WITHDRAW: anycast0 down after 3 consecutive failures
Dopo 15 secondi (3 controlli a intervalli di 5 secondi), l'health-check disattiva anycast0. BIRD2 rileva il cambio dell'interfaccia e ritira la rotta.
Controlla che la rotta sia sparita:
birdc show route export upstream4
La tabella deve essere vuota. Il router upstream rimuove 198.51.100.0/24 dal nodo A e inoltra tutto il traffico al nodo B.
Passo 3: verificare il failover del client
Dalla tua macchina esterna, interroga di nuovo:
dig @198.51.100.1 example.com A +short +time=5
La prima query dopo il ritiro potrebbe andare in timeout (la vecchia rotta è ancora in cache in alcuni router). Le query successive raggiungono il nodo B. La convergenza BGP richiede tipicamente 30-90 secondi a seconda della configurazione del tuo upstream.
Passo 4: ripristinare il nodo A
systemctl start named
L'health-check rileva il ritorno del DNS dopo 2 successi consecutivi (10 secondi) e riattiva anycast0. BIRD2 riannuncia la rotta.
journalctl -u anycast-healthcheck --no-pager | tail -5
Atteso:
ANNOUNCE: anycast0 up after 2 consecutive successes
Controlla le sessioni BGP:
birdc show protocols
Entrambe le sessioni BGP devono mostrare di nuovo Established.
Passo 5: misurare il tempo di convergenza
Per una misura precisa, avvia un ciclo dig continuo da una macchina esterna:
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
Ferma BIND9 sul nodo che stai attualmente raggiungendo. Conta i secondi tra l'ultima risposta riuscita e la prima risposta riuscita dall'altro nodo. Sull'infrastruttura Virtua con timer BGP predefiniti, aspettati 30-60 secondi di indisponibilità parziale.
Come si confronta BIRD2 con altri demoni di routing per l'anycast?
| Caratteristica | BIRD2 | FRRouting | ExaBGP |
|---|---|---|---|
| Linguaggio di configurazione | Filtri dichiarativi | CLI in stile Cisco | JSON/API |
| Integrazione anycast | Protocollo direct su interfaccia dummy | Rotta statica + redistribuzione | Script esterno annuncia/ritira |
| Metodo health-check | Up/down dell'interfaccia attiva il cambio di rotta | Comandi vtysh da script | Gestore di processi integrato |
| Supporto IPv6 | Configurazione unificata (canali ipv4/ipv6) | Famiglie di indirizzi separate | Manuale per famiglia |
| Complessità filtri | Linguaggio di filtri completo con funzioni | Route map, prefix list | Solo logica esterna |
| Consumo memoria | Basso (~10 MB per tabella piccola) | Medio (~30 MB) | Molto basso (~5 MB) |
| Maturità in produzione | IXP, DNS su larga scala (Cloudflare, RIPE) | ISP, data center | Piccoli deployment, monitoraggio |
Il vantaggio di BIRD2 per l'anycast: il protocol direct su un'interfaccia dummy offre il ritiro automatico della rotta quando l'interfaccia va giù. Nessun scripting esterno necessario per la parte BGP. L'health-check deve solo commutare l'interfaccia.
Come sincronizzo le zone DNS tra nodi anycast?
La sincronizzazione delle zone usa la replica primario/secondario integrata in BIND9 via AXFR, autenticata con TSIG. La configurazione è stata fatta nella sezione BIND9 sopra. Ecco i dettagli operativi.
Gli aggiornamenti di zona fluiscono in una direzione: modifica il file di zona sul nodo A (primario), incrementa il numero di serie e ricarica:
# 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
Esegui sempre named-checkzone prima di ricaricare. Cattura gli errori di sintassi.
rndc reload example.com
Il nodo B riceve un NOTIFY dal nodo A e scarica la zona aggiornata via AXFR. Sul nodo B, controlla il numero di serie:
dig @127.0.0.1 example.com SOA +short
Il numero di serie deve corrispondere. In caso contrario, controlla:
- Il firewall permette TCP 53 dal nodo A al nodo B
- La chiave TSIG è identica su entrambi i nodi
journalctl -u namedsul nodo B per errori di trasferimento
Aggiungere un terzo nodo: configuralo come un altro secondario. Aggiungi il suo IP alla lista also-notify e alle regole nftables del nodo A. Nessuna modifica necessaria sui secondari esistenti.
E per quanto riguarda il BGP anycast IPv6?
La configurazione BIRD2 sopra include già IPv6. Il blocco protocol direct anycast ha entrambi i canali ipv4 e ipv6. La sessione BGP upstream6 esporta il prefisso /48.
Controlla l'esportazione delle rotte 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
Testa il DNS su IPv6:
dig @2001:db8:abcd::1 example.com AAAA +short
Risoluzione dei problemi
Sessione BGP bloccata in Active/Connect:
journalctl -u bird -f
Cause comuni: IP del neighbor sbagliato, ASN sbagliato, password MD5 non corrispondente, firewall che blocca TCP 179. Esegui birdc show protocols all upstream4 per messaggi di errore dettagliati.
BIND9 non ascolta sull'IP anycast:
ss -tlnp | grep ':53'
Se vedi solo 127.0.0.1:53, controlla che listen-on includa l'IP anycast e che l'interfaccia anycast0 fosse attiva prima dell'avvio di BIND9. Riavvia BIND9 dopo l'attivazione dell'interfaccia: systemctl restart named.
Trasferimento di zona fallito:
journalctl -u named | grep -i "tsig\|transfer\|refused"
Controlla che il file della chiave sia leggibile dall'utente bind: ls -la /etc/bind/anycast-transfer.key. Esegui un test di trasferimento manuale: dig @203.0.113.10 example.com AXFR -k /etc/bind/anycast-transfer.key.
L'health-check non ritira le rotte:
journalctl -u anycast-healthcheck -f
Controlla i permessi sudo: sudo -u anycast-healthcheck sudo -l. L'utente deve poter eseguire /usr/sbin/ip link set anycast0 up e down senza password.
Query in timeout dopo il failover:
I client DNS mettono in cache le risposte. Il TTL nel file di zona (300 secondi / 5 minuti) determina quanto tempo i client usano dati obsoleti. La convergenza BGP aggiunge 30-90 secondi. Tempo totale di failover dal punto di vista del client: fino al TTL più il tempo di convergenza. Riduci il TTL minimo del SOA se serve un failover più rapido, ma non scendere sotto i 60 secondi per le zone autoritative.
Checklist di produzione
Prima di andare in produzione:
- Record ROA creati e validati (
rpki-cliento portale del tuo RIR) - Sessioni BGP stabilite su tutti i nodi (
birdc show protocols) - Prefisso anycast visibile nelle tabelle di routing globali (usa un looking glass)
- BIND9 risponde sull'IP anycast da reti esterne
- Numero di serie della zona corrisponde su tutti i nodi
- Permessi della chiave TSIG impostati a 640, proprietario root:bind
- Regole nftables attive su tutti i nodi
- Servizio health-check abilitato e in esecuzione
- Failover testato: BIND9 fermato, ritiro rotta confermato, ripristino confermato
- Monitoraggio configurato: allarmi su sessione BGP down, processo BIND9, guasti health-check
-
journalctl -u birdejournalctl -u namednon mostrano errori
Per la guida principale sui fondamenti BGP e sull'uso del proprio spazio IP, consulta BGP e Bring Your Own IP su un VPS: la guida completa. Per il filtraggio delle rotte BGP e l'hardening di sicurezza, consulta BGP Route Filtering: Prefix List, Filtri AS-Path, Bogon Rejection e GTSM.
Copyright 2026 Virtua.Cloud. Tutti i diritti riservati. Questo contenuto è un'opera originale del team Virtua.Cloud. La riproduzione, ripubblicazione o redistribuzione senza autorizzazione scritta è vietata.