Browse Source

Merge pull request #2976 from gravitl/release-v0.24.2

v0.24.2
Abhishek K 1 year ago
parent
commit
22a00bda9b

+ 1 - 0
.github/ISSUE_TEMPLATE/bug-report.yml

@@ -31,6 +31,7 @@ body:
       label: Version
       label: Version
       description: What version are you running?
       description: What version are you running?
       options:
       options:
+        - v0.24.2
         - v0.24.1
         - v0.24.1
         - v0.24.0
         - v0.24.0
         - v0.23.0
         - v0.23.0

+ 15 - 0
.github/workflows/sendtokitemaker.yml

@@ -0,0 +1,15 @@
+name: Send to Kitemaker
+
+on:
+    issues:
+        types: opened
+
+
+jobs:
+    send-ticket:
+        uses: gravitl/devops/.github/workflows/makekitemakerticket.yml@master
+        with:
+            title: ${{ github.event.issue.title }}
+            body: ${{ github.event.issue.body }}
+            repo_name: ${{ github.event.repository.name }}
+        secrets: inherit

+ 1 - 1
Dockerfile

@@ -6,7 +6,7 @@ COPY . .
 
 
 RUN GOOS=linux CGO_ENABLED=1 go build -ldflags="-s -w " -tags ${tags} .
 RUN GOOS=linux CGO_ENABLED=1 go build -ldflags="-s -w " -tags ${tags} .
 # RUN go build -tags=ee . -o netmaker main.go
 # RUN go build -tags=ee . -o netmaker main.go
-FROM alpine:3.19.1
+FROM alpine:3.20.0
 
 
 # add a c lib
 # add a c lib
 # set the working directory
 # set the working directory

+ 1 - 1
Dockerfile-quick

@@ -1,5 +1,5 @@
 #first stage - builder
 #first stage - builder
-FROM alpine:3.19.1
+FROM alpine:3.20.0
 ARG version 
 ARG version 
 WORKDIR /app
 WORKDIR /app
 COPY ./netmaker /root/netmaker
 COPY ./netmaker /root/netmaker

+ 1 - 1
README.md

@@ -16,7 +16,7 @@
 
 
 <p align="center">
 <p align="center">
   <a href="https://github.com/gravitl/netmaker/releases">
   <a href="https://github.com/gravitl/netmaker/releases">
-    <img src="https://img.shields.io/badge/Version-0.24.1-informational?style=flat-square" />
+    <img src="https://img.shields.io/badge/Version-0.24.2-informational?style=flat-square" />
   </a>
   </a>
   <a href="https://hub.docker.com/r/gravitl/netmaker/tags">
   <a href="https://hub.docker.com/r/gravitl/netmaker/tags">
     <img src="https://img.shields.io/docker/pulls/gravitl/netmaker?label=downloads" />
     <img src="https://img.shields.io/docker/pulls/gravitl/netmaker?label=downloads" />

+ 4 - 1
cli/cmd/host/update.go

@@ -18,6 +18,7 @@ var (
 	name            string
 	name            string
 	listenPort      int
 	listenPort      int
 	mtu             int
 	mtu             int
+	isStaticPort    bool
 	isStatic        bool
 	isStatic        bool
 	isDefault       bool
 	isDefault       bool
 	keepAlive       int
 	keepAlive       int
@@ -45,6 +46,7 @@ var hostUpdateCmd = &cobra.Command{
 			apiHost.Name = name
 			apiHost.Name = name
 			apiHost.ListenPort = listenPort
 			apiHost.ListenPort = listenPort
 			apiHost.MTU = mtu
 			apiHost.MTU = mtu
+			apiHost.IsStaticPort = isStaticPort
 			apiHost.IsStatic = isStatic
 			apiHost.IsStatic = isStatic
 			apiHost.IsDefault = isDefault
 			apiHost.IsDefault = isDefault
 			apiHost.PersistentKeepalive = keepAlive
 			apiHost.PersistentKeepalive = keepAlive
@@ -61,7 +63,8 @@ func init() {
 	hostUpdateCmd.Flags().IntVar(&listenPort, "listen_port", 0, "Listen port of the host")
 	hostUpdateCmd.Flags().IntVar(&listenPort, "listen_port", 0, "Listen port of the host")
 	hostUpdateCmd.Flags().IntVar(&mtu, "mtu", 0, "Host MTU size")
 	hostUpdateCmd.Flags().IntVar(&mtu, "mtu", 0, "Host MTU size")
 	hostUpdateCmd.Flags().IntVar(&keepAlive, "keep_alive", 0, "Interval (seconds) in which packets are sent to keep connections open with peers")
 	hostUpdateCmd.Flags().IntVar(&keepAlive, "keep_alive", 0, "Interval (seconds) in which packets are sent to keep connections open with peers")
-	hostUpdateCmd.Flags().BoolVar(&isStatic, "static", false, "Make Host Static ?")
+	hostUpdateCmd.Flags().BoolVar(&isStaticPort, "static_port", false, "Make Host Static Port?")
+	hostUpdateCmd.Flags().BoolVar(&isStatic, "static_endpoint", false, "Make Host Static Endpoint?")
 	hostUpdateCmd.Flags().BoolVar(&isDefault, "default", false, "Make Host Default ?")
 	hostUpdateCmd.Flags().BoolVar(&isDefault, "default", false, "Make Host Default ?")
 	rootCmd.AddCommand(hostUpdateCmd)
 	rootCmd.AddCommand(hostUpdateCmd)
 }
 }

+ 1 - 1
compose/docker-compose.netclient.yml

@@ -3,7 +3,7 @@ version: "3.4"
 services:
 services:
   netclient:
   netclient:
     container_name: netclient
     container_name: netclient
-    image: 'gravitl/netclient:v0.24.1'
+    image: 'gravitl/netclient:v0.24.2'
     hostname: netmaker-1
     hostname: netmaker-1
     network_mode: host
     network_mode: host
     restart: on-failure
     restart: on-failure

+ 1 - 1
controllers/docs.go

@@ -10,7 +10,7 @@
 //
 //
 //	Schemes: https
 //	Schemes: https
 //	BasePath: /
 //	BasePath: /
-//	Version: 0.24.1
+//	Version: 0.24.2
 //	Host: api.demo.netmaker.io
 //	Host: api.demo.netmaker.io
 //
 //
 //	Consumes:
 //	Consumes:

+ 5 - 5
controllers/hosts.go

@@ -304,17 +304,17 @@ func deleteHost(w http.ResponseWriter, r *http.Request) {
 			slog.Error("failed to remove host credentials from EMQX", "id", currHost.ID, "error", err)
 			slog.Error("failed to remove host credentials from EMQX", "id", currHost.ID, "error", err)
 		}
 		}
 	}
 	}
