Anycast-DNS met BIRD2 en BGP: multi-locatie setup
Implementeer anycast-DNS op meerdere VPS-locaties met BIRD2 en BIND9. BGP-routeaankondiging, zonesynchronisatie met TSIG, health-check failover en nftables-beveiliging.
Anycast-DNS wijst hetzelfde IP-adres toe aan DNS-servers op meerdere locaties. Elke server kondigt het IP aan via BGP. Routers sturen queries naar de dichtstbijzijnde server op basis van netwerktopologie. Als een server uitvalt en zijn BGP-route intrekt, bereiken queries automatisch de volgende dichtstbijzijnde server. Dit geeft je DNS-resolutie met lage latentie en automatische failover zonder wijzigingen aan de clientzijde.
Deze handleiding implementeert een productie anycast-DNS setup op twee Virtua VPS-nodes met BIRD2 als routing-daemon en BIND9 als autoritatieve DNS-server. Beveiliging is in elke stap ingebouwd, niet achteraf toegevoegd.
Wat je gaat bouwen:
- Twee VPS-nodes op verschillende locaties, die elk hetzelfde /24-prefix via BGP aankondigen
- BIND9 als autoritatieve DNS-server die luistert op het anycast-IP
- Zonesynchronisatie tussen nodes via TSIG-geauthenticeerde transfers
- Een health-check script dat BGP-routes intrekt wanneer DNS faalt
- nftables-firewallregels die elke node beveiligen
Wat zijn de vereisten voor anycast-DNS?
Voordat je begint, moeten de volgende resources al ingericht zijn. Als je nog nooit BGP op een VPS hebt geconfigureerd, lees dan eerst BIRD2 BGP-configuratie op een Linux VPS. Voor het aanmaken van ROA-records, zie RPKI ROA voor BGP: ROA's aanmaken, routes valideren in BIRD2 en FRR.
| Vereiste | Details |
|---|---|
| ASN | Je eigen ASN (bijv. AS212345), geregistreerd bij een RIR |
| IPv4-prefix | Minimaal een /24 (bijv. 198.51.100.0/24). Prefixen langer dan /24 worden door de meeste transit-providers gefilterd. |
| IPv6-prefix | Minimaal een /48 (bijv. 2001:db8:abcd::/48) |
| ROA-records | Aangemaakt in je RIR-portal voor beide prefixen, die je ASN autoriseren |
| VPS-nodes | 2+ Virtua VPS op verschillende locaties, elk met een BGP-sessie naar de upstream-router |
| Upstream BGP-info | Neighbor-IP en ASN per node, door Virtua verstrekt |
| Debian 12 | Deze handleiding gebruikt Debian Bookworm. Pas pakketnamen aan voor andere distributies. |
In deze handleiding gebruiken we de volgende voorbeeldwaarden. Vervang ze door je eigen waarden:
| Variabele | Waarde |
|---|---|
| Je ASN | 212345 |
| Anycast IPv4 | 198.51.100.1 |
| Anycast-prefix | 198.51.100.0/24 |
| Anycast IPv6 | 2001:db8:abcd::1 |
| Anycast IPv6-prefix | 2001:db8:abcd::/48 |
| Node A (Frankfurt) primair IP | 203.0.113.10 |
| Node A upstream neighbor | 169.254.169.1 AS 64496 |
| Node B (Amsterdam) primair IP | 203.0.113.20 |
| Node B upstream neighbor | 169.254.169.1 AS 64496 |
| DNS-zone | example.com |
Hoe ziet de netwerktopologie eruit?
┌─────────────┐
│ Internet │
└──────┬───────┘
│
┌────────────┴────────────┐
│ │
┌────────┴────────┐ ┌────────┴────────┐
│ Virtua Router │ │ Virtua Router │
│ Frankfurt │ │ Amsterdam │
│ AS 64496 │ │ AS 64496 │
└────────┬────────┘ └────────┬────────┘
│ eBGP │ eBGP
┌────────┴────────┐ ┌────────┴────────┐
│ Node A (VPS) │ │ Node 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 (via │
│ primaire IPs) │
└─────────────────────────┘
Beide nodes kondigen 198.51.100.0/24 en 2001:db8:abcd::/48 aan via BGP. Clients bereiken de topologisch dichtstbijzijnde node. Zonetransfers verlopen via de primaire (unicast) IPs, niet via het anycast-adres.
Hoe maak ik de anycast loopback-interface aan?
Elke node heeft het anycast-IP nodig op een lokale interface. Een dummy-interface is hiervoor het meest geschikt omdat deze altijd actief is en geen fysieke afhankelijkheid heeft. Configureer deze met systemd-networkd zodat het herstart overleeft.
Maak het netdev-bestand aan:
cat > /etc/systemd/network/10-anycast.netdev << 'EOF'
[NetDev]
Name=anycast0
Kind=dummy
EOF
Maak het network-bestand aan om de anycast-IPs toe te wijzen. Gebruik de volledige prefixlengte (/24 voor IPv4, /48 voor IPv6) zodat het direct-protocol in BIRD2 verbonden routes ziet die overeenkomen met de prefixen die je wilt aankondigen:
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
Activeer en start systemd-networkd. Als je VPS ifupdown gebruikt voor de primaire interface (controleer /etc/network/interfaces), gebruik dan niet systemctl restart systemd-networkd omdat dit het primaire netwerk kan verstoren. De eerste start is veilig omdat systemd-networkd alleen interfaces beheert die configuratiebestanden hebben in /etc/systemd/network/:
systemctl enable --now systemd-networkd
ip addr show anycast0
Verwachte output:
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
De interfacestatus toont UNKNOWN, wat normaal is voor dummy-interfaces. Het betekent dat de linklaag altijd actief is. Zowel het IPv4- als IPv6-adres moeten zichtbaar zijn.
Herhaal dit op elke anycast-node. De configuratie is identiek.
Welke BIRD2-configuratie is nodig voor anycast-routeaankondiging?
BIRD2 kondigt het anycast-prefix aan bij de upstream-router via eBGP. Het kernontwerp: BIRD2 exporteert alleen routes die via het direct-protocol van de anycast0-interface zijn geleerd. Wanneer de interface uitvalt (of het health-check script deze uitschakelt), trekt BIRD2 de route automatisch in.
Installeer BIRD2:
apt update && apt install -y bird2
Debian 12 levert BIRD 2.0.12 mee. Voor nieuwere functies (BFD-verbeteringen, filterverbeteringen) voeg je de officiële BIRD-repository toe.
Configuratie van Node 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;
};
}
Belangrijke punten:
protocol direct anycastleert routes alleen van deanycast0-interface. Geen andere interfaces lekken naar de routeringstabel.- Exportfilters beperken aankondigingen tot precies je /24 en /48. Dit voorkomt onbedoelde route-leks.
passwordschakelt TCP MD5-authenticatie (RFC 2385) in op de BGP-sessie. Vraag het gedeelde geheim op bij je upstream-provider.import nonebetekent dat deze node geen routes van de upstream accepteert. Anycast-nodes kondigen alleen aan; ze gebruiken de standaardroute van de VPS voor uitgaand verkeer.graceful restart onvermindert route-flapping bij BIRD2-herstarts.
Configuratie van Node B
Kopieer hetzelfde bestand naar Node B. Wijzig alleen:
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
BIRD2 starten
Activeer en start BIRD2:
systemctl enable --now bird
enable zorgt voor persistentie na herstarts. --now start het direct.
Status controleren:
systemctl status bird
BGP-sessiestatus controleren:
birdc show protocols
Verwachte output:
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
Beide BGP-sessies moeten Established tonen. Als je in plaats daarvan Active of Connect ziet, controleer dan het neighbor-IP, ASN en wachtwoord. Bekijk logs met journalctl -u bird -f.
Geëxporteerde routes controleren:
birdc show route export upstream4
Verwacht:
BIRD 2.0.12 ready.
Table master4:
198.51.100.0/24 unicast [anycast 2026-03-19] * (240)
dev anycast0
De route komt van het anycast direct-protocol en wordt geëxporteerd naar upstream4. Herhaal op Node B.
Hoe configureer ik BIND9 om te luisteren op het anycast-IP?
BIND9 draait als puur autoritatieve DNS-server op elke node en luistert op het anycast-IP. Clients bevragen het anycast-IP; BGP zorgt ervoor dat ze de dichtstbijzijnde node bereiken.
BIND9 installeren:
apt update && apt install -y bind9 bind9-utils
TSIG-sleutel genereren voor zonetransfers
Zonetransfers tussen nodes moeten geauthenticeerd zijn. Genereer een TSIG-sleutel met tsig-keygen:
tsig-keygen -a hmac-sha256 anycast-transfer > /etc/bind/anycast-transfer.key
De output ziet er zo uit:
cat /etc/bind/anycast-transfer.key
key "anycast-transfer" {
algorithm hmac-sha256;
secret "base64-encoded-secret-here";
};
Kopieer dit exacte bestand naar alle nodes. De sleutelnaam en het geheim moeten overal overeenkomen.
Stel restrictieve rechten in:
chown root:bind /etc/bind/anycast-transfer.key
chmod 640 /etc/bind/anycast-transfer.key
Het bestand moet root:bind eigenaarschap en 640 rechten tonen:
ls -la /etc/bind/anycast-transfer.key
-rw-r----- 1 root bind 113 Mar 19 12:00 /etc/bind/anycast-transfer.key
Configuratie van Node A (primair)
Bewerk /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;
};
Voeg de TSIG-sleutel toe. Bewerk /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;
};
Maak het zonebestand /var/lib/bind/db.example.com aan:
$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.
Eigenaarschap instellen:
chown bind:bind /var/lib/bind/db.example.com
chmod 640 /var/lib/bind/db.example.com
Configuratie van Node B (secundair)
Op Node B is /etc/bind/named.conf.options identiek aan Node A.
/etc/bind/named.conf.local verschilt:
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"; };
};
De server-directive vertelt BIND9 om alle communicatie met Node A te authenticeren met de TSIG-sleutel. Zonetransfers zijn automatisch zodra dit is geconfigureerd.
BIND9 starten
Op beide nodes activeren en starten:
systemctl enable --now named
Status controleren:
systemctl status named
DNS-resolutie testen tegen het anycast-IP:
dig @198.51.100.1 example.com A +short
Verwacht:
198.51.100.10
Op Node B controleren of de zone is overgedragen:
dig @198.51.100.1 example.com SOA +short
ns1.example.com. admin.example.com. 2026031901 3600 900 604800 300
BIND9-logs controleren op de transfer:
journalctl -u named --no-pager | grep "transfer of"
Verwacht:
transfer of 'example.com/IN' from 203.0.113.10#53: Transfer status: success
Het serienummer moet op beide nodes overeenkomen. Als de secundaire een ander serienummer toont, is de transfer mislukt. Controleer de consistentie van de TSIG-sleutel en firewallregels.
Welke firewallregels heeft een BGP anycast-DNS-node nodig?
Beveilig elke node met nftables. Sta alleen het noodzakelijke toe: SSH voor beheer, BGP van de upstream-router en DNS van overal.
Maak /etc/nftables.conf aan:
#!/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;
}
}
Pas de ip saddr- en ip6 saddr-regels aan op Node B (wissel de zonetransfer-peer-IPs en gebruik de IPv6 upstream-neighbor van Node B). In productie beperk je SSH tot je beheer-CIDR.
Toepassen en activeren:
systemctl enable --now nftables
Actieve regels weergeven om te bevestigen dat ze geladen zijn:
nft list ruleset
Test vervolgens of DNS nog werkt van buitenaf:
# Run this from your local machine, NOT the server
dig @198.51.100.1 example.com A +short
Als DNS stopt met werken na het toepassen van firewallregels, controleer dan of daddr exact overeenkomt met je anycast-IP en of de anycast0-interface actief is.
Hoe controleer ik DNS via health-check en trek ik BGP-routes in bij een storing?
Een health-check script bewaakt BIND9 en signaleert BIRD2 om de anycast-route in te trekken wanneer DNS uitvalt. De methode: de anycast0-interface uitschakelen, waardoor BIRD2 het prefix automatisch intrekt omdat het direct-protocol de route niet meer ziet.
Waarom niet gewoon op BGP-timers vertrouwen?
Met standaard BGP-timers (90 s hold, 30 s keepalive) duurt failover tot 90 seconden. Een health-check detecteert DNS-uitval binnen seconden en activeert het intrekken onmiddellijk. De onderstaande tabel toont het verschil:
| Methode | Detectietijd | Convergentie |
|---|---|---|
| BGP hold timer verlopen | 90 seconden | 90-180 seconden |
| BFD (indien beschikbaar) | <1 seconde | 1-3 seconden |
| Health-check script | 5-15 seconden | 35-45 seconden (+ BGP-propagatie) |
BFD detecteert alleen link-/sessiestoringen, geen applicatiestoringen. Het health-check script vangt BIND9-crashes, misconfiguraties en volle schijven op die BFD niet kan zien.
Dedicated gebruiker aanmaken
De health-check draait als dedicated gebruiker met minimale rechten:
useradd --system --no-create-home --shell /usr/sbin/nologin anycast-healthcheck
Geef deze gebruiker toestemming om de anycast-interface te bedienen. Maak een sudoers-regel aan:
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
Health-check script schrijven
Maak /usr/local/bin/anycast-healthcheck.sh aan:
#!/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
Rechten instellen:
chmod 755 /usr/local/bin/anycast-healthcheck.sh
Systemd-service aanmaken
Maak /etc/systemd/system/anycast-healthcheck.service aan:
[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 is hier vereist omdat het script sudo gebruikt om de interface te schakelen. De sudoers-regel beperkt welke commando's de gebruiker mag uitvoeren.
De [Unit]-sectie gebruikt Wants= in plaats van Requires=. Met Requires= zou systemd de health-check stoppen wanneer named stopt. Dat is precies het tegenovergestelde van wat je wilt: de health-check moet blijven draaien om de DNS-storing te detecteren en de route in te trekken.
Activeren en starten:
systemctl daemon-reload
systemctl enable --now anycast-healthcheck
Controleren of het draait:
systemctl status anycast-healthcheck
De logs moeten het opstartbericht tonen:
journalctl -u anycast-healthcheck -f
Starting anycast health check for example.com on 127.0.0.1
Hoe test ik anycast-DNS failover?
Voer deze tests uit vanaf een externe machine, niet vanaf een van de nodes.
Stap 1: bevestig dat beide nodes aankondigen
Vanaf je lokale machine:
dig @198.51.100.1 example.com A +short
Bevestig dat je een antwoord krijgt. Voer een traceroute uit om te zien welke node je bereikt:
traceroute -n 198.51.100.1
Het pad toont welke locatie het dichtst bij je is.
Stap 2: simuleer een DNS-storing op Node A
Op Node A, stop BIND9:
systemctl stop named
Bekijk de health-check logs:
journalctl -u anycast-healthcheck -f
Verwachte reeks:
DNS check failed (1/3)
DNS check failed (2/3)
DNS check failed (3/3)
WITHDRAW: anycast0 down after 3 consecutive failures
Na 15 seconden (3 controles met 5-seconden intervallen) schakelt de health-check anycast0 uit. BIRD2 detecteert de interfacewijziging en trekt de route in.
Controleer of de route verdwenen is:
birdc show route export upstream4
De tabel moet leeg zijn. De upstream-router verwijdert 198.51.100.0/24 van Node A en stuurt al het verkeer naar Node B.
Stap 3: client-failover controleren
Vanaf je externe machine, bevraag opnieuw:
dig @198.51.100.1 example.com A +short +time=5
De eerste query na het intrekken kan een timeout geven (de oude route is nog gecacht in sommige routers). Vervolgqueries bereiken Node B. BGP-convergentie duurt doorgaans 30-90 seconden afhankelijk van de upstream-configuratie.
Stap 4: Node A herstellen
systemctl start named
De health-check detecteert dat DNS terug is na 2 opeenvolgende successen (10 seconden) en activeert anycast0. BIRD2 kondigt de route opnieuw aan.
journalctl -u anycast-healthcheck --no-pager | tail -5
Verwacht:
ANNOUNCE: anycast0 up after 2 consecutive successes
BGP-sessies controleren:
birdc show protocols
Beide BGP-sessies moeten weer Established tonen.
Stap 5: convergentietijd meten
Voor een nauwkeurige meting, voer een continue dig-loop uit vanaf een externe machine:
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
Stop BIND9 op de node die je momenteel bereikt. Tel de seconden tussen het laatste succesvolle antwoord en het eerste succesvolle antwoord van de andere node. Op Virtua-infrastructuur met standaard BGP-timers kun je 30-60 seconden gedeeltelijke onbereikbaarheid verwachten.
Hoe verhoudt BIRD2 zich tot andere routing-daemons voor anycast?
| Eigenschap | BIRD2 | FRRouting | ExaBGP |
|---|---|---|---|
| Configuratietaal | Declaratieve filters | Cisco-achtige CLI | JSON/API |
| Anycast-integratie | Direct-protocol op dummy-interface | Statische route + redistributie | Extern script kondigt aan/trekt in |
| Health-check methode | Interface up/down triggert routewijziging | vtysh-commando's vanuit script | Ingebouwde procesmanager |
| IPv6-ondersteuning | Uniforme configuratie (ipv4/ipv6-kanalen) | Aparte adresfamilies | Handmatig per familie |
| Filtercomplexiteit | Volledige filtertaal met functies | Route maps, prefix lists | Alleen externe logica |
| Geheugengebruik | Laag (~10 MB voor kleine tabel) | Gemiddeld (~30 MB) | Zeer laag (~5 MB) |
| Productievolwassenheid | IXPs, grootschalige DNS (Cloudflare, RIPE) | ISPs, datacenters | Kleine deployments, monitoring |
Het voordeel van BIRD2 voor anycast: het protocol direct op een dummy-interface biedt automatisch intrekken van routes wanneer de interface uitvalt. Geen extern scripting nodig voor het BGP-deel. De health-check hoeft alleen de interface te schakelen.
Hoe synchroniseer ik DNS-zones tussen anycast-nodes?
Zonesynchronisatie gebruikt de ingebouwde primair/secundair replicatie van BIND9 via AXFR, geauthenticeerd met TSIG. De configuratie is hierboven in de BIND9-sectie gedaan. Hier de operationele details.
Zone-updates stromen in één richting: bewerk het zonebestand op Node A (primair), verhoog het serienummer en herlaad:
# 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
Voer altijd named-checkzone uit voor het herladen. Het vangt syntaxfouten op.
rndc reload example.com
Node B ontvangt een NOTIFY van Node A en haalt de bijgewerkte zone op via AXFR. Op Node B het serienummer controleren:
dig @127.0.0.1 example.com SOA +short
Het serienummer moet overeenkomen. Als dat niet het geval is, controleer dan:
- De firewall staat TCP 53 toe van Node A naar Node B
- De TSIG-sleutel is identiek op beide nodes
journalctl -u namedop Node B voor transferfouten
Een derde node toevoegen: configureer deze als extra secundaire node. Voeg het IP toe aan de also-notify-lijst en de nftables-regels op Node A. Geen wijzigingen nodig op bestaande secundaire nodes.
Hoe zit het met IPv6 BGP anycast?
De BIRD2-configuratie hierboven bevat al IPv6. Het protocol direct anycast-blok heeft zowel ipv4- als ipv6-kanalen. De upstream6 BGP-sessie exporteert het /48-prefix.
IPv6-route-export controleren:
birdc show route export upstream6
BIRD 2.0.12 ready.
Table master6:
2001:db8:abcd::/48 unicast [anycast 2026-03-19] * (240)
dev anycast0
DNS via IPv6 testen:
dig @2001:db8:abcd::1 example.com AAAA +short
Probleemoplossing
BGP-sessie vastgelopen in Active/Connect:
journalctl -u bird -f
Veelvoorkomende oorzaken: verkeerd neighbor-IP, verkeerd ASN, MD5-wachtwoord komt niet overeen, firewall blokkeert TCP 179. Voer birdc show protocols all upstream4 uit voor gedetailleerde foutmeldingen.
BIND9 luistert niet op het anycast-IP:
ss -tlnp | grep ':53'
Als je alleen 127.0.0.1:53 ziet, controleer dan of listen-on het anycast-IP bevat en of de anycast0-interface actief was voordat BIND9 startte. Herstart BIND9 nadat de interface actief is: systemctl restart named.
Zonetransfer mislukt:
journalctl -u named | grep -i "tsig\|transfer\|refused"
Controleer of het sleutelbestand leesbaar is door de bind-gebruiker: ls -la /etc/bind/anycast-transfer.key. Voer een handmatige transfertest uit: dig @203.0.113.10 example.com AXFR -k /etc/bind/anycast-transfer.key.
Health-check trekt routes niet in:
journalctl -u anycast-healthcheck -f
Controleer sudo-rechten: sudo -u anycast-healthcheck sudo -l. De gebruiker moet /usr/sbin/ip link set anycast0 up en down zonder wachtwoord kunnen uitvoeren.
Queries geven timeout na failover:
DNS-clients cachen antwoorden. De TTL in het zonebestand (300 seconden / 5 minuten) bepaalt hoe lang clients verouderde data gebruiken. BGP-convergentie voegt 30-90 seconden toe. Totale failovertijd vanuit clientperspectief: tot de TTL plus convergentietijd. Verlaag de SOA minimum TTL als snellere failover belangrijk is, maar ga niet onder de 60 seconden voor autoritatieve zones.
Productiechecklist
Voor je live gaat:
- ROA-records aangemaakt en gevalideerd (
rpki-clientof je RIR-portal) - BGP-sessies op alle nodes tot stand gebracht (
birdc show protocols) - Anycast-prefix zichtbaar in globale routeringstabellen (gebruik een looking glass)
- BIND9 reageert op het anycast-IP vanaf externe netwerken
- Zoneserienummer komt overeen op alle nodes
- TSIG-sleutelrechten ingesteld op 640, eigenaar root:bind
- nftables-regels actief op alle nodes
- Health-check service geactiveerd en draaiend
- Failover getest: BIND9 gestopt, route-intrekking bevestigd, herstel bevestigd
- Monitoring geconfigureerd: alerts bij BGP-sessie down, BIND9-proces, health-check fouten
-
journalctl -u birdenjournalctl -u namedtonen geen fouten
Voor de bovenliggende handleiding over BGP-basisprincipes en het meenemen van je eigen IP-ruimte, zie BGP en Bring Your Own IP op een VPS: de complete gids. Voor BGP-routefiltering en beveiligingshardening, zie BGP Route Filtering: Prefix Lists, AS-Path Filters, Bogon Rejection en GTSM.
Copyright 2026 Virtua.Cloud. Alle rechten voorbehouden. Deze inhoud is een origineel werk van het Virtua.Cloud-team. Reproductie, herpublicatie of herdistributie zonder schriftelijke toestemming is verboden.
Klaar om het zelf te proberen?
Draai BGP-sessies op uw eigen IP-ruimte met Virtua.Cloud VPS.
Bekijk VPS-aanbod