Extract the Central networking functionality from wild-cloud/api into a standalone service. Wild Central manages DNS (dnsmasq), gateway (HAProxy), firewall (nftables), VPN (WireGuard), TLS (certbot), security (CrowdSec), and DDNS — all the network appliance concerns. All Cloud-specific code (instances, clusters, nodes, apps, backups, operations, kubectl/talosctl tooling) has been removed. The API struct and route registration contain only Central endpoints. Tests updated to match the new API signature. Builds and all tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
105 lines
2.7 KiB
Go
105 lines
2.7 KiB
Go
package network
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os/exec"
|
|
"strings"
|
|
)
|
|
|
|
// NetworkInfo holds detected network configuration
|
|
type NetworkInfo struct {
|
|
PrimaryIP string `json:"primary_ip"`
|
|
PrimaryInterface string `json:"primary_interface"`
|
|
Gateway string `json:"gateway"`
|
|
}
|
|
|
|
// DetectNetworkInfo detects the machine's primary network configuration
|
|
// It finds the interface and IP used for internet-bound traffic by checking
|
|
// the route to a public DNS server (8.8.8.8)
|
|
func DetectNetworkInfo() (*NetworkInfo, error) {
|
|
// Use ip route to find the default route interface and IP
|
|
// This tells us which interface would be used to reach the internet
|
|
cmd := exec.Command("ip", "route", "get", "8.8.8.8")
|
|
output, err := cmd.Output()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get network route: %w", err)
|
|
}
|
|
|
|
line := string(output)
|
|
// Example output: "8.8.8.8 via 192.168.8.1 dev wlan0 src 192.168.8.152 uid 1000"
|
|
|
|
// Extract gateway (after "via")
|
|
gateway := ""
|
|
if idx := strings.Index(line, "via "); idx != -1 {
|
|
rest := line[idx+4:]
|
|
parts := strings.Fields(rest)
|
|
if len(parts) > 0 {
|
|
gateway = parts[0]
|
|
}
|
|
}
|
|
|
|
// Extract interface (after "dev")
|
|
iface := ""
|
|
if idx := strings.Index(line, "dev "); idx != -1 {
|
|
rest := line[idx+4:]
|
|
parts := strings.Fields(rest)
|
|
if len(parts) > 0 {
|
|
iface = parts[0]
|
|
}
|
|
}
|
|
|
|
// Extract source IP (after "src")
|
|
// This is the IP that would be used as the source for outbound traffic
|
|
ip := ""
|
|
if idx := strings.Index(line, "src "); idx != -1 {
|
|
rest := line[idx+4:]
|
|
parts := strings.Fields(rest)
|
|
if len(parts) > 0 {
|
|
ip = parts[0]
|
|
}
|
|
}
|
|
|
|
if ip == "" || iface == "" {
|
|
return nil, fmt.Errorf("could not detect network information from route output: %s", line)
|
|
}
|
|
|
|
return &NetworkInfo{
|
|
PrimaryIP: ip,
|
|
PrimaryInterface: iface,
|
|
Gateway: gateway,
|
|
}, nil
|
|
}
|
|
|
|
// ResolveDomain resolves a domain name to an IP address using the system DNS resolver
|
|
func ResolveDomain(domain string) (string, error) {
|
|
ips, err := net.LookupIP(domain)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to resolve domain: %w", err)
|
|
}
|
|
|
|
if len(ips) == 0 {
|
|
return "", fmt.Errorf("no IP addresses found for domain")
|
|
}
|
|
|
|
// Return the first IPv4 address
|
|
for _, ip := range ips {
|
|
if ipv4 := ip.To4(); ipv4 != nil {
|
|
return ipv4.String(), nil
|
|
}
|
|
}
|
|
|
|
// If no IPv4, return first IP
|
|
return ips[0].String(), nil
|
|
}
|
|
|
|
// GetWildCentralIP returns the IP address of the Wild Central server
|
|
// This is the primary IP address that the server uses for network communication
|
|
func GetWildCentralIP() (string, error) {
|
|
netInfo, err := DetectNetworkInfo()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to detect Wild Central IP: %w", err)
|
|
}
|
|
return netInfo.PrimaryIP, nil
|
|
}
|