-	if err = logic.RemoveHost(currHost, forceDelete); err != nil {
-		logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
 	if err = mq.HostUpdate(&models.HostUpdate{
 	if err = mq.HostUpdate(&models.HostUpdate{
 		Action: models.DeleteHost,
 		Action: models.DeleteHost,
 		Host:   *currHost,
 		Host:   *currHost,
 	}); err != nil {
 	}); err != nil {
 		logger.Log(0, r.Header.Get("user"), "failed to send delete host update: ", currHost.ID.String(), err.Error())
 		logger.Log(0, r.Header.Get("user"), "failed to send delete host update: ", currHost.ID.String(), err.Error())
 	}
 	}
+	if err = logic.RemoveHost(currHost, forceDelete); err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
 
 
 	apiHostData := currHost.ConvertNMHostToAPI()
 	apiHostData := currHost.ConvertNMHostToAPI()
 	logger.Log(2, r.Header.Get("user"), "removed host", currHost.Name)
 	logger.Log(2, r.Header.Get("user"), "removed host", currHost.Name)

+ 1 - 0
controllers/migrate.go

@@ -150,6 +150,7 @@ func convertLegacyHostNode(legacy models.LegacyNode) (models.Host, models.Node)
 	host.EndpointIP = net.ParseIP(legacy.Endpoint)
 	host.EndpointIP = net.ParseIP(legacy.Endpoint)
 	host.IsDocker = models.ParseBool(legacy.IsDocker)
 	host.IsDocker = models.ParseBool(legacy.IsDocker)
 	host.IsK8S = models.ParseBool(legacy.IsK8S)
 	host.IsK8S = models.ParseBool(legacy.IsK8S)
+	host.IsStaticPort = models.ParseBool(legacy.IsStatic)
 	host.IsStatic = models.ParseBool(legacy.IsStatic)
 	host.IsStatic = models.ParseBool(legacy.IsStatic)
 	host.PersistentKeepalive = time.Duration(legacy.PersistentKeepalive) * time.Second
 	host.PersistentKeepalive = time.Duration(legacy.PersistentKeepalive) * time.Second
 	if host.PersistentKeepalive == 0 {
 	if host.PersistentKeepalive == 0 {

+ 4 - 0
controllers/network.go

@@ -490,6 +490,10 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
 			// make host remote access gateway
 			// make host remote access gateway
 			logic.CreateIngressGateway(network.NetID, newNode.ID.String(), models.IngressRequest{})
 			logic.CreateIngressGateway(network.NetID, newNode.ID.String(), models.IngressRequest{})
 		}
 		}
+		// send peer updates
+		if err = mq.PublishPeerUpdate(false); err != nil {
+			logger.Log(1, "failed to publish peer update for default hosts after network is added")
+		}
 	}()
 	}()
 
 
 	logger.Log(1, r.Header.Get("user"), "created network", network.NetID)
 	logger.Log(1, r.Header.Get("user"), "created network", network.NetID)

+ 1 - 1
go.mod

@@ -57,7 +57,7 @@ require (
 	github.com/felixge/httpsnoop v1.0.3 // indirect
 	github.com/felixge/httpsnoop v1.0.3 // indirect
 	github.com/go-playground/locales v0.14.1 // indirect
 	github.com/go-playground/locales v0.14.1 // indirect
 	github.com/go-playground/universal-translator v0.18.1 // indirect
 	github.com/go-playground/universal-translator v0.18.1 // indirect
-	github.com/hashicorp/go-version v1.6.0
+	github.com/hashicorp/go-version v1.7.0
 	github.com/leodido/go-urn v1.4.0 // indirect
 	github.com/leodido/go-urn v1.4.0 // indirect
 	github.com/mattn/go-runewidth v0.0.13 // indirect
 	github.com/mattn/go-runewidth v0.0.13 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect

+ 2 - 2
go.sum

@@ -41,8 +41,8 @@ github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/
 github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
 github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
 github.com/guumaster/tablewriter v0.0.10 h1:A0HD94yMdt4usgxBjoEceNeE0XMJ027euoHAzsPqBQs=
 github.com/guumaster/tablewriter v0.0.10 h1:A0HD94yMdt4usgxBjoEceNeE0XMJ027euoHAzsPqBQs=
 github.com/guumaster/tablewriter v0.0.10/go.mod h1:p4FRFhyfo0UD9ZLmMRbbJooTUsxo6b80qZTERVDWrH8=
 github.com/guumaster/tablewriter v0.0.10/go.mod h1:p4FRFhyfo0UD9ZLmMRbbJooTUsxo6b80qZTERVDWrH8=
-github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
-github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
+github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
 github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=

+ 1 - 1
k8s/client/netclient-daemonset.yaml

@@ -16,7 +16,7 @@ spec:
       hostNetwork: true
       hostNetwork: true
       containers:
       containers:
       - name: netclient
       - name: netclient
-        image: gravitl/netclient:v0.24.1
+        image: gravitl/netclient:v0.24.2
         env:
         env:
         - name: TOKEN
         - name: TOKEN
           value: "TOKEN_VALUE"
           value: "TOKEN_VALUE"

+ 1 - 1
k8s/client/netclient.yaml

@@ -28,7 +28,7 @@ spec:
       #           - "<node label value>"
       #           - "<node label value>"
       containers:
       containers:
       - name: netclient
       - name: netclient
-        image: gravitl/netclient:v0.24.1
+        image: gravitl/netclient:v0.24.2
         env:
         env:
         - name: TOKEN
         - name: TOKEN
           value: "TOKEN_VALUE"
           value: "TOKEN_VALUE"

+ 1 - 1
k8s/server/netmaker-ui.yaml

@@ -15,7 +15,7 @@ spec:
     spec:
     spec:
       containers:
       containers:
       - name: netmaker-ui
       - name: netmaker-ui
-        image: gravitl/netmaker-ui:v0.24.1
+        image: gravitl/netmaker-ui:v0.24.2
         ports:
         ports:
         - containerPort: 443
         - containerPort: 443
         env:
         env:

+ 12 - 17
logic/extpeers.go

@@ -376,7 +376,7 @@ func ToggleExtClientConnectivity(client *models.ExtClient, enable bool) (models.
 	return newClient, nil
 	return newClient, nil
 }
 }
 
 
