- 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>
88 lines
2.6 KiB
Go
88 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.netcell-it.de/projekte/edgeguard-native/internal/chrony"
|
|
"git.netcell-it.de/projekte/edgeguard-native/internal/configgen"
|
|
"git.netcell-it.de/projekte/edgeguard-native/internal/database"
|
|
"git.netcell-it.de/projekte/edgeguard-native/internal/firewall"
|
|
"git.netcell-it.de/projekte/edgeguard-native/internal/haproxy"
|
|
"git.netcell-it.de/projekte/edgeguard-native/internal/cluster"
|
|
"git.netcell-it.de/projekte/edgeguard-native/internal/keepalived"
|
|
"git.netcell-it.de/projekte/edgeguard-native/internal/services/configorch"
|
|
"git.netcell-it.de/projekte/edgeguard-native/internal/services/secrets"
|
|
"git.netcell-it.de/projekte/edgeguard-native/internal/squid"
|
|
"git.netcell-it.de/projekte/edgeguard-native/internal/unbound"
|
|
"git.netcell-it.de/projekte/edgeguard-native/internal/wireguard"
|
|
)
|
|
|
|
// cmdRenderConfig regenerates every per-service config file from PG
|
|
// state. Used after package install (postinst), after admin
|
|
// mutations (UI button), or as a manual recovery action.
|
|
//
|
|
// Flags:
|
|
//
|
|
// --no-reload Write configs but skip systemctl reload.
|
|
// --only=svc1,svc2 Run only the named generators.
|
|
func cmdRenderConfig(args []string) int {
|
|
skipReload := false
|
|
var only []string
|
|
for i := 0; i < len(args); i++ {
|
|
switch {
|
|
case args[i] == "--no-reload":
|
|
skipReload = true
|
|
case strings.HasPrefix(args[i], "--only="):
|
|
val := strings.TrimPrefix(args[i], "--only=")
|
|
only = strings.Split(val, ",")
|
|
case args[i] == "-h" || args[i] == "--help":
|
|
fmt.Println("Usage: edgeguard-ctl render-config [--no-reload] [--only=svc1,svc2]")
|
|
return 0
|
|
}
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
|
defer cancel()
|
|
|
|
pool, err := database.Open(ctx, database.ConnStringFromEnv())
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "render-config: open db:", err)
|
|
return 1
|
|
}
|
|
defer pool.Close()
|
|
|
|
hap := haproxy.New(pool)
|
|
fw := firewall.New(pool)
|
|
sq := squid.New(pool)
|
|
wg := wireguard.New(pool, secrets.New(""))
|
|
ub := unbound.New(pool)
|
|
cn := chrony.New(pool)
|
|
if skipReload {
|
|
hap.SkipReload = true
|
|
fw.SkipReload = true
|
|
}
|
|
|
|
// keepalived: Node-ID aus node.conf für Prioritäts-Berechnung
|
|
var ka configgen.Generator
|
|
if lc, err := cluster.LoadLocalConfig(""); err == nil && lc.NodeID != "" {
|
|
ka = keepalived.New(pool, lc.NodeID)
|
|
}
|
|
|
|
gens := []configgen.Generator{hap, fw, sq, wg, ub, cn}
|
|
if ka != nil {
|
|
gens = append(gens, ka)
|
|
}
|
|
|
|
results, runErr := configorch.Run(ctx, gens, only)
|
|
fmt.Print(configorch.Summarise(results))
|
|
if runErr != nil {
|
|
fmt.Fprintln(os.Stderr, "render-config aborted:", runErr)
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|