Commit Graph

10 Commits

Author SHA1 Message Date
Debian
18ee243a44 feat(keepalived): VIP wandert zum PG-Primary zurück (preempt_delay) — v1.3.9
Bisher trugen beide VRRP-Instanzen `nopreempt` → ein erholter Primary holte die
VIP NICHT zurück; nach einem Deploy-/VM-Blip blieb sie auf dem Standby kleben
(genau die Situation: VIP auf utm-2 obwohl utm-1 der PG-Primary ist).

Jetzt: der bevorzugte Node (PG-Primary, Prio 200) rendert `preempt_delay 120`
statt nopreempt → er holt die VIP nach 120s STABILER Erholung heim. Der Standby
(Prio 100) behält nopreempt (reißt die VIP nie an sich → Split-Brain-Schutz).
Der 120s-Delay + gw-check + Heartbeat-Sync-Group verhindern Flap-Back bei kurzen
Hicks. State bleibt immer BACKUP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-31 17:56:53 +02:00
Debian
02096c8ad8 chore(lint): golangci-lint --fix — misspell + staticcheck-Autofixes (Backlog 142→~98)
Erster Schritt des golangci-lint-Rollouts (non-blocking): 44 misspell + 4
staticcheck automatisch behoben (32 Dateien, nur Tippfehler/mechanisch).
build+test grün. Kein Runtime-Change → kein Deploy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 23:25:22 +02:00
Debian
f3c76f6d18 fix(cluster): selbstheilende public_ip + advert_int 2 (Flapping) — v1.2.107
Zwei keepalived-Flapping-Restursachen:
1) Strukturell: der Cluster-Push (autoRegister, Primary→Secondary) trägt KEINE
   public_ip → der Empfänger AgentRegisterPeer ließ sie NULL → Peer fehlte im
   nft-peer_ipv4-Set → VRRP-Adverts (eth0/VI_1) nur via conntrack → Flapping.
   (utm-1 lernte utm-2 korrekt via Joiner-Client-IP in preRegisterJoiner; die
   Gegenrichtung fehlte.) Fix: AgentRegisterPeer fällt bei leerem req.PublicIP
   auf die mTLS-Client-IP (c.ClientIP()) zurück — die EIGEN-IP des Peers, nicht
   die VIP. Selbstheilend, überlebt Re-Joins/Failover.
2) advert_int 1 → 2 (Master-Down ~6s statt ~3s): reißt nicht mehr bei kurzen
   VM-/Heartbeat-Hiccups (VI_HB). Trade-off: Failover-Erkennung ~6s.

Tests: advert_int 2 im Render.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 13:42:21 +02:00
Debian
b2fc7b7dee fix(cluster): chk_edgeguard fall 3→8 — Upgrade-Restart triggert keinen Failover — v1.2.106
Seit v1.2.105 (Track-Scripts ohne weight = FAULT-Trigger) löste jeder
edgeguard-api-Restart beim Deploy einen Failover aus (kurze Health-Check-Fehler
während Stop/Render/Restart reichten bei fall 3 = 6s). fall 8 (16s) lässt einen
normalen Upgrade-Restart durchrutschen; ein echter API-Tod schwenkt weiter in 16s.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 19:14:08 +02:00
Debian
9b563baaa1 fix(cluster): keepalived Track-Scripts ohne weight (Sync-Group) — v1.2.105
`keepalived -t` zeigte "ignoring tracked script chk_edgeguard/chk_gateway with
weights due to SYNC group": gewichtete Track-Scripts werden in einer
vrrp_sync_group ignoriert → Health-Check-Failover (Gateway weg / API tot) griff
NICHT. Fix: weight entfernt → Scripts wirken als binäre FAULT-Trigger (fall-mal
Fehler → Instanz+Sync-Group FAULT → gesunder Peer übernimmt). Für 2-Node-Cluster
die korrekte Semantik.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 19:07:24 +02:00
Debian
d8b8fef680 fix(cluster): keepalived 2.3.x lehnt vrrp_garp_interval 0 ab — entfernt — v1.2.104
v1.2.103 hatte vrrp_garp_interval 0 / vrrp_gna_interval 0 aus dem Legacy-Template
übernommen. keepalived 2.3.3 lehnt 0 ab (Range [0.000001, …]) → "invalid",
`keepalived -t` schlägt fehl (keepalived ignoriert sie zwar mit Warnung und läuft,
aber latentes Risiko bei striktem Start). Entfernt — Default passt. Die wirksame
Anti-Aging-Direktive vrrp_garp_master_refresh 60 + master_repeat 5 bleiben.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 19:02:02 +02:00
Debian
fd8125247c fix(cluster): VIP-Failover am Upstream zuverlässig — GARP + Priming-Ping — v1.2.103
Nach v1.2.102 (VIP nicht mehr statisch gebunden) wandert die Public-VIP .100
erstmals wirklich per keepalived zwischen den Node-MACs. Der Hosting-Upstream
lernte die neue MAC aber nicht zuverlässig → .100 nach Schwenk von außen
unerreichbar (vorher maskiert, weil .100 statisch dauerhaft auf utm-1 lag).

Zwei Mechanismen ergänzt:
1) keepalived.conf.tpl global_defs: vrrp_garp_master_repeat 5 +
   vrrp_garp_master_refresh 60 (+ vrrp_garp_interval/gna 0, aus Legacy-Template
   verloren gegangen) → forciertes GARP beim Wechsel + periodische Auffrischung,
   damit die VIP-MAC am Switch nicht altert.