-func getExtPeers(node, peer *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, []models.EgressNetworkRoutes, error) {
+func GetExtPeers(node, peer *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, []models.EgressNetworkRoutes, error) {
 	var peers []wgtypes.PeerConfig
 	var peers []wgtypes.PeerConfig
 	var idsAndAddr []models.IDandAddr
 	var idsAndAddr []models.IDandAddr
 	var egressRoutes []models.EgressNetworkRoutes
 	var egressRoutes []models.EgressNetworkRoutes
@@ -431,7 +431,7 @@ func getExtPeers(node, peer *models.Node) ([]wgtypes.PeerConfig, []models.IDandA
 				allowedips = append(allowedips, *cidr)
 				allowedips = append(allowedips, *cidr)
 			}
 			}
 		}
 		}
-		egressRoutes = append(egressRoutes, getExtPeerEgressRoute(extPeer)...)
+		egressRoutes = append(egressRoutes, getExtPeerEgressRoute(*node, extPeer)...)
 		primaryAddr := extPeer.Address
 		primaryAddr := extPeer.Address
 		if primaryAddr == "" {
 		if primaryAddr == "" {
 			primaryAddr = extPeer.Address6
 			primaryAddr = extPeer.Address6
@@ -453,23 +453,18 @@ func getExtPeers(node, peer *models.Node) ([]wgtypes.PeerConfig, []models.IDandA
 
 
 }
 }
 
 
-func getExtPeerEgressRoute(extPeer models.ExtClient) (egressRoutes []models.EgressNetworkRoutes) {
-	if extPeer.Address != "" {
-		egressRoutes = append(egressRoutes, models.EgressNetworkRoutes{
-			NodeAddr:     extPeer.AddressIPNet4(),
-			EgressRanges: extPeer.ExtraAllowedIPs,
-		})
-	}
-	if extPeer.Address6 != "" {
-		egressRoutes = append(egressRoutes, models.EgressNetworkRoutes{
-			NodeAddr:     extPeer.AddressIPNet6(),
-			EgressRanges: extPeer.ExtraAllowedIPs,
-		})
-	}
+func getExtPeerEgressRoute(node models.Node, extPeer models.ExtClient) (egressRoutes []models.EgressNetworkRoutes) {
+	egressRoutes = append(egressRoutes, models.EgressNetworkRoutes{
+		EgressGwAddr:  extPeer.AddressIPNet4(),
+		EgressGwAddr6: extPeer.AddressIPNet6(),
+		NodeAddr:      node.Address,
+		NodeAddr6:     node.Address6,
+		EgressRanges:  extPeer.ExtraAllowedIPs,
+	})
 	return
 	return
 }
 }
 
 
-func getExtpeersExtraRoutes(network string) (egressRoutes []models.EgressNetworkRoutes) {
+func getExtpeersExtraRoutes(node models.Node, network string) (egressRoutes []models.EgressNetworkRoutes) {
 	extPeers, err := GetNetworkExtClients(network)
 	extPeers, err := GetNetworkExtClients(network)
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -478,7 +473,7 @@ func getExtpeersExtraRoutes(network string) (egressRoutes []models.EgressNetwork
 		if len(extPeer.ExtraAllowedIPs) == 0 {
 		if len(extPeer.ExtraAllowedIPs) == 0 {
 			continue
 			continue
 		}
 		}
-		egressRoutes = append(egressRoutes, getExtPeerEgressRoute(extPeer)...)
+		egressRoutes = append(egressRoutes, getExtPeerEgressRoute(node, extPeer)...)
 	}
 	}
 	return
 	return
 }
 }

+ 6 - 12
logic/hosts.go

@@ -266,6 +266,7 @@ func UpdateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool)
 	currHost.Debug = newHost.Debug
 	currHost.Debug = newHost.Debug
 	currHost.Verbosity = newHost.Verbosity
 	currHost.Verbosity = newHost.Verbosity
 	currHost.Version = newHost.Version
 	currHost.Version = newHost.Version
+	currHost.IsStaticPort = newHost.IsStaticPort
 	currHost.IsStatic = newHost.IsStatic
 	currHost.IsStatic = newHost.IsStatic
 	currHost.MTU = newHost.MTU
 	currHost.MTU = newHost.MTU
 	currHost.Name = newHost.Name
 	currHost.Name = newHost.Name
