Anycast-DNS mit BIRD2 und BGP: Multi-Standort-Setup
Anycast-DNS über mehrere VPS-Standorte mit BIRD2 und BIND9 bereitstellen. BGP-Routenankündigung, Zonensynchronisation mit TSIG, Health-Check-Failover und nftables-Absicherung.
Anycast-DNS weist mehreren DNS-Servern an verschiedenen Standorten dieselbe IP-Adresse zu. Jeder Server kündigt die IP per BGP an. Router leiten Anfragen basierend auf der Netzwerktopologie zum nächstgelegenen Server. Fällt ein Server aus und zieht seine BGP-Route zurück, erreichen Anfragen automatisch den nächsten erreichbaren Server. Das ergibt DNS-Auflösung mit niedriger Latenz und automatisches Failover ohne clientseitige Änderungen.
Diese Anleitung richtet ein produktionsreifes Anycast-DNS auf zwei Virtua-VPS-Knoten ein, mit BIRD2 als Routing-Daemon und BIND9 als autoritativem DNS-Server. Sicherheitsmaßnahmen sind in jeden Schritt eingebaut, nicht nachträglich hinzugefügt.
Was Sie aufbauen werden:
- Zwei VPS-Knoten an verschiedenen Standorten, die jeweils dasselbe /24-Präfix per BGP ankündigen
- BIND9 als autoritativer DNS-Server, der auf der Anycast-IP lauscht
- Zonensynchronisation zwischen Knoten über TSIG-authentifizierte Transfers
- Ein Health-Check-Skript, das BGP-Routen zurückzieht, wenn DNS ausfällt
- nftables-Firewallregeln zur Absicherung jedes Knotens
Was sind die Voraussetzungen für Anycast-DNS?
Bevor Sie beginnen, müssen folgende Ressourcen bereitgestellt sein. Falls Sie noch nie BGP auf einem VPS konfiguriert haben, lesen Sie zuerst BIRD2 BGP-Konfiguration auf einem Linux VPS. Für die Erstellung von ROA-Einträgen siehe RPKI ROA für BGP: ROAs erstellen, Routen in BIRD2 und FRR validieren.
| Voraussetzung | Details |
|---|---|
| ASN | Ihre eigene ASN (z.B. AS212345), bei einem RIR registriert |
| IPv4-Präfix | Mindestens ein /24 (z.B. 198.51.100.0/24). Längere Präfixe als /24 werden von den meisten Transit-Anbietern gefiltert. |
| IPv6-Präfix | Mindestens ein /48 (z.B. 2001:db8:abcd::/48) |
| ROA-Einträge | Im RIR-Portal für beide Präfixe erstellt, Ihre ASN autorisierend |
| VPS-Knoten | 2+ Virtua-VPS an verschiedenen Standorten, jeweils mit BGP-Session zum Upstream-Router |
| Upstream-BGP-Infos | Neighbor-IP und ASN pro Knoten, von Virtua bereitgestellt |
| Debian 12 | Diese Anleitung verwendet Debian Bookworm. Passen Sie Paketnamen für andere Distributionen an. |
In dieser Anleitung verwenden wir folgende Beispielwerte. Ersetzen Sie diese durch Ihre eigenen:
| Variable | Wert |
|---|---|
| Ihre ASN | 212345 |
| Anycast-IPv4 | 198.51.100.1 |
| Anycast-Präfix | 198.51.100.0/24 |
| Anycast-IPv6 | 2001:db8:abcd::1 |
| Anycast-IPv6-Präfix | 2001:db8:abcd::/48 |
| Knoten A (Frankfurt) primäre IP | 203.0.113.10 |
| Knoten A Upstream-Neighbor | 169.254.169.1 AS 64496 |
| Knoten B (Amsterdam) primäre IP | 203.0.113.20 |
| Knoten B Upstream-Neighbor | 169.254.169.1 AS 64496 |
| DNS-Zone | example.com |
Wie sieht die Netzwerktopologie aus?
┌─────────────┐
│ Internet │
└──────┬───────┘
│
┌────────────┴────────────┐
│ │
┌────────┴────────┐ ┌────────┴────────┐
│ Virtua-Router │ │ Virtua-Router │
│ Frankfurt │ │ Amsterdam │
│ AS 64496 │ │ AS 64496 │
└────────┬────────┘ └────────┬────────┘
│ eBGP │ eBGP
┌────────┴────────┐ ┌────────┴────────┐
│ Knoten A (VPS) │ │ Knoten 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 (über │
│ primäre IPs) │
└─────────────────────────┘
Beide Knoten kündigen 198.51.100.0/24 und 2001:db8:abcd::/48 per BGP an. Clients erreichen den topologisch nächsten Knoten. Zonentransfers laufen über die primären (Unicast-)IPs, nicht über die Anycast-Adresse.
Wie erstelle ich das Anycast-Loopback-Interface?
Jeder Knoten benötigt die Anycast-IP auf einem lokalen Interface. Ein Dummy-Interface eignet sich am besten, da es immer aktiv ist und keine physische Abhängigkeit hat. Konfigurieren Sie es mit systemd-networkd, damit es Neustarts übersteht.
Erstellen Sie die Netdev-Datei:
cat > /etc/systemd/network/10-anycast.netdev << 'EOF'
[NetDev]
Name=anycast0
Kind=dummy
EOF
Erstellen Sie die Network-Datei, um die Anycast-IPs zuzuweisen. Verwenden Sie die vollständige Präfixlänge (/24 für IPv4, /48 für IPv6), damit das direct-Protokoll in BIRD2 verbundene Routen sieht, die zu den anzukündigenden Präfixen passen:
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
Aktivieren und starten Sie systemd-networkd. Falls Ihr VPS ifupdown für das primäre Interface verwendet (prüfen Sie /etc/network/interfaces), verwenden Sie nicht systemctl restart systemd-networkd, da dies das primäre Netzwerk stören kann. Der Erststart ist sicher, da systemd-networkd nur Interfaces verwaltet, die Konfigurationsdateien in /etc/systemd/network/ haben:
systemctl enable --now systemd-networkd
ip addr show anycast0
Erwartete Ausgabe:
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
Der Interface-Status zeigt UNKNOWN, was für Dummy-Interfaces normal ist. Es bedeutet, dass die Verbindungsschicht immer aktiv ist. Beide IPv4- und IPv6-Adressen sollten erscheinen.
Wiederholen Sie dies auf jedem Anycast-Knoten. Die Konfiguration ist identisch.
Welche BIRD2-Konfiguration wird für die Anycast-Routenankündigung benötigt?
BIRD2 kündigt das Anycast-Präfix beim Upstream-Router per eBGP an. Das Kernprinzip: BIRD2 exportiert nur Routen, die über das direct-Protokoll vom anycast0-Interface gelernt wurden. Wenn das Interface ausfällt (oder das Health-Check-Skript es deaktiviert), zieht BIRD2 die Route automatisch zurück.
Installieren Sie BIRD2:
apt update && apt install -y bird2
Debian 12 liefert BIRD 2.0.12 aus. Für neuere Funktionen (BFD-Verbesserungen, erweiterte Filter) fügen Sie das offizielle BIRD-Repository hinzu.
Konfiguration von Knoten 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;
};
}
Wichtige Punkte:
protocol direct anycastlernt Routen nur vomanycast0-Interface. Keine anderen Interfaces gelangen in die Routing-Tabelle.- Export-Filter beschränken Ankündigungen auf genau Ihr /24 und /48. Das verhindert versehentliche Route-Leaks.
passwordaktiviert TCP-MD5-Authentifizierung (RFC 2385) auf der BGP-Session. Das Shared Secret erhalten Sie von Ihrem Upstream-Anbieter.import nonebedeutet, dass dieser Knoten keine Routen vom Upstream akzeptiert. Anycast-Knoten kündigen nur an; sie verwenden die Standard-Route des VPS für ausgehenden Verkehr.graceful restart onreduziert Route-Flapping bei BIRD2-Neustarts.
Konfiguration von Knoten B
Kopieren Sie dieselbe Datei auf Knoten B. Ändern Sie nur:
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
Aktivieren und starten Sie BIRD2:
systemctl enable --now bird
enable sorgt für Persistenz nach Neustarts. --now startet den Dienst sofort.
Status prüfen:
systemctl status bird
BGP-Session-Status prüfen:
birdc show protocols
Erwartete Ausgabe:
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-Sessions sollten Established anzeigen. Sehen Sie stattdessen Active oder Connect, prüfen Sie Neighbor-IP, ASN und Passwort. Logs einsehen mit journalctl -u bird -f.
Exportierte Routen prüfen:
birdc show route export upstream4
Erwartet:
BIRD 2.0.12 ready.
Table master4:
198.51.100.0/24 unicast [anycast 2026-03-19] * (240)
dev anycast0
Die Route stammt vom anycast-Direct-Protokoll und wird an upstream4 exportiert. Auf Knoten B wiederholen.
Wie konfiguriere ich BIND9 zum Lauschen auf der Anycast-IP?
BIND9 läuft als rein autoritativer DNS-Server auf jedem Knoten und lauscht auf der Anycast-IP. Clients befragen die Anycast-IP; BGP stellt sicher, dass sie den nächsten Knoten erreichen.
BIND9 installieren:
apt update && apt install -y bind9 bind9-utils
TSIG-Schlüssel für Zonentransfers generieren
Zonentransfers zwischen Knoten müssen authentifiziert sein. Generieren Sie einen TSIG-Schlüssel mit tsig-keygen:
tsig-keygen -a hmac-sha256 anycast-transfer > /etc/bind/anycast-transfer.key
Die Ausgabe sieht so aus:
cat /etc/bind/anycast-transfer.key
key "anycast-transfer" {
algorithm hmac-sha256;
secret "base64-encoded-secret-here";
};
Kopieren Sie diese Datei exakt auf alle Knoten. Schlüsselname und Secret müssen überall übereinstimmen.
Restriktive Berechtigungen setzen:
chown root:bind /etc/bind/anycast-transfer.key
chmod 640 /etc/bind/anycast-transfer.key
Die Datei sollte root:bind als Eigentümer und 640 als Berechtigungen zeigen:
ls -la /etc/bind/anycast-transfer.key
-rw-r----- 1 root bind 113 Mar 19 12:00 /etc/bind/anycast-transfer.key
Konfiguration von Knoten A (primär)
Bearbeiten Sie /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;
};
TSIG-Schlüssel einbinden. Bearbeiten Sie /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;
};
Erstellen Sie die Zonendatei /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.
Eigentümer setzen:
chown bind:bind /var/lib/bind/db.example.com
chmod 640 /var/lib/bind/db.example.com
Konfiguration von Knoten B (sekundär)
Auf Knoten B ist /etc/bind/named.conf.options identisch mit Knoten A.
/etc/bind/named.conf.local unterscheidet sich:
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"; };
};
Die server-Anweisung weist BIND9 an, jede Kommunikation mit Knoten A per TSIG-Schlüssel zu authentifizieren. Zonentransfers sind automatisch, sobald dies konfiguriert ist.
BIND9 starten
Auf beiden Knoten aktivieren und starten:
systemctl enable --now named
Status prüfen:
systemctl status named
DNS-Auflösung gegen die Anycast-IP testen:
dig @198.51.100.1 example.com A +short
Erwartet:
198.51.100.10
Auf Knoten B prüfen, ob die Zone übertragen wurde:
dig @198.51.100.1 example.com SOA +short
ns1.example.com. admin.example.com. 2026031901 3600 900 604800 300
BIND9-Logs auf den Transfer prüfen:
journalctl -u named --no-pager | grep "transfer of"
Erwartet:
transfer of 'example.com/IN' from 203.0.113.10#53: Transfer status: success
Die Seriennummer muss auf beiden Knoten übereinstimmen. Zeigt der sekundäre Knoten eine andere Seriennummer, ist der Transfer fehlgeschlagen. Prüfen Sie TSIG-Schlüssel-Konsistenz und Firewallregeln.
Welche Firewallregeln braucht ein BGP-Anycast-DNS-Knoten?
Sichern Sie jeden Knoten mit nftables ab. Erlauben Sie nur das Nötige: SSH zur Verwaltung, BGP vom Upstream-Router und DNS von überall.
Erstellen Sie /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;
}
}
Passen Sie die ip saddr- und ip6 saddr-Zeilen auf Knoten B an (tauschen Sie die Zonentransfer-Peer-IPs, und verwenden Sie den IPv6-Upstream-Neighbor von Knoten B). In Produktion beschränken Sie SSH auf Ihr Verwaltungs-CIDR.
Anwenden und aktivieren:
systemctl enable --now nftables
Aktive Regeln auflisten, um das Laden zu bestätigen:
nft list ruleset
Dann testen, ob DNS von außen noch funktioniert:
# Run this from your local machine, NOT the server
dig @198.51.100.1 example.com A +short
Falls DNS nach dem Anwenden der Firewallregeln nicht mehr funktioniert, prüfen Sie, ob daddr genau Ihrer Anycast-IP entspricht und ob das anycast0-Interface aktiv ist.
Wie prüfe ich DNS per Health-Check und ziehe BGP-Routen bei Ausfall zurück?
Ein Health-Check-Skript überwacht BIND9 und signalisiert BIRD2, die Anycast-Route zurückzuziehen, wenn DNS ausfällt. Die Methode: Das anycast0-Interface herunterfahren, woraufhin BIRD2 das Präfix automatisch zurückzieht, da das direct-Protokoll die Route nicht mehr sieht.
Warum nicht einfach auf BGP-Timer vertrauen?
Mit Standard-BGP-Timern (90 s Hold, 30 s Keepalive) dauert ein Failover bis zu 90 Sekunden. Ein Health-Check erkennt DNS-Ausfälle in Sekunden und löst den Rückzug sofort aus. Die folgende Tabelle zeigt die Auswirkungen:
| Methode | Erkennungszeit | Konvergenz |
|---|---|---|
| BGP-Hold-Timer-Ablauf | 90 Sekunden | 90-180 Sekunden |
| BFD (falls verfügbar) | <1 Sekunde | 1-3 Sekunden |
| Health-Check-Skript | 5-15 Sekunden | 35-45 Sekunden (+ BGP-Propagation) |
BFD erkennt nur Link-/Session-Ausfälle, keine Anwendungsausfälle. Das Health-Check-Skript fängt BIND9-Abstürze, Fehlkonfigurationen und volle Festplatten ab, die BFD nicht sehen kann.
Dedizierten Benutzer anlegen
Der Health-Check läuft unter einem dedizierten Benutzer mit minimalen Rechten:
useradd --system --no-create-home --shell /usr/sbin/nologin anycast-healthcheck
Erteilen Sie ihm die Berechtigung, das Anycast-Interface zu steuern. Erstellen Sie eine Sudoers-Regel:
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-Skript schreiben
Erstellen Sie /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
Berechtigungen setzen:
chmod 755 /usr/local/bin/anycast-healthcheck.sh
Systemd-Service erstellen
Erstellen Sie /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 ist hier erforderlich, weil das Skript sudo zum Umschalten des Interfaces nutzt. Die Sudoers-Regel beschränkt, welche Befehle der Benutzer ausführen darf.
Der [Unit]-Abschnitt verwendet Wants= statt Requires=. Mit Requires= würde systemd den Health-Check stoppen, wenn named stoppt. Das ist genau das Gegenteil des gewünschten Verhaltens: Der Health-Check muss weiterlaufen, um den DNS-Ausfall zu erkennen und die Route zurückzuziehen.
Aktivieren und starten:
systemctl daemon-reload
systemctl enable --now anycast-healthcheck
Prüfen, ob es läuft:
systemctl status anycast-healthcheck
Die Logs sollten die Startmeldung zeigen:
journalctl -u anycast-healthcheck -f
Starting anycast health check for example.com on 127.0.0.1
Wie teste ich das Anycast-DNS-Failover?
Führen Sie diese Tests von einer externen Maschine aus, nicht von einem der Knoten.
Schritt 1: Bestätigen, dass beide Knoten ankündigen
Von Ihrer lokalen Maschine:
dig @198.51.100.1 example.com A +short
Bestätigen Sie eine Antwort. Führen Sie einen Traceroute aus, um zu sehen, welchen Knoten Sie erreichen:
traceroute -n 198.51.100.1
Der Pfad zeigt, welcher Standort Ihnen am nächsten ist.
Schritt 2: DNS-Ausfall auf Knoten A simulieren
Auf Knoten A BIND9 stoppen:
systemctl stop named
Health-Check-Logs beobachten:
journalctl -u anycast-healthcheck -f
Erwartete Sequenz:
DNS check failed (1/3)
DNS check failed (2/3)
DNS check failed (3/3)
WITHDRAW: anycast0 down after 3 consecutive failures
Nach 15 Sekunden (3 Prüfungen im 5-Sekunden-Intervall) deaktiviert der Health-Check anycast0. BIRD2 erkennt die Interface-Änderung und zieht die Route zurück.
Prüfen, ob die Route verschwunden ist:
birdc show route export upstream4
Die Tabelle sollte leer sein. Der Upstream-Router entfernt 198.51.100.0/24 von Knoten A und leitet den gesamten Verkehr an Knoten B weiter.
Schritt 3: Client-Failover prüfen
Von Ihrer externen Maschine erneut abfragen:
dig @198.51.100.1 example.com A +short +time=5
Die erste Abfrage nach dem Rückzug kann ein Timeout ergeben (die alte Route ist in manchen Routern noch gecacht). Folgeabfragen erreichen Knoten B. Die BGP-Konvergenz dauert je nach Upstream-Konfiguration typischerweise 30-90 Sekunden.
Schritt 4: Knoten A wiederherstellen
systemctl start named
Der Health-Check erkennt, dass DNS nach 2 aufeinanderfolgenden Erfolgen (10 Sekunden) wieder funktioniert, und aktiviert anycast0. BIRD2 kündigt die Route erneut an.
journalctl -u anycast-healthcheck --no-pager | tail -5
Erwartet:
ANNOUNCE: anycast0 up after 2 consecutive successes
BGP-Sessions prüfen:
birdc show protocols
Beide BGP-Sessions sollten wieder Established anzeigen.
Schritt 5: Konvergenzzeit messen
Für eine genaue Messung starten Sie eine kontinuierliche dig-Schleife von einer externen Maschine:
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
Stoppen Sie BIND9 auf dem Knoten, den Sie gerade erreichen. Zählen Sie die Sekunden zwischen der letzten erfolgreichen Antwort und der ersten erfolgreichen Antwort vom anderen Knoten. Auf Virtua-Infrastruktur mit Standard-BGP-Timern sind 30-60 Sekunden partielle Nichterreichbarkeit zu erwarten.
Wie schneidet BIRD2 im Vergleich zu anderen Routing-Daemons für Anycast ab?
| Eigenschaft | BIRD2 | FRRouting | ExaBGP |
|---|---|---|---|
| Konfigurationssprache | Deklarative Filter | Cisco-ähnliche CLI | JSON/API |
| Anycast-Integration | Direct-Protokoll auf Dummy-Interface | Statische Route + Redistribution | Externes Skript kündigt an/zieht zurück |
| Health-Check-Methode | Interface-Up/Down löst Routenänderung aus | vtysh-Befehle aus Skript | Eingebauter Prozessmanager |
| IPv6-Unterstützung | Einheitliche Konfiguration (IPv4/IPv6-Kanäle) | Separate Adressfamilien | Manuell pro Familie |
| Filterkomplexität | Vollständige Filtersprache mit Funktionen | Route Maps, Prefix Lists | Nur externe Logik |
| Speicherverbrauch | Niedrig (~10 MB bei kleiner Tabelle) | Mittel (~30 MB) | Sehr niedrig (~5 MB) |
| Produktionsreife | IXPs, großangelegtes DNS (Cloudflare, RIPE) | ISPs, Rechenzentren | Kleine Deployments, Monitoring |
Der Vorteil von BIRD2 für Anycast: Das protocol direct auf einem Dummy-Interface bietet automatischen Routenrückzug, wenn das Interface ausfällt. Kein externes Scripting für den BGP-Teil nötig. Der Health-Check muss nur das Interface umschalten.
Wie synchronisiere ich DNS-Zonen über Anycast-Knoten hinweg?
Die Zonensynchronisation nutzt die in BIND9 eingebaute Primär/Sekundär-Replikation über AXFR, authentifiziert mit TSIG. Die Konfiguration wurde im BIND9-Abschnitt oben vorgenommen. Hier die operativen Details.
Zonenaktualisierungen fließen in eine Richtung: Bearbeiten Sie die Zonendatei auf Knoten A (primär), erhöhen Sie die Seriennummer und laden Sie neu:
# 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
Führen Sie immer named-checkzone vor dem Neuladen aus. Es findet Syntaxfehler.
rndc reload example.com
Knoten B erhält ein NOTIFY von Knoten A und holt die aktualisierte Zone per AXFR. Auf Knoten B die Seriennummer prüfen:
dig @127.0.0.1 example.com SOA +short
Die Seriennummer muss übereinstimmen. Falls nicht, prüfen Sie:
- Die Firewall erlaubt TCP 53 von Knoten A zu Knoten B
- Der TSIG-Schlüssel ist auf beiden Knoten identisch
journalctl -u namedauf Knoten B auf Transferfehler
Dritten Knoten hinzufügen: Konfigurieren Sie ihn als weiteren sekundären Knoten. Fügen Sie seine IP zur also-notify-Liste und den nftables-Regeln auf Knoten A hinzu. Keine Änderungen an bestehenden sekundären Knoten nötig.
Was ist mit IPv6-BGP-Anycast?
Die obige BIRD2-Konfiguration enthält bereits IPv6. Der protocol direct anycast-Block hat beide Kanäle ipv4 und ipv6. Die upstream6-BGP-Session exportiert das /48-Präfix.
IPv6-Routenexport prüfen:
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 über IPv6 testen:
dig @2001:db8:abcd::1 example.com AAAA +short
Fehlerbehebung
BGP-Session hängt in Active/Connect:
journalctl -u bird -f
Häufige Ursachen: falsche Neighbor-IP, falsche ASN, MD5-Passwort-Mismatch, Firewall blockiert TCP 179. Führen Sie birdc show protocols all upstream4 für detaillierte Fehlermeldungen aus.
BIND9 lauscht nicht auf der Anycast-IP:
ss -tlnp | grep ':53'
Sehen Sie nur 127.0.0.1:53, prüfen Sie, ob listen-on die Anycast-IP enthält und ob das anycast0-Interface vor dem Start von BIND9 aktiv war. Starten Sie BIND9 nach dem Hochfahren des Interfaces neu: systemctl restart named.
Zonentransfer schlägt fehl:
journalctl -u named | grep -i "tsig\|transfer\|refused"
Prüfen Sie, ob die Schlüsseldatei vom bind-Benutzer gelesen werden kann: ls -la /etc/bind/anycast-transfer.key. Manuellen Transfertest ausführen: dig @203.0.113.10 example.com AXFR -k /etc/bind/anycast-transfer.key.
Health-Check zieht Routen nicht zurück:
journalctl -u anycast-healthcheck -f
Sudo-Berechtigungen prüfen: sudo -u anycast-healthcheck sudo -l. Der Benutzer sollte /usr/sbin/ip link set anycast0 up und down ohne Passwort ausführen können.
Abfragen laufen nach Failover in ein Timeout:
DNS-Clients cachen Antworten. Der TTL in der Zonendatei (300 Sekunden / 5 Minuten) bestimmt, wie lange Clients veraltete Daten verwenden. Die BGP-Konvergenz fügt 30-90 Sekunden hinzu. Gesamte Failover-Zeit aus Client-Sicht: bis zum TTL plus Konvergenzzeit. Senken Sie den SOA-Minimum-TTL, wenn schnelleres Failover wichtig ist, aber gehen Sie für autoritative Zonen nicht unter 60 Sekunden.
Produktions-Checkliste
Vor dem Go-Live:
- ROA-Einträge erstellt und validiert (
rpki-clientoder Ihr RIR-Portal) - BGP-Sessions auf allen Knoten etabliert (
birdc show protocols) - Anycast-Präfix in globalen Routing-Tabellen sichtbar (nutzen Sie ein Looking Glass)
- BIND9 antwortet auf der Anycast-IP aus externen Netzen
- Zonenseriennummer stimmt auf allen Knoten überein
- TSIG-Schlüssel-Berechtigungen auf 640 gesetzt, Eigentümer root:bind
- nftables-Regeln auf allen Knoten aktiv
- Health-Check-Service aktiviert und läuft
- Failover getestet: BIND9 gestoppt, Routenrückzug bestätigt, Wiederherstellung bestätigt
- Monitoring konfiguriert: Alarme bei BGP-Session-Down, BIND9-Prozess, Health-Check-Fehlern
-
journalctl -u birdundjournalctl -u namedzeigen keine Fehler
Für die übergeordnete Anleitung zu BGP-Grundlagen und eigenem IP-Adressraum siehe BGP und Bring Your Own IP auf einem VPS: Der vollständige Leitfaden. Für BGP-Routenfilterung und Sicherheitshärtung siehe BGP Route Filtering: Prefix-Listen, AS-Path-Filter, Bogon-Ablehnung und GTSM.
Bereit, es selbst auszuprobieren?
Betreiben Sie BGP-Sessions auf Ihrem eigenen IP-Bereich mit Virtua.Cloud VPS. →