diff --git a/pkg/auth/credentials.go b/pkg/auth/credentials.go index 5017165..3eefd9c 100644 --- a/pkg/auth/credentials.go +++ b/pkg/auth/credentials.go @@ -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 diff --git a/pkg/auth/simple_auth.go b/pkg/auth/simple_auth.go index 433974a..8d4cfa9 100644 --- a/pkg/auth/simple_auth.go +++ b/pkg/auth/simple_auth.go @@ -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") diff --git a/pkg/cli/auth_commands.go b/pkg/cli/auth_commands.go index 8cf14ab..f5e675d 100644 --- a/pkg/cli/auth_commands.go +++ b/pkg/cli/auth_commands.go @@ -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")) } diff --git a/pkg/node/dns_registration.go b/pkg/node/dns_registration.go index 134713a..197af09 100644 --- a/pkg/node/dns_registration.go +++ b/pkg/node/dns_registration.go @@ -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 }