@@ -396,20 +397,13 @@ func DissasociateNodeFromHost(n *models.Node, h *models.Host) error {
 	if len(h.Nodes) == 0 {
 	if len(h.Nodes) == 0 {
 		return fmt.Errorf("no nodes present in given host")
 		return fmt.Errorf("no nodes present in given host")
 	}
 	}
-	index := -1
+	nList := []string{}
 	for i := range h.Nodes {
 	for i := range h.Nodes {
-		if h.Nodes[i] == n.ID.String() {
-			index = i
-			break
+		if h.Nodes[i] != n.ID.String() {
+			nList = append(nList, h.Nodes[i])
 		}
 		}
 	}
 	}
-	if index < 0 {
-		if len(h.Nodes) == 0 {
-			return fmt.Errorf("node %s, not found in host, %s", n.ID.String(), h.ID.String())
-		}
-	} else {
-		h.Nodes = RemoveStringSlice(h.Nodes, index)
-	}
+	h.Nodes = nList
 	go func() {
 	go func() {
 		if servercfg.IsPro {
 		if servercfg.IsPro {
 			if clients, err := GetNetworkExtClients(n.Network); err != nil {
 			if clients, err := GetNetworkExtClients(n.Network); err != nil {
@@ -434,7 +428,7 @@ func DisassociateAllNodesFromHost(hostID string) error {
 	for _, nodeID := range host.Nodes {
 	for _, nodeID := range host.Nodes {
 		node, err := GetNodeByID(nodeID)
 		node, err := GetNodeByID(nodeID)
 		if err != nil {
 		if err != nil {
-			logger.Log(0, "failed to get host node", err.Error())
+			logger.Log(0, "failed to get host node, node id:", nodeID, err.Error())
 			continue
 			continue
 		}
 		}
 		if err := DeleteNode(&node, true); err != nil {
 		if err := DeleteNode(&node, true); err != nil {

+ 15 - 10
logic/peers.go

@@ -91,6 +91,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 		if err != nil {
 		if err != nil {
 			continue
 			continue
 		}
 		}
+
 		if !node.Connected || node.PendingDelete || node.Action == models.NODE_DELETE {
 		if !node.Connected || node.PendingDelete || node.Action == models.NODE_DELETE {
 			continue
 			continue
 		}
 		}
@@ -181,7 +182,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				})
 				})
 			}
 			}
 			if peer.IsIngressGateway {
 			if peer.IsIngressGateway {
-				hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, getExtpeersExtraRoutes(peer.Network)...)
+				hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, getExtpeersExtraRoutes(node, peer.Network)...)
 			}
 			}
 			_, isFailOverPeer := node.FailOverPeers[peer.ID.String()]
 			_, isFailOverPeer := node.FailOverPeers[peer.ID.String()]
 			if servercfg.IsPro {
 			if servercfg.IsPro {
@@ -249,9 +250,10 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig)
 				hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig)
 				peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1
 				peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1
 				hostPeerUpdate.HostNetworkInfo[peerHost.PublicKey.String()] = models.HostNetworkInfo{
 				hostPeerUpdate.HostNetworkInfo[peerHost.PublicKey.String()] = models.HostNetworkInfo{
-					Interfaces: peerHost.Interfaces,
-					ListenPort: peerHost.ListenPort,
-					IsStatic:   peerHost.IsStatic,
+					Interfaces:   peerHost.Interfaces,
+					ListenPort:   peerHost.ListenPort,
+					IsStaticPort: peerHost.IsStaticPort,
+					IsStatic:     peerHost.IsStatic,
 				}
 				}
 				nodePeer = peerConfig
 				nodePeer = peerConfig
 			} else {
 			} else {
@@ -259,10 +261,12 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				peerAllowedIPs = append(peerAllowedIPs, peerConfig.AllowedIPs...)
 				peerAllowedIPs = append(peerAllowedIPs, peerConfig.AllowedIPs...)
 				hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].AllowedIPs = peerAllowedIPs
 				hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].AllowedIPs = peerAllowedIPs
 				hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].Remove = false
 				hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].Remove = false
+				hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].Endpoint = peerConfig.Endpoint
 				hostPeerUpdate.HostNetworkInfo[peerHost.PublicKey.String()] = models.HostNetworkInfo{
 				hostPeerUpdate.HostNetworkInfo[peerHost.PublicKey.String()] = models.HostNetworkInfo{
-					Interfaces: peerHost.Interfaces,
-					ListenPort: peerHost.ListenPort,
-					IsStatic:   peerHost.IsStatic,
+					Interfaces:   peerHost.Interfaces,
+					ListenPort:   peerHost.ListenPort,
+					IsStaticPort: peerHost.IsStaticPort,
+					IsStatic:     peerHost.IsStatic,
 				}
 				}
 				nodePeer = hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]]
 				nodePeer = hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]]
 			}
 			}
@@ -283,7 +287,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 		var extPeerIDAndAddrs []models.IDandAddr
 		var extPeerIDAndAddrs []models.IDandAddr
 		var egressRoutes []models.EgressNetworkRoutes
 		var egressRoutes []models.EgressNetworkRoutes
 		if node.IsIngressGateway {
 		if node.IsIngressGateway {
-			extPeers, extPeerIDAndAddrs, egressRoutes, err = getExtPeers(&node, &node)
+			extPeers, extPeerIDAndAddrs, egressRoutes, err = GetExtPeers(&node, &node)
 			if err == nil {
 			if err == nil {
 				hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, egressRoutes...)
 				hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, egressRoutes...)
 				hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, extPeers...)
 				hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, extPeers...)
@@ -342,6 +346,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				},
 				},
 			}
 			}
 		}
 		}
+
 	}
 	}
 	// == post peer calculations ==
 	// == post peer calculations ==
 	// indicate removal if no allowed IPs were calculated
 	// indicate removal if no allowed IPs were calculated