2) keepalived-master.sh: neuer Master pingt den Default-Gateway aus jeder
   Public-IP/VIP an (ping -I <vip>) → Upstream sieht Traffic VON der VIP und
   lernt die MAC sofort. Manche Hoster relearnen nur so, nicht via GARP.

Test: keepalived_test.go prüft GARP-Direktiven im Render.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 18:56:32 +02:00
Debian
a2450a759c fix(cluster): keepalived Split-Brain + Boot-Race behoben — v1.2.101
Ursache der „WireGuard reißt immer wieder ab"-Abrisse war NICHT die UniFi,
sondern keepalived-Flapping im HA-Cluster: die VIP 89.163.205.100 (an der die
UniFi-Site-to-Site hängt) wanderte bei ~17 VRRP-Wahlen/Tag zwischen utm-1/utm-2
→ Tunnel-Abriss bei jeder Wahl.

Drei Bugs:
1) Firewall ließ VRRP (IP-Proto 112) zwischen den Cluster-Peers NICHT zu
   (policy drop). Adverts überlebten nur via conntrack-Reverse-Matching → bei
   conntrack-Ablauf gedroppt → Peer promotet sich → Split-Brain.
   Fix: ruleset.nft.tpl erlaubt `ip/ip6 ... vrrp saddr @peer_ipv4/6`;
   firewall.go nimmt zusätzlich hb_src_ip/hb_peer_ip aus cluster_settings ins
   Peer-Set (deckt den Heartbeat-Pfad 169.254.0.x ab).
2) Kein nopreempt → erholter Node riss die VIP sofort zurück (Flap-Back);
   aggressiver gw-Check (fall 2 → 10s-Blip = Failover).
   Fix: keepalived.conf.tpl mit `nopreempt` in VI_1+VI_HB, chk_gateway fall 2→5;
   keepalived.go setzt State immer BACKUP (nopreempt wirkt nur in BACKUP),
   Priorität 200/100 aus pg_role bleibt → deckt sich mit „manuelles Promote".
3) keepalived-Boot-Race: Unit startete vor vlan500 (nur After=network-online)
   → „interface vlan500 doesn't exist" → permanenter CONFIG-Crash ohne Recovery
   (keepalived nach Reboot tot). Fix: postinst legt Drop-in mit
   After=/Wants=edgeguard-interfaces.service + Restart=on-failure an.

Neuer Test internal/keepalived/keepalived_test.go (nopreempt/BACKUP/fall).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 17:49:04 +02:00
Debian
1d06b28064 feat: HA-Cluster v1.2.x — Split-Brain, TOTP, Enterprise-FW, Drift-Fix, VIP-Recovery
- keepalived: pg_role='standby' hat Vorrang vor role für BACKUP-Bestimmung
- keepalived-master.sh: gecrasht Dienste beim MASTER-Übergang starten (nicht nur reload)
- confighash: ip_addresses per Interface-Name hashen statt per FK (Cross-Node-Drift-Fix)
- TOTP/2FA: RFC 6238 — Setup-Flow, QR-Code, Admin-Disable; two-step Login
- Firewall-UI: Enterprise-Design — auto-Beschreibung, icon-only Actions, zero-hit Indikator
- fe80-Filter: Link-local IPv6 aus NTP/DNS Listen-Dropdowns entfernen
- VIP-Dashboard, Dual-Path VRRP, GW-Tracking (Migrations 0033/0034)
- Forward Proxy + DNS erweiterte Einstellungen (Migrations 0031/0032)
- unbound-control: edgeguard in unbound-Gruppe via postinst

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 18:18:31 +02:00
Debian
25c7cd0cb5 feat(cluster): PG Logical Replication + VIP/Keepalived + config_hash sync (v1.2.1–1.2.2)
- PG Logical Replication: edgeguard_shared PUBLICATION auf Primary,
  edgeguard_sub SUBSCRIPTION auf Secondary. Nur geteilte Config-Tabellen
  werden repliziert; node-eigene Daten (network_interfaces, ip_addresses,
  static_routes, cluster_settings, dns_settings, ntp_settings) bleiben
  lokal — OPNsense-Muster.
- cluster-init-replication: Erstellt PUBLICATION, Rolle + pg_hba-Einträge
  (logical + replication), WAL-Level auf logical.
- cluster-setup-standby: Erstellt SUBSCRIPTION (copy_data=true), pollt
  pg_subscription_rel bis alle Tabellen sync = 'r', rendert dann Configs.
- promote: manueller Failover via pg_promote() + touch recovery.signal.
- VIP/Keepalived: cluster_settings-Tabelle (vip_address, vip_interface,
  vrrp_router_id), /cluster/vip-settings API, Keepalived-Config-Generator
  mit VRRP + check_script + notify-Skripten in /usr/lib/edgeguard/scripts/.
- config_hash sync: Secondary pusht alle 5 Min seinen Hash via mTLS an
  Primary (PushSelfToPrimary). Heartbeat schreibt nur LOCAL, daher ohne
  aktiven Push wäre Primary-Sicht des Secondary-Hash stale gewesen.
- runSecondaryConfigRender: Goroutine auf Secondary rendert HAProxy+nftables
  neu wenn config_hash sich ändert (Logical-Replication-Nachzügler).
- confighash: node-spezifische Tabellen aus hashSpec entfernt.
- postinst: Keepalived-Skripte installieren, sudoers für keepalived.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 23:40:37 +02:00