diff --git a/VERSION b/VERSION index ff710f5..760f327 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.102 \ No newline at end of file +1.2.103 \ No newline at end of file diff --git a/internal/keepalived/keepalived.conf.tpl b/internal/keepalived/keepalived.conf.tpl index 98e6c1b..da4e24b 100644 --- a/internal/keepalived/keepalived.conf.tpl +++ b/internal/keepalived/keepalived.conf.tpl @@ -2,6 +2,14 @@ global_defs { router_id {{ .RouterID }} script_user root enable_script_security + # GARP: beim Master-Wechsel Gratuitous-ARP forciert senden (repeat) UND + # periodisch auffrischen (master_refresh) — sonst lässt der Upstream-Switch + # die VIP-MAC altern und die Failover-IP wird nach Minuten unerreichbar. + # Ergänzt durch den Priming-Ping in keepalived-master.sh (Traffic AUS der VIP). + vrrp_garp_interval 0 + vrrp_gna_interval 0 + vrrp_garp_master_repeat 5 + vrrp_garp_master_refresh 60 } vrrp_script chk_edgeguard { diff --git a/internal/keepalived/keepalived_test.go b/internal/keepalived/keepalived_test.go index abb6ea1..f7a1f3a 100644 --- a/internal/keepalived/keepalived_test.go +++ b/internal/keepalived/keepalived_test.go @@ -43,6 +43,17 @@ func TestTemplateNopreemptOnBothInstances(t *testing.T) { } } +// GARP muss forciert + periodisch aufgefrischt werden, sonst altert die +// VIP-MAC am Upstream-Switch und die Failover-IP wird unerreichbar. +func TestTemplateGARPRefresh(t *testing.T) { + out := render(t, testView()) + for _, want := range []string{"vrrp_garp_master_refresh", "vrrp_garp_master_repeat"} { + if !strings.Contains(out, want) { + t.Fatalf("global_defs sollte %q enthalten:\n%s", want, out) + } + } +} + // gw-Check darf nicht zu zucken (fall 5, nicht fall 2) — ein kurzer Upstream- // Blip soll keinen Failover erzwingen. func TestTemplateGatewayCheckNotTwitchy(t *testing.T) { diff --git a/packaging/scripts/keepalived-master.sh b/packaging/scripts/keepalived-master.sh index 7086722..282071c 100644 --- a/packaging/scripts/keepalived-master.sh +++ b/packaging/scripts/keepalived-master.sh @@ -7,6 +7,21 @@ logger -t keepalived -p daemon.warning \ "MASTER: VIP übernommen — PG-Rolle ist noch '$(cat /var/lib/edgeguard/pg_role 2>/dev/null || echo standby)'. Für PG-Failover: edgeguard-ctl promote" +# ── Upstream-ARP/Routing für die Failover-VIP(s) aktualisieren ── +# Manche Hoster lernen die neue MAC einer Failover-IP NICHT zuverlässig über +# Gratuitous-ARP, sondern erst, wenn sie Traffic VON der IP sehen. Ohne das ist +# die VIP nach einem Schwenk von außen unerreichbar. Wir pingen daher den +# Default-Gateway aus jeder Public-IP (inkl. VIP) an (source via -I), damit der +# Upstream die MAC sofort umlernt. Hintergrund-Pings, damit notify nicht blockt. +WAN_DEV="$(ip -4 route show default 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="dev"){print $(i+1); exit}}')" +WAN_GW="$(ip -4 route show default 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="via"){print $(i+1); exit}}')" +if [ -n "$WAN_DEV" ] && [ -n "$WAN_GW" ]; then + for vip in $(ip -4 -o addr show dev "$WAN_DEV" scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1); do + ping -I "$vip" -c 3 -W 1 "$WAN_GW" >/dev/null 2>&1 & + done + logger -t keepalived -p daemon.info "MASTER: Upstream-ARP via Ping aus VIP(s) auf $WAN_GW ($WAN_DEV) angestoßen" +fi + # Dienste reloaden/starten damit sie die neu aktiven VIPs binden. # Squid + Unbound + HAProxy binden beim Start an spezifische IPs — war der Dienst # während des BACKUP-Zustands gecrasht oder gestoppt, muss er gestartet werden.