@@ -392,7 +397,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 // GetPeerListenPort - given a host, retrieve it's appropriate listening port
 // GetPeerListenPort - given a host, retrieve it's appropriate listening port
 func GetPeerListenPort(host *models.Host) int {
 func GetPeerListenPort(host *models.Host) int {
 	peerPort := host.ListenPort
 	peerPort := host.ListenPort
-	if host.WgPublicListenPort != 0 {
+	if !host.IsStaticPort && host.WgPublicListenPort != 0 {
 		peerPort = host.WgPublicListenPort
 		peerPort = host.WgPublicListenPort
 	}
 	}
 	return peerPort
 	return peerPort
@@ -415,7 +420,7 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet
 
 
 	// handle ingress gateway peers
 	// handle ingress gateway peers
 	if peer.IsIngressGateway {
 	if peer.IsIngressGateway {
-		extPeers, _, _, err := getExtPeers(peer, node)
+		extPeers, _, _, err := GetExtPeers(peer, node)
 		if err != nil {
 		if err != nil {
 			logger.Log(2, "could not retrieve ext peers for ", peer.ID.String(), err.Error())
 			logger.Log(2, "could not retrieve ext peers for ", peer.ID.String(), err.Error())
 		}
 		}

+ 1 - 1
main.go

@@ -27,7 +27,7 @@ import (
 	"golang.org/x/exp/slog"
 	"golang.org/x/exp/slog"
 )
 )
 
 
-var version = "v0.24.1"
+var version = "v0.24.2"
 
 
 // Start DB Connection and start API Request Handler
 // Start DB Connection and start API Request Handler
 func main() {
 func main() {

+ 3 - 0
models/api_host.go

@@ -15,6 +15,7 @@ type ApiHost struct {
 	Name                string     `json:"name"`
 	Name                string     `json:"name"`
 	OS                  string     `json:"os"`
 	OS                  string     `json:"os"`
 	Debug               bool       `json:"debug"`
 	Debug               bool       `json:"debug"`
+	IsStaticPort        bool       `json:"isstaticport"`
 	IsStatic            bool       `json:"isstatic"`
 	IsStatic            bool       `json:"isstatic"`
 	ListenPort          int        `json:"listenport"`
 	ListenPort          int        `json:"listenport"`
 	WgPublicListenPort  int        `json:"wg_public_listen_port" yaml:"wg_public_listen_port"`
 	WgPublicListenPort  int        `json:"wg_public_listen_port" yaml:"wg_public_listen_port"`
@@ -61,6 +62,7 @@ func (h *Host) ConvertNMHostToAPI() *ApiHost {
 		}
 		}
 	}
 	}
 	a.DefaultInterface = h.DefaultInterface
 	a.DefaultInterface = h.DefaultInterface
+	a.IsStaticPort = h.IsStaticPort
 	a.IsStatic = h.IsStatic
 	a.IsStatic = h.IsStatic
 	a.ListenPort = h.ListenPort
 	a.ListenPort = h.ListenPort
 	a.MTU = h.MTU
 	a.MTU = h.MTU
@@ -104,6 +106,7 @@ func (a *ApiHost) ConvertAPIHostToNMHost(currentHost *Host) *Host {
 	h.DefaultInterface = currentHost.DefaultInterface
 	h.DefaultInterface = currentHost.DefaultInterface
 	h.IsDocker = currentHost.IsDocker
 	h.IsDocker = currentHost.IsDocker
 	h.IsK8S = currentHost.IsK8S
 	h.IsK8S = currentHost.IsK8S
+	h.IsStaticPort = a.IsStaticPort
 	h.IsStatic = a.IsStatic
 	h.IsStatic = a.IsStatic
 	h.ListenPort = a.ListenPort
 	h.ListenPort = a.ListenPort
 	h.MTU = a.MTU
 	h.MTU = a.MTU

+ 2 - 1
models/host.go

@@ -66,7 +66,8 @@ type Host struct {
 	EndpointIPv6        net.IP           `json:"endpointipv6"            yaml:"endpointipv6"`
 	EndpointIPv6        net.IP           `json:"endpointipv6"            yaml:"endpointipv6"`
 	IsDocker            bool             `json:"isdocker"                yaml:"isdocker"`
 	IsDocker            bool             `json:"isdocker"                yaml:"isdocker"`
 	IsK8S               bool             `json:"isk8s"                   yaml:"isk8s"`
 	IsK8S               bool             `json:"isk8s"                   yaml:"isk8s"`
-	IsStatic            bool             `json:"isstatic"                yaml:"isstatic"`
+	IsStaticPort        bool             `json:"isstaticport"            yaml:"isstaticport"`
+	IsStatic            bool             `json:"isstatic"        yaml:"isstatic"`
 	IsDefault           bool             `json:"isdefault"               yaml:"isdefault"`
 	IsDefault           bool             `json:"isdefault"               yaml:"isdefault"`
 	NatType             string           `json:"nat_type,omitempty"      yaml:"nat_type,omitempty"`
 	NatType             string           `json:"nat_type,omitempty"      yaml:"nat_type,omitempty"`
 	TurnEndpoint        *netip.AddrPort  `json:"turn_endpoint,omitempty" yaml:"turn_endpoint,omitempty"`
 	TurnEndpoint        *netip.AddrPort  `json:"turn_endpoint,omitempty" yaml:"turn_endpoint,omitempty"`

+ 4 - 3
models/metrics.go

@@ -42,9 +42,10 @@ type HostInfoMap map[string]HostNetworkInfo
 
 
 // HostNetworkInfo - holds info related to host networking (used for client side peer calculations)
 // HostNetworkInfo - holds info related to host networking (used for client side peer calculations)
 type HostNetworkInfo struct {
 type HostNetworkInfo struct {
-	Interfaces []Iface `json:"interfaces" yaml:"interfaces"`
-	ListenPort int     `json:"listen_port" yaml:"listen_port"`
-	IsStatic   bool    `json:"is_static"`
+	Interfaces   []Iface `json:"interfaces" yaml:"interfaces"`
+	ListenPort   int     `json:"listen_port" yaml:"listen_port"`
+	IsStaticPort bool    `json:"is_static_port"`
+	IsStatic     bool    `json:"is_static"`
 }
 }
 
 
 // PeerMap - peer map for ids and addresses in metrics
 // PeerMap - peer map for ids and addresses in metrics

+ 1 - 0
models/structs.go

@@ -75,6 +75,7 @@ type UserRemoteGws struct {
 	GwListenPort      int       `json:"gw_listen_port"`
 	GwListenPort      int       `json:"gw_listen_port"`
 	Metadata          string    `json:"metadata"`
 	Metadata          string    `json:"metadata"`
 	AllowedEndpoints  []string  `json:"allowed_endpoints"`
 	AllowedEndpoints  []string  `json:"allowed_endpoints"`
+	NetworkAddresses  []string  `json:"network_addresses"`
 }
 }
 
 
 // UserRemoteGwsReq - struct to hold user remote acccess gws req
 // UserRemoteGwsReq - struct to hold user remote acccess gws req

+ 1 - 14
mq/handlers.go

@@ -201,20 +201,7 @@ func signalPeer(signal models.Signal) {
 		slog.Error("failed to signal, peer host not found", "error", err)
 		slog.Error("failed to signal, peer host not found", "error", err)
 		return
 		return
 	}
 	}
-	peerNode, err := logic.GetNodeByID(signal.ToNodeID)
-	if err != nil {
-		slog.Error("failed to signal, node not found", "error", err)
-		return
-	}
-	node, err := logic.GetNodeByID(signal.FromNodeID)
-	if err != nil {
-		slog.Error("failed to signal, peer node not found", "error", err)
-		return
-	}
-	if peerNode.IsIngressGateway || node.IsIngressGateway || peerNode.IsInternetGateway || node.IsInternetGateway {
-		signal.Action = ""
-		return
-	}
+
 	err = HostUpdate(&models.HostUpdate{
 	err = HostUpdate(&models.HostUpdate{
 		Action: models.SignalHost,
 		Action: models.SignalHost,
 		Host:   *peerHost,
 		Host:   *peerHost,

+ 19 - 4
pro/auth/headless_callback.go

@@ -6,6 +6,7 @@ import (
 	"net/http"
 	"net/http"
 
 
 	"github.com/gravitl/netmaker/auth"
 	"github.com/gravitl/netmaker/auth"
+	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic/pro/netcache"
 	"github.com/gravitl/netmaker/logic/pro/netcache"
@@ -51,6 +52,10 @@ func HandleHeadlessSSOCallback(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 
 
+	if !isEmailAllowed(userClaims.Email) {
+		handleOauthUserNotAllowedToSignUp(w)
+		return
+	}
 	// check if user approval is already pending
 	// check if user approval is already pending
 	if logic.IsPendingUser(userClaims.getUserName()) {
 	if logic.IsPendingUser(userClaims.getUserName()) {
 		handleOauthUserSignUpApprovalPending(w)
 		handleOauthUserSignUpApprovalPending(w)
@@ -58,10 +63,20 @@ func HandleHeadlessSSOCallback(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 	user, err := logic.GetUser(userClaims.getUserName())
 	user, err := logic.GetUser(userClaims.getUserName())
 	if err != nil {
 	if err != nil {
-		response := returnErrTemplate("", "user not found", state, reqKeyIf)
-		w.WriteHeader(http.StatusForbidden)
-		w.Write(response)
-		return
+		if database.IsEmptyRecord(err) { // user must not exist, so try to make one
+			err = logic.InsertPendingUser(&models.User{
+				UserName: userClaims.getUserName(),
+			})
+			if err != nil {
+				handleSomethingWentWrong(w)
+				return
+			}
+			handleFirstTimeOauthUserSignUp(w)
+			return
+		} else {
+			handleSomethingWentWrong(w)
+			return
+		}
 	}
 	}
 	newPass, fetchErr := auth.FetchPassValue("")
 	newPass, fetchErr := auth.FetchPassValue("")
 	if fetchErr != nil {
 	if fetchErr != nil {

+ 16 - 4
pro/controllers/failover.go

@@ -159,12 +159,24 @@ func failOverME(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("peer not found"), "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("peer not found"), "badrequest"))
 		return
 		return
 	}
 	}
-	if node.IsRelayed || node.IsFailOver {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node is relayed or acting as failover"), "badrequest"))
+	if node.IsFailOver {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node is acting as failover"), "badrequest"))
 		return
 		return
 	}
 	}
-	if peerNode.IsRelayed || peerNode.IsFailOver {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("peer node is relayed or acting as failover"), "badrequest"))
+	if node.IsRelayed && node.RelayedBy == peerNode.ID.String() {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node is relayed by peer node"), "badrequest"))
+		return
+	}
+	if node.IsRelay && peerNode.RelayedBy == node.ID.String() {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node acting as relay for the peer node"), "badrequest"))
+		return
+	}
+	if node.IsInternetGateway && peerNode.InternetGwID == node.ID.String() {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node acting as internet gw for the peer node"), "badrequest"))
+		return
+	}
+	if node.InternetGwID != "" && node.InternetGwID == peerNode.ID.String() {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node using a internet gw by the peer node"), "badrequest"))
 		return
 		return
 	}
 	}
 
 

+ 16 - 0
pro/controllers/users.go

@@ -214,6 +214,10 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
 			if err != nil {
 			if err != nil {
 				continue
 				continue
 			}
 			}
+			network, err := logic.GetNetwork(node.Network)
+			if err != nil {
+				slog.Error("failed to get node network", "error", err)
+			}
 
 
 			if _, ok := user.RemoteGwIDs[node.ID.String()]; (!user.IsAdmin && !user.IsSuperAdmin) && ok {
 			if _, ok := user.RemoteGwIDs[node.ID.String()]; (!user.IsAdmin && !user.IsSuperAdmin) && ok {
 				gws := userGws[node.Network]
 				gws := userGws[node.Network]
@@ -229,6 +233,7 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
 					GwListenPort:      logic.GetPeerListenPort(host),
 					GwListenPort:      logic.GetPeerListenPort(host),
 					Metadata:          node.Metadata,
 					Metadata:          node.Metadata,
 					AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
 					AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
+					NetworkAddresses:  []string{network.AddressRange, network.AddressRange6},
 				})
 				})
 				userGws[node.Network] = gws
 				userGws[node.Network] = gws
 				delete(user.RemoteGwIDs, node.ID.String())
 				delete(user.RemoteGwIDs, node.ID.String())
@@ -246,6 +251,7 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
 					GwListenPort:      logic.GetPeerListenPort(host),
 					GwListenPort:      logic.GetPeerListenPort(host),
 					Metadata:          node.Metadata,
 					Metadata:          node.Metadata,
 					AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
 					AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
+					NetworkAddresses:  []string{network.AddressRange, network.AddressRange6},
 				})
 				})
 				userGws[node.Network] = gws
 				userGws[node.Network] = gws
 				processedAdminNodeIds[node.ID.String()] = struct{}{}
 				processedAdminNodeIds[node.ID.String()] = struct{}{}
@@ -270,6 +276,10 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
 			if err != nil {
 			if err != nil {
 				continue
 				continue
 			}
 			}
+			network, err := logic.GetNetwork(node.Network)
+			if err != nil {
+				slog.Error("failed to get node network", "error", err)
+			}
 			gws := userGws[node.Network]
 			gws := userGws[node.Network]
 
 
 			gws = append(gws, models.UserRemoteGws{
 			gws = append(gws, models.UserRemoteGws{
@@ -281,6 +291,7 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
 				GwListenPort:      logic.GetPeerListenPort(host),
 				GwListenPort:      logic.GetPeerListenPort(host),
 				Metadata:          node.Metadata,
 				Metadata:          node.Metadata,
 				AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
 				AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
+				NetworkAddresses:  []string{network.AddressRange, network.AddressRange6},
 			})
 			})
 			userGws[node.Network] = gws
 			userGws[node.Network] = gws
 		}
 		}
@@ -299,6 +310,10 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
 					slog.Error("failed to fetch host", "error", err)
 					slog.Error("failed to fetch host", "error", err)
 					continue
 					continue
 				}
 				}
+				network, err := logic.GetNetwork(node.Network)
+				if err != nil {
+					slog.Error("failed to get node network", "error", err)
+				}
 				gws := userGws[node.Network]
 				gws := userGws[node.Network]
 
 
 				gws = append(gws, models.UserRemoteGws{
 				gws = append(gws, models.UserRemoteGws{
@@ -310,6 +325,7 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
 					GwListenPort:      logic.GetPeerListenPort(host),
 					GwListenPort:      logic.GetPeerListenPort(host),
 					Metadata:          node.Metadata,
 					Metadata:          node.Metadata,
 					AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
 					AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
+					NetworkAddresses:  []string{network.AddressRange, network.AddressRange6},
 				})
 				})
 				userGws[node.Network] = gws
 				userGws[node.Network] = gws
 			}
 			}

