fix(cluster): Primary bidirektional in ha_nodes eintragen nach Join
Nach erfolgreichem cluster-join trägt der Joining-Node (utm-2) den Primary per DNS-Lookup in seine lokale ha_nodes ein und lädt nftables neu. Damit ist @peer_ipv4 auf utm-2 sofort mit utm-1's IP befüllt und Port 8443 ist bidirektional offen — der Aggregator auf utm-1 kann utm-2 auf :8443 abklappern. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -60,7 +60,7 @@ import (
|
|||||||
usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users"
|
usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.1.154"
|
var version = "1.1.155"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
addr := os.Getenv("EDGEGUARD_API_ADDR")
|
addr := os.Getenv("EDGEGUARD_API_ADDR")
|
||||||
@@ -276,6 +276,9 @@ func main() {
|
|||||||
"wireguard": wgrender.New(pool, secretsBox).RenderToString,
|
"wireguard": wgrender.New(pool, secretsBox).RenderToString,
|
||||||
})
|
})
|
||||||
setupHdl.WithAudit(auditRepo, nodeID)
|
setupHdl.WithAudit(auditRepo, nodeID)
|
||||||
|
setupHdl.WithClusterSupport(clusterStore, func(ctx context.Context) error {
|
||||||
|
return firewallrender.New(pool).Render(ctx)
|
||||||
|
})
|
||||||
usersRepo := usersvc.New(pool)
|
usersRepo := usersvc.New(pool)
|
||||||
authHdl.WithAudit(auditRepo, nodeID).WithUsers(usersRepo)
|
authHdl.WithAudit(auditRepo, nodeID).WithUsers(usersRepo)
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/setup"
|
"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
|
const usage = `edgeguard-ctl — EdgeGuard CLI
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ import (
|
|||||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
|
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.1.154"
|
var version = "1.1.155"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// renewTickInterval — how often we re-evaluate expiring certs.
|
// renewTickInterval — how often we re-evaluate expiring certs.
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/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/audit"
|
||||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/clusterjoin"
|
"git.netcell-it.de/projekte/edgeguard-native/internal/services/clusterjoin"
|
||||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/setup"
|
"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
|
// are mounted before SetupGate so they remain reachable while the API
|
||||||
// is in setup mode.
|
// is in setup mode.
|
||||||
type SetupHandler struct {
|
type SetupHandler struct {
|
||||||
Store *setup.Store
|
Store *setup.Store
|
||||||
Audit *audit.Repo
|
Audit *audit.Repo
|
||||||
NodeID string
|
NodeID string
|
||||||
Version string
|
Version string
|
||||||
|
ClusterStore *cluster.Store
|
||||||
|
PeerReloader PeerReloader
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSetupHandler(store *setup.Store) *SetupHandler {
|
func NewSetupHandler(store *setup.Store) *SetupHandler {
|
||||||
@@ -40,6 +49,15 @@ func (h *SetupHandler) WithVersion(v string) *SetupHandler {
|
|||||||
return h
|
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) {
|
func (h *SetupHandler) Register(rg *gin.RouterGroup) {
|
||||||
g := rg.Group("/setup")
|
g := rg.Group("/setup")
|
||||||
g.GET("/status", h.Status)
|
g.GET("/status", h.Status)
|
||||||
@@ -180,9 +198,55 @@ func (h *SetupHandler) JoinCluster(c *gin.Context) {
|
|||||||
response.Internal(c, err)
|
response.Internal(c, err)
|
||||||
return
|
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{
|
response.OK(c, gin.H{
|
||||||
"completed": st.Completed,
|
"completed": st.Completed,
|
||||||
"is_cluster_node": st.IsClusterNode,
|
"is_cluster_node": st.IsClusterNode,
|
||||||
"fqdn": st.FQDN,
|
"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)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user