Forráskód Böngészése

add addtional nameservers from host external services config

abhishek9686 5 hónapja
szülő
commit
98dbc5271c
7 módosított fájl, 216 hozzáadás és 56 törlés
  1. 14 0
      logic/dns.go
  2. 117 0
      logic/egress.go
  3. 7 0
      logic/hosts.go
  4. 1 0
      logic/peers.go
  5. 16 0
      logic/util.go
  6. 24 24
      models/api_host.go
  7. 37 32
      models/host.go

+ 14 - 0
logic/dns.go

@@ -326,3 +326,17 @@ func CreateDNS(entry models.DNSEntry) (models.DNSEntry, error) {
 	err = database.Insert(k, string(data), database.DNS_TABLE_NAME)
 	return entry, err
 }
+func GetAdditionalNameservers() (ns []string) {
+	hosts, err := GetAllHosts()
+	if err != nil {
+		return
+	}
+	for _, host := range hosts {
+		if ips, ok := host.EgressServices["DNS"]; ok {
+			for _, ip := range ips {
+				ns = append(ns, ip.NATIP.String())
+			}
+		}
+	}
+	return
+}

+ 117 - 0
logic/egress.go

@@ -3,6 +3,7 @@ package logic
 import (
 	"errors"
 	"fmt"
+	"math/big"
 	"net"
 	"slices"
 	"sort"
@@ -25,6 +26,7 @@ func AutoConfigureEgress(h *models.Host, node *models.Node) {
 		rangeWithMetric := models.EgressRangeMetric{
 			Network:           addr,
 			VirtualNATNetwork: iface.VirtualNATAddr.String(),
+			RouteMetric:       256,
 		}
 		if currRangeMetric, ok := currRangesWithMetric[addr]; ok {
 			lastMetricValue := currRangeMetric[len(currRangeMetric)-1]
@@ -41,6 +43,121 @@ func AutoConfigureEgress(h *models.Host, node *models.Node) {
 	})
 }
 
+func MapExternalServicesToHostNodes(h *models.Host) {
+	ranges := []string{}
+	rangesWithMetric := []models.EgressRangeMetric{}
+	for _, nodeID := range h.Nodes {
+		node, err := GetNodeByID(nodeID)
+		if err != nil {
+			continue
+		}
+		for i, egressServiceIPs := range h.EgressServices {
+			for j, egressIPNat := range egressServiceIPs {
+				currRangesWithMetric := GetEgressRangesWithMetric(models.NetworkID(node.Network))
+
+				addr := egressIPNat.EgressIP.String()
+				ranges = append(ranges, addr)
+				for _, iface := range h.Interfaces {
+					if !iface.Address.IP.IsPrivate() || iface.Name == h.DefaultInterface {
+						continue
+					}
+					ifaceAddr, err := NormalizeCIDRV1(iface.Address.String())
+					if err != nil {
+						continue
+					}
+					if !ifaceAddr.Contains(egressIPNat.EgressIP) {
+						continue
+					}
+					egressNATIP, err := netmapTranslate(egressIPNat.EgressIP, ifaceAddr.String(), iface.VirtualNATAddr.String())
+					if err != nil {
+						continue
+					}
+					egressIPNat.NATIP = egressNATIP
+					egressServiceIPs[j] = egressIPNat
+					rangeWithMetric := models.EgressRangeMetric{
+						Network:           addr,
+						VirtualNATNetwork: egressNATIP.String(),
+						RouteMetric:       256,
+					}
+					if currRangeMetric, ok := currRangesWithMetric[addr]; ok {
+						lastMetricValue := currRangeMetric[len(currRangeMetric)-1]
+						rangeWithMetric.RouteMetric = lastMetricValue.RouteMetric + 10
+					}
+					rangesWithMetric = append(rangesWithMetric, rangeWithMetric)
+				}
+				h.EgressServices[i] = egressServiceIPs
+
+			}
+
+		}
+		if !node.IsEgressGateway {
+			CreateEgressGateway(models.EgressGatewayRequest{
+				NodeID:           node.ID.String(),
+				NetID:            node.Network,
+				NatEnabled:       "yes",
+				Ranges:           ranges,
+				RangesWithMetric: rangesWithMetric,
+			})
+		} else {
+			node.EgressGatewayRequest.Ranges = append(node.EgressGatewayRequest.Ranges, ranges...)
+			node.EgressGatewayRequest.RangesWithMetric = append(node.EgressGatewayRequest.RangesWithMetric, rangesWithMetric...)
+			node.EgressGatewayRanges = append(node.EgressGatewayRanges, ranges...)
+			UpsertNode(&node)
+		}
+
+	}
+	UpsertHost(h)
+}
+
+// netmapTranslate translates an IP address from one subnet to another
+func netmapTranslate(ip net.IP, srcCIDR, dstCIDR string) (net.IP, error) {
+	_, srcNet, err := net.ParseCIDR(srcCIDR)
+	if err != nil {
+		return nil, fmt.Errorf("invalid source CIDR: %w", err)
+	}
+
+	_, dstNet, err := net.ParseCIDR(dstCIDR)
+	if err != nil {
+		return nil, fmt.Errorf("invalid destination CIDR: %w", err)
+	}
+
+	// Ensure IP belongs to the source subnet
+	if !srcNet.Contains(ip) {
+		return nil, fmt.Errorf("IP %s not in source CIDR %s", ip, srcCIDR)
+	}
+
+	// Convert IPs to big.Int
+	ipInt := ipToBigInt(ip)
+	srcBase := ipToBigInt(srcNet.IP)
+	dstBase := ipToBigInt(dstNet.IP)
+
+	// Compute offset and apply it to the destination base
+	offset := new(big.Int).Sub(ipInt, srcBase)
+	translatedIP := new(big.Int).Add(dstBase, offset)
+
+	return bigIntToIP(translatedIP, ip), nil
+}
+
+// ipToBigInt converts an IP address to a big integer
+func ipToBigInt(ip net.IP) *big.Int {
+	return new(big.Int).SetBytes(ip.To16()) // Always use IPv6 representation
+}
+
+// bigIntToIP correctly converts a big integer back to an IP address
+func bigIntToIP(bi *big.Int, originalIP net.IP) net.IP {
+	ipBytes := bi.Bytes()
+
+	// Ensure correct length (IPv4 = 4 bytes, IPv6 = 16 bytes)
+	if len(originalIP) == net.IPv4len && len(ipBytes) > 4 {
+		ipBytes = ipBytes[len(ipBytes)-4:] // Extract last 4 bytes for IPv4
+	} else if len(ipBytes) < len(originalIP) {
+		padding := make([]byte, len(originalIP)-len(ipBytes))
+		ipBytes = append(padding, ipBytes...)
+	}
+
+	return net.IP(ipBytes)
+}
+
 // isConflicting checks if a given CIDR conflicts with existing subnets
 func isConflicting(cidr *net.IPNet, existing []net.IPNet) bool {
 	for _, net := range existing {

+ 7 - 0
logic/hosts.go

@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"reflect"
 	"sort"
 	"sync"
 
@@ -294,6 +295,12 @@ func UpdateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool)
 		sendPeerUpdate = true
 		isEndpointChanged = true
 	}
+	if !reflect.DeepEqual(currHost.EgressServices, newHost.EgressServices) {
+		currHost.EgressServices = newHost.EgressServices
+		// update egress range on nodes
+		MapExternalServicesToHostNodes(currHost)
+		sendPeerUpdate = true
+	}
 
 	if isEndpointChanged {
 		for _, nodeID := range currHost.Nodes {

+ 1 - 0
logic/peers.go

@@ -159,6 +159,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 		HostNetworkInfo: models.HostInfoMap{},
 		ServerConfig:    servercfg.ServerInfo,
 	}
+	hostPeerUpdate.NameServers = append(hostPeerUpdate.NameServers, GetAdditionalNameservers()...)
 	defer func() {
 		if !hostPeerUpdate.FwUpdate.AllowAll {
 			aclRule := models.AclRule{

+ 16 - 0
logic/util.go

@@ -133,6 +133,22 @@ func NormalizeCIDR(address string) (string, error) {
 	return IPNet.String(), nil
 }
 
+// NormalizeCIDR - returns the first address of CIDR
+func NormalizeCIDRV1(address string) (net.IPNet, error) {
+	ip, IPNet, err := net.ParseCIDR(address)
+	if err != nil {
+		return net.IPNet{}, err
+	}
+	if ip.To4() == nil {
+		net6 := iplib.Net6FromStr(IPNet.String())
+		IPNet.IP = net6.FirstAddress()
+	} else {
+		net4 := iplib.Net4FromStr(IPNet.String())
+		IPNet.IP = net4.NetworkAddress()
+	}
+	return *IPNet, nil
+}
+
 // StringDifference - returns the elements in `a` that aren't in `b`.
 func StringDifference(a, b []string) []string {
 	mb := make(map[string]struct{}, len(b))

+ 24 - 24
models/api_host.go

@@ -8,30 +8,30 @@ import (
 
 // ApiHost - the host struct for API usage
 type ApiHost struct {
-	ID                  string              `json:"id"`
-	Verbosity           int                 `json:"verbosity"`
-	FirewallInUse       string              `json:"firewallinuse"`
-	Version             string              `json:"version"`
-	Name                string              `json:"name"`
-	OS                  string              `json:"os"`
-	Debug               bool                `json:"debug"`
-	IsStaticPort        bool                `json:"isstaticport"`
-	IsStatic            bool                `json:"isstatic"`
-	ListenPort          int                 `json:"listenport"`
-	WgPublicListenPort  int                 `json:"wg_public_listen_port" yaml:"wg_public_listen_port"`
-	MTU                 int                 `json:"mtu"                   yaml:"mtu"`
-	Interfaces          []ApiIface          `json:"interfaces"            yaml:"interfaces"`
-	DefaultInterface    string              `json:"defaultinterface"      yaml:"defautlinterface"`
-	EndpointIP          string              `json:"endpointip"            yaml:"endpointip"`
-	EndpointIPv6        string              `json:"endpointipv6"            yaml:"endpointipv6"`
-	PublicKey           string              `json:"publickey"`
-	MacAddress          string              `json:"macaddress"`
-	Nodes               []string            `json:"nodes"`
-	IsDefault           bool                `json:"isdefault"             yaml:"isdefault"`
-	NatType             string              `json:"nat_type"              yaml:"nat_type"`
-	PersistentKeepalive int                 `json:"persistentkeepalive"   yaml:"persistentkeepalive"`
-	AutoUpdate          bool                `json:"autoupdate"              yaml:"autoupdate"`
-	EgressServices      map[string][]net.IP `json:"egress_services"`
+	ID                  string                   `json:"id"`
+	Verbosity           int                      `json:"verbosity"`
+	FirewallInUse       string                   `json:"firewallinuse"`
+	Version             string                   `json:"version"`
+	Name                string                   `json:"name"`
+	OS                  string                   `json:"os"`
+	Debug               bool                     `json:"debug"`
+	IsStaticPort        bool                     `json:"isstaticport"`
+	IsStatic            bool                     `json:"isstatic"`
+	ListenPort          int                      `json:"listenport"`
+	WgPublicListenPort  int                      `json:"wg_public_listen_port" yaml:"wg_public_listen_port"`
+	MTU                 int                      `json:"mtu"                   yaml:"mtu"`
+	Interfaces          []ApiIface               `json:"interfaces"            yaml:"interfaces"`
+	DefaultInterface    string                   `json:"defaultinterface"      yaml:"defautlinterface"`
+	EndpointIP          string                   `json:"endpointip"            yaml:"endpointip"`
+	EndpointIPv6        string                   `json:"endpointipv6"            yaml:"endpointipv6"`
+	PublicKey           string                   `json:"publickey"`
+	MacAddress          string                   `json:"macaddress"`
+	Nodes               []string                 `json:"nodes"`
+	IsDefault           bool                     `json:"isdefault"             yaml:"isdefault"`
+	NatType             string                   `json:"nat_type"              yaml:"nat_type"`
+	PersistentKeepalive int                      `json:"persistentkeepalive"   yaml:"persistentkeepalive"`
+	AutoUpdate          bool                     `json:"autoupdate"              yaml:"autoupdate"`
+	EgressServices      map[string][]EgressIPNat `json:"egress_services"`
 }
 
 // ApiIface - the interface struct for API usage

+ 37 - 32
models/host.go

@@ -41,38 +41,43 @@ const (
 
 // Host - represents a host on the network
 type Host struct {
-	ID                  uuid.UUID           `json:"id"                      yaml:"id"`
-	Verbosity           int                 `json:"verbosity"               yaml:"verbosity"`
-	FirewallInUse       string              `json:"firewallinuse"           yaml:"firewallinuse"`
-	Version             string              `json:"version"                 yaml:"version"`
-	IPForwarding        bool                `json:"ipforwarding"            yaml:"ipforwarding"`
-	DaemonInstalled     bool                `json:"daemoninstalled"         yaml:"daemoninstalled"`
-	AutoUpdate          bool                `json:"autoupdate"              yaml:"autoupdate"`
-	HostPass            string              `json:"hostpass"                yaml:"hostpass"`
-	Name                string              `json:"name"                    yaml:"name"`
-	OS                  string              `json:"os"                      yaml:"os"`
-	Interface           string              `json:"interface"               yaml:"interface"`
-	Debug               bool                `json:"debug"                   yaml:"debug"`
-	ListenPort          int                 `json:"listenport"              yaml:"listenport"`
-	WgPublicListenPort  int                 `json:"wg_public_listen_port"   yaml:"wg_public_listen_port"`
-	MTU                 int                 `json:"mtu"                     yaml:"mtu"`
-	PublicKey           wgtypes.Key         `json:"publickey"               yaml:"publickey"`
-	MacAddress          net.HardwareAddr    `json:"macaddress"              yaml:"macaddress"`
-	TrafficKeyPublic    []byte              `json:"traffickeypublic"        yaml:"traffickeypublic"`
-	Nodes               []string            `json:"nodes"                   yaml:"nodes"`
-	Interfaces          []Iface             `json:"interfaces"              yaml:"interfaces"`
-	DefaultInterface    string              `json:"defaultinterface"        yaml:"defaultinterface"`
-	EndpointIP          net.IP              `json:"endpointip"              yaml:"endpointip"`
-	EndpointIPv6        net.IP              `json:"endpointipv6"            yaml:"endpointipv6"`
-	IsDocker            bool                `json:"isdocker"                yaml:"isdocker"`
-	IsK8S               bool                `json:"isk8s"                   yaml:"isk8s"`
-	IsStaticPort        bool                `json:"isstaticport"            yaml:"isstaticport"`
-	IsStatic            bool                `json:"isstatic"        yaml:"isstatic"`
-	IsDefault           bool                `json:"isdefault"               yaml:"isdefault"`
-	NatType             string              `json:"nat_type,omitempty"      yaml:"nat_type,omitempty"`
-	TurnEndpoint        *netip.AddrPort     `json:"turn_endpoint,omitempty" yaml:"turn_endpoint,omitempty"`
-	PersistentKeepalive time.Duration       `json:"persistentkeepalive" swaggertype:"primitive,integer" format:"int64" yaml:"persistentkeepalive"`
-	EgressServices      map[string][]net.IP `json:"egress_services"`
+	ID                  uuid.UUID                `json:"id"                      yaml:"id"`
+	Verbosity           int                      `json:"verbosity"               yaml:"verbosity"`
+	FirewallInUse       string                   `json:"firewallinuse"           yaml:"firewallinuse"`
+	Version             string                   `json:"version"                 yaml:"version"`
+	IPForwarding        bool                     `json:"ipforwarding"            yaml:"ipforwarding"`
+	DaemonInstalled     bool                     `json:"daemoninstalled"         yaml:"daemoninstalled"`
+	AutoUpdate          bool                     `json:"autoupdate"              yaml:"autoupdate"`
+	HostPass            string                   `json:"hostpass"                yaml:"hostpass"`
+	Name                string                   `json:"name"                    yaml:"name"`
+	OS                  string                   `json:"os"                      yaml:"os"`
+	Interface           string                   `json:"interface"               yaml:"interface"`
+	Debug               bool                     `json:"debug"                   yaml:"debug"`
+	ListenPort          int                      `json:"listenport"              yaml:"listenport"`
+	WgPublicListenPort  int                      `json:"wg_public_listen_port"   yaml:"wg_public_listen_port"`
+	MTU                 int                      `json:"mtu"                     yaml:"mtu"`
+	PublicKey           wgtypes.Key              `json:"publickey"               yaml:"publickey"`
+	MacAddress          net.HardwareAddr         `json:"macaddress"              yaml:"macaddress"`
+	TrafficKeyPublic    []byte                   `json:"traffickeypublic"        yaml:"traffickeypublic"`
+	Nodes               []string                 `json:"nodes"                   yaml:"nodes"`
+	Interfaces          []Iface                  `json:"interfaces"              yaml:"interfaces"`
+	DefaultInterface    string                   `json:"defaultinterface"        yaml:"defaultinterface"`
+	EndpointIP          net.IP                   `json:"endpointip"              yaml:"endpointip"`
+	EndpointIPv6        net.IP                   `json:"endpointipv6"            yaml:"endpointipv6"`
+	IsDocker            bool                     `json:"isdocker"                yaml:"isdocker"`
+	IsK8S               bool                     `json:"isk8s"                   yaml:"isk8s"`
+	IsStaticPort        bool                     `json:"isstaticport"            yaml:"isstaticport"`
+	IsStatic            bool                     `json:"isstatic"        yaml:"isstatic"`
+	IsDefault           bool                     `json:"isdefault"               yaml:"isdefault"`
+	NatType             string                   `json:"nat_type,omitempty"      yaml:"nat_type,omitempty"`
+	TurnEndpoint        *netip.AddrPort          `json:"turn_endpoint,omitempty" yaml:"turn_endpoint,omitempty"`
+	PersistentKeepalive time.Duration            `json:"persistentkeepalive" swaggertype:"primitive,integer" format:"int64" yaml:"persistentkeepalive"`
+	EgressServices      map[string][]EgressIPNat `json:"egress_services"`
+}
+
+type EgressIPNat struct {
+	EgressIP net.IP
+	NATIP    net.IP
 }
 
 // FormatBool converts a boolean to a [yes|no] string