+ 36 - 6
pro/logic/failover.go

@@ -5,15 +5,14 @@ import (
 	"net"
 	"net"
 
 
 	"github.com/google/uuid"
 	"github.com/google/uuid"
+	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 	"golang.org/x/exp/slog"
 	"golang.org/x/exp/slog"
 )
 )
 
 
 func SetFailOverCtx(failOverNode, victimNode, peerNode models.Node) error {
 func SetFailOverCtx(failOverNode, victimNode, peerNode models.Node) error {
-	if victimNode.IsIngressGateway || peerNode.IsIngressGateway || victimNode.IsInternetGateway || peerNode.IsInternetGateway {
-		return nil
-	}
+
 	if peerNode.FailOverPeers == nil {
 	if peerNode.FailOverPeers == nil {
 		peerNode.FailOverPeers = make(map[string]struct{})
 		peerNode.FailOverPeers = make(map[string]struct{})
 	}
 	}
@@ -125,7 +124,38 @@ func GetFailOverPeerIps(peer, node *models.Node) []net.IPNet {
 			if failOverpeer.IsEgressGateway {
 			if failOverpeer.IsEgressGateway {
 				allowedips = append(allowedips, logic.GetEgressIPs(&failOverpeer)...)
 				allowedips = append(allowedips, logic.GetEgressIPs(&failOverpeer)...)
 			}
 			}
-
+			if failOverpeer.IsRelay {
+				for _, id := range failOverpeer.RelayedNodes {
+					rNode, _ := logic.GetNodeByID(id)
+					if rNode.Address.IP != nil {
+						allowed := net.IPNet{
+							IP:   rNode.Address.IP,
+							Mask: net.CIDRMask(32, 32),
+						}
+						allowedips = append(allowedips, allowed)
+					}
+					if rNode.Address6.IP != nil {
+						allowed := net.IPNet{
+							IP:   rNode.Address6.IP,
+							Mask: net.CIDRMask(128, 128),
+						}
+						allowedips = append(allowedips, allowed)
+					}
+					if rNode.IsEgressGateway {
+						allowedips = append(allowedips, logic.GetEgressIPs(&rNode)...)
+					}
+				}
+			}
+			// handle ingress gateway peers
+			if failOverpeer.IsIngressGateway {
+				extPeers, _, _, err := logic.GetExtPeers(&failOverpeer, node)
+				if err != nil {
+					logger.Log(2, "could not retrieve ext peers for ", peer.ID.String(), err.Error())
+				}
+				for _, extPeer := range extPeers {
+					allowedips = append(allowedips, extPeer.AllowedIPs...)
+				}
+			}
 		}
 		}
 	}
 	}
 	return allowedips
 	return allowedips
