diff --git a/VERSION b/VERSION index 258b768..2ac243d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.154 +1.1.155 diff --git a/cmd/edgeguard-api/main.go b/cmd/edgeguard-api/main.go index 474618d..7ab7f8b 100644 --- a/cmd/edgeguard-api/main.go +++ b/cmd/edgeguard-api/main.go @@ -60,7 +60,7 @@ import ( usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users" ) -var version = "1.1.154" +var version = "1.1.155" func main() { addr := os.Getenv("EDGEGUARD_API_ADDR") @@ -276,6 +276,9 @@ func main() { "wireguard": wgrender.New(pool, secretsBox).RenderToString, }) setupHdl.WithAudit(auditRepo, nodeID) + setupHdl.WithClusterSupport(clusterStore, func(ctx context.Context) error { + return firewallrender.New(pool).Render(ctx) + }) usersRepo := usersvc.New(pool) authHdl.WithAudit(auditRepo, nodeID).WithUsers(usersRepo) diff --git a/cmd/edgeguard-ctl/main.go b/cmd/edgeguard-ctl/main.go index 7007e4d..870f8df 100644 --- a/cmd/edgeguard-ctl/main.go +++ b/cmd/edgeguard-ctl/main.go @@ -11,7 +11,7 @@ import ( "git.netcell-it.de/projekte/edgeguard-native/internal/services/setup" ) -var version = "1.1.154" +var version = "1.1.155" const usage = `edgeguard-ctl — EdgeGuard CLI diff --git a/cmd/edgeguard-scheduler/main.go b/cmd/edgeguard-scheduler/main.go index 715a3f7..93a9df7 100644 --- a/cmd/edgeguard-scheduler/main.go +++ b/cmd/edgeguard-scheduler/main.go @@ -41,7 +41,7 @@ import ( "git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts" ) -var version = "1.1.154" +var version = "1.1.155" const ( // renewTickInterval — how often we re-evaluate expiring certs. diff --git a/internal/handlers/setup.go b/internal/handlers/setup.go index 4bbabfb..ff29d04 100644 --- a/internal/handlers/setup.go +++ b/internal/handlers/setup.go @@ -1,11 +1,18 @@ package handlers import ( + "context" + "fmt" + "log/slog" + "net" "strings" + "time" "github.com/gin-gonic/gin" + "git.netcell-it.de/projekte/edgeguard-native/internal/cluster" "git.netcell-it.de/projekte/edgeguard-native/internal/handlers/response" + "git.netcell-it.de/projekte/edgeguard-native/internal/models" "git.netcell-it.de/projekte/edgeguard-native/internal/services/audit" "git.netcell-it.de/projekte/edgeguard-native/internal/services/clusterjoin" "git.netcell-it.de/projekte/edgeguard-native/internal/services/setup" @@ -15,10 +22,12 @@ import ( // are mounted before SetupGate so they remain reachable while the API // is in setup mode. type SetupHandler struct { - Store *setup.Store - Audit *audit.Repo - NodeID string - Version string + Store *setup.Store + Audit *audit.Repo + NodeID string + Version string + ClusterStore *cluster.Store + PeerReloader PeerReloader } func NewSetupHandler(store *setup.Store) *SetupHandler { @@ -40,6 +49,15 @@ func (h *SetupHandler) WithVersion(v string) *SetupHandler { return h } +// WithClusterSupport erlaubt dem JoinCluster-Handler nach dem Join den +// Primary in der lokalen ha_nodes zu registrieren + nftables neu zu laden, +// damit Port 8443 bidirektional offen ist. +func (h *SetupHandler) WithClusterSupport(store *cluster.Store, reloader PeerReloader) *SetupHandler { + h.ClusterStore = store + h.PeerReloader = reloader + return h +} + func (h *SetupHandler) Register(rg *gin.RouterGroup) { g := rg.Group("/setup") g.GET("/status", h.Status) @@ -180,9 +198,55 @@ func (h *SetupHandler) JoinCluster(c *gin.Context) { response.Internal(c, err) return } + + // Pre-register the primary in our local ha_nodes so its IP lands in + // @peer_ipv4 and port 8443 is open bidirectionally. + if h.ClusterStore != nil && h.PeerReloader != nil { + go h.preRegisterPrimary(body.PrimaryFQDN) + } + response.OK(c, gin.H{ "completed": st.Completed, "is_cluster_node": st.IsClusterNode, "fqdn": st.FQDN, }) } + +// preRegisterPrimary inserts the primary node into the local ha_nodes with +// its resolved IP so nftables @peer_ipv4 allows port 8443 from the primary. +func (h *SetupHandler) preRegisterPrimary(primaryFQDN string) { + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + + primaryFQDN = strings.ToLower(strings.TrimSpace(primaryFQDN)) + + addrs, err := net.DefaultResolver.LookupHost(ctx, primaryFQDN) + if err != nil || len(addrs) == 0 { + slog.Warn("setup: could not resolve primary FQDN for pre-registration", + "fqdn", primaryFQDN, "error", err) + return + } + ip := addrs[0] + + nodeID := fmt.Sprintf("primary-%x", time.Now().UnixNano()) + n := models.HANode{ + ID: nodeID, + Name: primaryFQDN, + FQDN: primaryFQDN, + APIURL: "https://" + primaryFQDN + ":3443", + Role: "primary", + Status: "online", + } + n.PublicIP = &ip + + if _, err := h.ClusterStore.UpsertSelf(ctx, n); err != nil { + slog.Warn("setup: pre-register primary in ha_nodes failed", "fqdn", primaryFQDN, "error", err) + return + } + if err := h.PeerReloader(ctx); err != nil { + slog.Warn("setup: PeerReloader failed after primary pre-register", "error", err) + return + } + slog.Info("setup: primary pre-registered locally, firewall updated", + "fqdn", primaryFQDN, "ip", ip) +}