Fix ensure only nameservers nodes added on schema for caddy load balancing

This commit is contained in:
anonpenguin23 2026-02-02 11:17:54 +02:00
parent e95ecfb12a
commit 79a489d650
4 changed files with 79 additions and 9 deletions

View File

@ -19,6 +19,7 @@ type Credentials struct {
IssuedAt time.Time `json:"issued_at"`
LastUsedAt time.Time `json:"last_used_at,omitempty"`
Plan string `json:"plan,omitempty"`
NamespaceURL string `json:"namespace_url,omitempty"`
}
// CredentialStore manages credentials for multiple gateways

View File

@ -72,13 +72,20 @@ func PerformSimpleAuthentication(gatewayURL, wallet, namespace string) (*Credent
return nil, fmt.Errorf("failed to request API key: %w", err)
}
// Build namespace gateway URL from the gateway URL
namespaceURL := ""
if domain := extractDomainFromURL(gatewayURL); domain != "" {
namespaceURL = fmt.Sprintf("https://ns-%s.%s", namespace, domain)
}
// Create credentials
creds := &Credentials{
APIKey: apiKey,
Namespace: namespace,
UserID: wallet,
Wallet: wallet,
IssuedAt: time.Now(),
APIKey: apiKey,
Namespace: namespace,
UserID: wallet,
Wallet: wallet,
IssuedAt: time.Now(),
NamespaceURL: namespaceURL,
}
fmt.Printf("\n🎉 Authentication successful!\n")

View File

@ -156,6 +156,9 @@ func handleAuthLogin(wallet, namespace string) {
fmt.Printf("🎯 Wallet: %s\n", creds.Wallet)
fmt.Printf("🏢 Namespace: %s\n", creds.Namespace)
fmt.Printf("🔑 API Key: %s\n", creds.APIKey)
if creds.NamespaceURL != "" {
fmt.Printf("🌐 Namespace URL: %s\n", creds.NamespaceURL)
}
}
func handleAuthLogout() {
@ -184,6 +187,9 @@ func handleAuthWhoami() {
fmt.Println("✅ Authenticated")
fmt.Printf(" Wallet: %s\n", creds.Wallet)
fmt.Printf(" Namespace: %s\n", creds.Namespace)
if creds.NamespaceURL != "" {
fmt.Printf(" NS Gateway: %s\n", creds.NamespaceURL)
}
fmt.Printf(" Issued At: %s\n", creds.IssuedAt.Format("2006-01-02 15:04:05"))
if !creds.ExpiresAt.IsZero() {
fmt.Printf(" Expires At: %s\n", creds.ExpiresAt.Format("2006-01-02 15:04:05"))
@ -231,6 +237,9 @@ func handleAuthStatus() {
fmt.Println(" Status: ✅ Authenticated")
fmt.Printf(" Wallet: %s\n", creds.Wallet)
fmt.Printf(" Namespace: %s\n", creds.Namespace)
if creds.NamespaceURL != "" {
fmt.Printf(" NS Gateway: %s\n", creds.NamespaceURL)
}
if !creds.ExpiresAt.IsZero() {
fmt.Printf(" Expires: %s\n", creds.ExpiresAt.Format("2006-01-02 15:04:05"))
}

View File

@ -4,6 +4,9 @@ import (
"context"
"fmt"
"net"
"os"
"path/filepath"
"strings"
"time"
"github.com/DeBrosOfficial/network/pkg/logging"
@ -143,8 +146,10 @@ func (n *Node) ensureBaseDNSRecords(ctx context.Context) error {
value string
}
// Base domain records (e.g., dbrs.space, *.dbrs.space) — for round-robin across all nodes
if baseDomain != "" {
// Base domain records (e.g., dbrs.space, *.dbrs.space) — only for nameserver nodes.
// Only nameserver nodes run Caddy (HTTPS), so only they should appear in base domain
// round-robin. Non-nameserver nodes would cause TLS failures for clients.
if baseDomain != "" && n.isNameserverNode(ctx) {
records = append(records,
struct{ fqdn, value string }{baseDomain + ".", ipAddress},
struct{ fqdn, value string }{"*." + baseDomain + ".", ipAddress},
@ -176,8 +181,9 @@ func (n *Node) ensureBaseDNSRecords(ctx context.Context) error {
n.ensureSOAAndNSRecords(ctx, baseDomain)
}
// Claim an NS slot for the base domain (ns1/ns2/ns3)
if baseDomain != "" {
// Claim an NS slot for the base domain (ns1/ns2/ns3) — only if this node
// was installed with --nameserver (i.e. runs Caddy + CoreDNS).
if baseDomain != "" && n.isNameserverPreference() {
n.claimNameserverSlot(ctx, baseDomain, ipAddress)
}
@ -369,6 +375,37 @@ func (n *Node) cleanupStaleNodeRecords(ctx context.Context) {
}
}
// isNameserverPreference checks if this node was installed with --nameserver flag
// by reading the preferences.yaml file. Only nameserver nodes should claim NS slots.
func (n *Node) isNameserverPreference() bool {
oramaDir := filepath.Join(os.ExpandEnv(n.config.Node.DataDir), "..")
prefsPath := filepath.Join(oramaDir, "preferences.yaml")
data, err := os.ReadFile(prefsPath)
if err != nil {
return false
}
// Simple check: look for "nameserver: true" in the YAML
return strings.Contains(string(data), "nameserver: true")
}
// isNameserverNode checks if this node has claimed a nameserver slot (ns1/ns2/ns3).
// Only nameserver nodes run Caddy for HTTPS, so only they should be in base domain DNS.
func (n *Node) isNameserverNode(ctx context.Context) bool {
if n.rqliteAdapter == nil {
return false
}
nodeID := n.GetPeerID()
if nodeID == "" {
return false
}
db := n.rqliteAdapter.GetSQLDB()
var count int
err := db.QueryRowContext(ctx,
`SELECT COUNT(*) FROM dns_nameservers WHERE node_id = ?`, nodeID,
).Scan(&count)
return err == nil && count > 0
}
// getWireGuardIP returns the IPv4 address assigned to the wg0 interface, if any
func (n *Node) getWireGuardIP() (string, error) {
iface, err := net.InterfaceByName("wg0")
@ -411,5 +448,21 @@ func (n *Node) getNodeIPAddress() (string, error) {
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
if localAddr.IP.IsPrivate() || localAddr.IP.IsLoopback() {
// UDP dial returned a private/loopback IP (e.g. WireGuard 10.0.0.x).
// Fall back to scanning interfaces for a public IPv4.
addrs, err := net.InterfaceAddrs()
if err != nil {
return "", fmt.Errorf("private IP detected (%s) and failed to list interfaces: %w", localAddr.IP, err)
}
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() && !ipnet.IP.IsPrivate() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String(), nil
}
}
}
return "", fmt.Errorf("private IP detected (%s) and no public IPv4 found on interfaces", localAddr.IP)
}
return localAddr.IP.String(), nil
}