@@ -140,10 +170,10 @@ func CreateFailOver(node models.Node) error {
 		return err
 		return err
 	}
 	}
 	if host.OS != models.OS_Types.Linux {
 	if host.OS != models.OS_Types.Linux {
-		return err
+		return errors.New("only linux nodes are allowed to be set as failover")
 	}
 	}
 	if node.IsRelayed {
 	if node.IsRelayed {
-		return err
+		return errors.New("relayed node cannot be set as failover")
 	}
 	}
 	node.IsFailOver = true
 	node.IsFailOver = true
 	err = logic.UpsertNode(&node)
 	err = logic.UpsertNode(&node)

+ 16 - 3
pro/logic/nodes.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"fmt"
 	"net"
 	"net"
 
 
+	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 	"golang.org/x/exp/slog"
 	"golang.org/x/exp/slog"
@@ -29,6 +30,7 @@ func ValidateInetGwReq(inetNode models.Node, req models.InetNodeReq, update bool
 	if inetNode.IsRelayed {
 	if inetNode.IsRelayed {
 		return fmt.Errorf("node %s is being relayed", inetHost.Name)
 		return fmt.Errorf("node %s is being relayed", inetHost.Name)
 	}
 	}
+
 	for _, clientNodeID := range req.InetNodeClientIDs {
 	for _, clientNodeID := range req.InetNodeClientIDs {
 		clientNode, err := logic.GetNodeByID(clientNodeID)
 		clientNode, err := logic.GetNodeByID(clientNodeID)
 		if err != nil {
 		if err != nil {
@@ -53,6 +55,9 @@ func ValidateInetGwReq(inetNode models.Node, req models.InetNodeReq, update bool
 				return fmt.Errorf("node %s is already using a internet gateway", clientHost.Name)
 				return fmt.Errorf("node %s is already using a internet gateway", clientHost.Name)
 			}
 			}
 		}
 		}
+		if clientNode.FailedOverBy != uuid.Nil {
+			ResetFailedOverPeer(&clientNode)
+		}
 
 
 		if clientNode.IsRelayed {
 		if clientNode.IsRelayed {
 			return fmt.Errorf("node %s is being relayed", clientHost.Name)
 			return fmt.Errorf("node %s is being relayed", clientHost.Name)
@@ -107,9 +112,13 @@ func UnsetInternetGw(node *models.Node) {
 
 
 func SetDefaultGwForRelayedUpdate(relayed, relay models.Node, peerUpdate models.HostPeerUpdate) models.HostPeerUpdate {
 func SetDefaultGwForRelayedUpdate(relayed, relay models.Node, peerUpdate models.HostPeerUpdate) models.HostPeerUpdate {
 	if relay.InternetGwID != "" {
 	if relay.InternetGwID != "" {
+		relayedHost, err := logic.GetHost(relayed.HostID.String())
+		if err != nil {
+			return peerUpdate
+		}
 		peerUpdate.ChangeDefaultGw = true
 		peerUpdate.ChangeDefaultGw = true
 		peerUpdate.DefaultGwIp = relay.Address.IP
 		peerUpdate.DefaultGwIp = relay.Address.IP
-		if peerUpdate.DefaultGwIp == nil {
+		if peerUpdate.DefaultGwIp == nil || relayedHost.EndpointIP == nil {
 			peerUpdate.DefaultGwIp = relay.Address6.IP
 			peerUpdate.DefaultGwIp = relay.Address6.IP
 		}
 		}
 
 
@@ -124,9 +133,14 @@ func SetDefaultGw(node models.Node, peerUpdate models.HostPeerUpdate) models.Hos
 		if err != nil {
 		if err != nil {
 			return peerUpdate
 			return peerUpdate
 		}
 		}
+		host, err := logic.GetHost(node.HostID.String())
+		if err != nil {
+			return peerUpdate
+		}
+
 		peerUpdate.ChangeDefaultGw = true
 		peerUpdate.ChangeDefaultGw = true
 		peerUpdate.DefaultGwIp = inetNode.Address.IP
 		peerUpdate.DefaultGwIp = inetNode.Address.IP
-		if peerUpdate.DefaultGwIp == nil {
+		if peerUpdate.DefaultGwIp == nil || host.EndpointIP == nil {
 			peerUpdate.DefaultGwIp = inetNode.Address6.IP
 			peerUpdate.DefaultGwIp = inetNode.Address6.IP
 		}
 		}
 	}
 	}
@@ -155,7 +169,6 @@ func GetAllowedIpForInetNodeClient(node, peer *models.Node) []net.IPNet {
 	if peer.Address.IP != nil {
 	if peer.Address.IP != nil {
 		_, ipnet, _ := net.ParseCIDR(IPv4Network)
 		_, ipnet, _ := net.ParseCIDR(IPv4Network)
 		allowedips = append(allowedips, *ipnet)
 		allowedips = append(allowedips, *ipnet)
-		return allowedips
 	}
 	}
 
 
 	if peer.Address6.IP != nil {
 	if peer.Address6.IP != nil {

+ 7 - 0
pro/logic/relays.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"fmt"
 	"net"
 	"net"
 
 
+	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
@@ -122,6 +123,12 @@ func ValidateRelay(relay models.RelayRequest, update bool) error {
 		if relayedNode.IsInternetGateway {
 		if relayedNode.IsInternetGateway {
 			return errors.New("cannot relay an internet gateway (" + relayedNodeID + ")")
 			return errors.New("cannot relay an internet gateway (" + relayedNodeID + ")")
 		}
 		}
+		if relayedNode.IsFailOver {
+			return errors.New("cannot relay a failOver (" + relayedNodeID + ")")
+		}
+		if relayedNode.FailedOverBy != uuid.Nil {
+			ResetFailedOverPeer(&relayedNode)
+		}
 	}
 	}
 	return err
 	return err
 }
 }

+ 11 - 15
release.md

@@ -1,22 +1,18 @@
-# Netmaker v0.24.1
+# Netmaker v0.24.2
 
 
 ## Whats New ✨
 ## Whats New ✨
-- Users Can define Multiple Endpoints On The Remote Access Gateway To Choose From While Establishing a Connection.
-- OAUTH Code Moved From CE To Pro.
-- nm-quick.sh Enhancement To Install The Latest Docker To Enable Support Of the Latest Distros.
-- IPv6 Enhancements.
+- Static Host Functionality With Separate Settings For Port and endpoint IP
+- Network Info And Metadata Info Added To Remote-Access-Client
 
 
 ## What's Fixed/Improved 🛠
 ## What's Fixed/Improved 🛠
-
-- Egress Enhancement In Multiple Networks
-- Fix armv5-v7 Upgrade Download Link
-- Fix Windows Interface Issue In Multiple Networks
-- SSO network join Improvements.
-- Remove Egress Routes After Egress Gateway Removed
-- Remote Access Gateway Connection Handling Improvements.
+- Improved FailOver Functionality
+- Local Peer Routing In Dual-Stack Environment
+- Stale Node Issue On Multinet With `netclient uninstall`
+- IPv6 Internet Gateways Improvements
+- Handled New Oauth User SignUp via Remote-Access-Client
+- PeerUpdate Improvements Around Default Host and Multi-nets
 
 
 ## Known Issues 🐞
 ## Known Issues 🐞
 
 
-- Erratic Traffic Data In Metrics
-- `netclient server leave` Leaves a Stale Node Record In At Least One Network When Part Of Multiple Networks, But Can Be Deleted From The UI.
-- IPv6 internet traffic does not route to the InetGw in Dual Stack Network
+- Erratic Traffic Data In Metrics.
+- Stale peer on the interface, when forced removed from multiple networks at once.

+ 1 - 1
swagger.yml

@@ -1472,7 +1472,7 @@ info:
 
 
         API calls must be authenticated via a header of the format -H “Authorization: Bearer <YOUR_SECRET_KEY>” There are two methods to obtain YOUR_SECRET_KEY: 1. Using the masterkey. By default, this value is “secret key,” but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [Netmaker](https://docs.netmaker.org/index.html) documentation for more details. 2. Using a JWT received for a node. This can be retrieved by calling the /api/nodes/<network>/authenticate endpoint, as documented below.
         API calls must be authenticated via a header of the format -H “Authorization: Bearer <YOUR_SECRET_KEY>” There are two methods to obtain YOUR_SECRET_KEY: 1. Using the masterkey. By default, this value is “secret key,” but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [Netmaker](https://docs.netmaker.org/index.html) documentation for more details. 2. Using a JWT received for a node. This can be retrieved by calling the /api/nodes/<network>/authenticate endpoint, as documented below.
     title: Netmaker
     title: Netmaker
-    version: 0.24.1
+    version: 0.24.2
 paths:
 paths:
     /api/dns:
     /api/dns:
         get:
         get: