Ver código fonte

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

v0.24.2
Abhishek K 1 ano atrás
pai
commit
22a00bda9b

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

@@ -31,6 +31,7 @@ body:
       label: Version
       description: What version are you running?
       options:
+        - v0.24.2
         - v0.24.1
         - v0.24.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 go build -tags=ee . -o netmaker main.go
-FROM alpine:3.19.1
+FROM alpine:3.20.0
 
 # add a c lib
 # set the working directory

+ 1 - 1
Dockerfile-quick

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

+ 1 - 1
README.md

@@ -16,7 +16,7 @@
 
 <p align="center">
   <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 href="https://hub.docker.com/r/gravitl/netmaker/tags">
     <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
 	listenPort      int
 	mtu             int
+	isStaticPort    bool
 	isStatic        bool
 	isDefault       bool
 	keepAlive       int
@@ -45,6 +46,7 @@ var hostUpdateCmd = &cobra.Command{
 			apiHost.Name = name
 			apiHost.ListenPort = listenPort
 			apiHost.MTU = mtu
+			apiHost.IsStaticPort = isStaticPort
 			apiHost.IsStatic = isStatic
 			apiHost.IsDefault = isDefault
 			apiHost.PersistentKeepalive = keepAlive
@@ -61,7 +63,8 @@ func init() {
 	hostUpdateCmd.Flags().IntVar(&listenPort, "listen_port", 0, "Listen port of the host")
 	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().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 ?")
 	rootCmd.AddCommand(hostUpdateCmd)
 }

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

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

+ 1 - 1
controllers/docs.go

@@ -10,7 +10,7 @@
 //
 //	Schemes: https
 //	BasePath: /
-//	Version: 0.24.1
+//	Version: 0.24.2
 //	Host: api.demo.netmaker.io
 //
 //	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)
 		}
 	}
-	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{
 		Action: models.DeleteHost,
 		Host:   *currHost,
 	}); err != nil {
 		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()
 	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.IsDocker = models.ParseBool(legacy.IsDocker)
 	host.IsK8S = models.ParseBool(legacy.IsK8S)
+	host.IsStaticPort = models.ParseBool(legacy.IsStatic)
 	host.IsStatic = models.ParseBool(legacy.IsStatic)
 	host.PersistentKeepalive = time.Duration(legacy.PersistentKeepalive) * time.Second
 	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
 			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)

+ 1 - 1
go.mod

@@ -57,7 +57,7 @@ require (
 	github.com/felixge/httpsnoop v1.0.3 // indirect
 	github.com/go-playground/locales v0.14.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/mattn/go-runewidth v0.0.13 // 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/guumaster/tablewriter v0.0.10 h1:A0HD94yMdt4usgxBjoEceNeE0XMJ027euoHAzsPqBQs=
 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/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 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
       containers:
       - name: netclient
-        image: gravitl/netclient:v0.24.1
+        image: gravitl/netclient:v0.24.2
         env:
         - name: TOKEN
           value: "TOKEN_VALUE"

+ 1 - 1
k8s/client/netclient.yaml

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

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

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

+ 12 - 17
logic/extpeers.go

@@ -376,7 +376,7 @@ func ToggleExtClientConnectivity(client *models.ExtClient, enable bool) (models.
 	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 idsAndAddr []models.IDandAddr
 	var egressRoutes []models.EgressNetworkRoutes
@@ -431,7 +431,7 @@ func getExtPeers(node, peer *models.Node) ([]wgtypes.PeerConfig, []models.IDandA
 				allowedips = append(allowedips, *cidr)
 			}
 		}
-		egressRoutes = append(egressRoutes, getExtPeerEgressRoute(extPeer)...)
+		egressRoutes = append(egressRoutes, getExtPeerEgressRoute(*node, extPeer)...)
 		primaryAddr := extPeer.Address
 		if primaryAddr == "" {
 			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
 }
 
-func getExtpeersExtraRoutes(network string) (egressRoutes []models.EgressNetworkRoutes) {
+func getExtpeersExtraRoutes(node models.Node, network string) (egressRoutes []models.EgressNetworkRoutes) {
 	extPeers, err := GetNetworkExtClients(network)
 	if err != nil {
 		return
@@ -478,7 +473,7 @@ func getExtpeersExtraRoutes(network string) (egressRoutes []models.EgressNetwork
 		if len(extPeer.ExtraAllowedIPs) == 0 {
 			continue
 		}
-		egressRoutes = append(egressRoutes, getExtPeerEgressRoute(extPeer)...)
+		egressRoutes = append(egressRoutes, getExtPeerEgressRoute(node, extPeer)...)
 	}
 	return
 }

+ 6 - 12
logic/hosts.go

@@ -266,6 +266,7 @@ func UpdateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool)
 	currHost.Debug = newHost.Debug
 	currHost.Verbosity = newHost.Verbosity
 	currHost.Version = newHost.Version
+	currHost.IsStaticPort = newHost.IsStaticPort
 	currHost.IsStatic = newHost.IsStatic
 	currHost.MTU = newHost.MTU
 	currHost.Name = newHost.Name
@@ -396,20 +397,13 @@ func DissasociateNodeFromHost(n *models.Node, h *models.Host) error {
 	if len(h.Nodes) == 0 {
 		return fmt.Errorf("no nodes present in given host")
 	}
-	index := -1
+	nList := []string{}
 	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() {
 		if servercfg.IsPro {
 			if clients, err := GetNetworkExtClients(n.Network); err != nil {
@@ -434,7 +428,7 @@ func DisassociateAllNodesFromHost(hostID string) error {
 	for _, nodeID := range host.Nodes {
 		node, err := GetNodeByID(nodeID)
 		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
 		}
 		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 {
 			continue
 		}
+
 		if !node.Connected || node.PendingDelete || node.Action == models.NODE_DELETE {
 			continue
 		}
@@ -181,7 +182,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				})
 			}
 			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()]
 			if servercfg.IsPro {
@@ -249,9 +250,10 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig)
 				peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1
 				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
 			} else {
@@ -259,10 +261,12 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				peerAllowedIPs = append(peerAllowedIPs, peerConfig.AllowedIPs...)
 				hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].AllowedIPs = peerAllowedIPs
 				hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].Remove = false
+				hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].Endpoint = peerConfig.Endpoint
 				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()]]
 			}
@@ -283,7 +287,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 		var extPeerIDAndAddrs []models.IDandAddr
 		var egressRoutes []models.EgressNetworkRoutes
 		if node.IsIngressGateway {
-			extPeers, extPeerIDAndAddrs, egressRoutes, err = getExtPeers(&node, &node)
+			extPeers, extPeerIDAndAddrs, egressRoutes, err = GetExtPeers(&node, &node)
 			if err == nil {
 				hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, egressRoutes...)
 				hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, extPeers...)
@@ -342,6 +346,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				},
 			}
 		}
+
 	}
 	// == post peer calculations ==
 	// 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
 func GetPeerListenPort(host *models.Host) int {
 	peerPort := host.ListenPort
-	if host.WgPublicListenPort != 0 {
+	if !host.IsStaticPort && host.WgPublicListenPort != 0 {
 		peerPort = host.WgPublicListenPort
 	}
 	return peerPort
@@ -415,7 +420,7 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet
 
 	// handle ingress gateway peers
 	if peer.IsIngressGateway {
-		extPeers, _, _, err := getExtPeers(peer, node)
+		extPeers, _, _, err := GetExtPeers(peer, node)
 		if err != nil {
 			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"
 )
 
-var version = "v0.24.1"
+var version = "v0.24.2"
 
 // Start DB Connection and start API Request Handler
 func main() {

+ 3 - 0
models/api_host.go

@@ -15,6 +15,7 @@ type ApiHost struct {
 	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"`
@@ -61,6 +62,7 @@ func (h *Host) ConvertNMHostToAPI() *ApiHost {
 		}
 	}
 	a.DefaultInterface = h.DefaultInterface
+	a.IsStaticPort = h.IsStaticPort
 	a.IsStatic = h.IsStatic
 	a.ListenPort = h.ListenPort
 	a.MTU = h.MTU
@@ -104,6 +106,7 @@ func (a *ApiHost) ConvertAPIHostToNMHost(currentHost *Host) *Host {
 	h.DefaultInterface = currentHost.DefaultInterface
 	h.IsDocker = currentHost.IsDocker
 	h.IsK8S = currentHost.IsK8S
+	h.IsStaticPort = a.IsStaticPort
 	h.IsStatic = a.IsStatic
 	h.ListenPort = a.ListenPort
 	h.MTU = a.MTU

+ 2 - 1
models/host.go

@@ -66,7 +66,8 @@ type Host struct {
 	EndpointIPv6        net.IP           `json:"endpointipv6"            yaml:"endpointipv6"`
 	IsDocker            bool             `json:"isdocker"                yaml:"isdocker"`
 	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"`
 	NatType             string           `json:"nat_type,omitempty"      yaml:"nat_type,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)
 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

+ 1 - 0
models/structs.go

@@ -75,6 +75,7 @@ type UserRemoteGws struct {
 	GwListenPort      int       `json:"gw_listen_port"`
 	Metadata          string    `json:"metadata"`
 	AllowedEndpoints  []string  `json:"allowed_endpoints"`
+	NetworkAddresses  []string  `json:"network_addresses"`
 }
 
 // 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)
 		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{
 		Action: models.SignalHost,
 		Host:   *peerHost,

+ 19 - 4
pro/auth/headless_callback.go

@@ -6,6 +6,7 @@ import (
 	"net/http"
 
 	"github.com/gravitl/netmaker/auth"
+	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic/pro/netcache"
@@ -51,6 +52,10 @@ func HandleHeadlessSSOCallback(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	if !isEmailAllowed(userClaims.Email) {
+		handleOauthUserNotAllowedToSignUp(w)
+		return
+	}
 	// check if user approval is already pending
 	if logic.IsPendingUser(userClaims.getUserName()) {
 		handleOauthUserSignUpApprovalPending(w)
@@ -58,10 +63,20 @@ func HandleHeadlessSSOCallback(w http.ResponseWriter, r *http.Request) {
 	}
 	user, err := logic.GetUser(userClaims.getUserName())
 	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("")
 	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"))
 		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
 	}
-	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
 	}
 

+ 16 - 0
pro/controllers/users.go

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

+ 36 - 6
pro/logic/failover.go

@@ -5,15 +5,14 @@ import (
 	"net"
 
 	"github.com/google/uuid"
+	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"golang.org/x/exp/slog"
 )
 
 func SetFailOverCtx(failOverNode, victimNode, peerNode models.Node) error {
-	if victimNode.IsIngressGateway || peerNode.IsIngressGateway || victimNode.IsInternetGateway || peerNode.IsInternetGateway {
-		return nil
-	}
+
 	if peerNode.FailOverPeers == nil {
 		peerNode.FailOverPeers = make(map[string]struct{})
 	}
@@ -125,7 +124,38 @@ func GetFailOverPeerIps(peer, node *models.Node) []net.IPNet {
 			if failOverpeer.IsEgressGateway {
 				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
@@ -140,10 +170,10 @@ func CreateFailOver(node models.Node) error {
 		return err
 	}
 	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 {
-		return err
+		return errors.New("relayed node cannot be set as failover")
 	}
 	node.IsFailOver = true
 	err = logic.UpsertNode(&node)

+ 16 - 3
pro/logic/nodes.go

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

+ 7 - 0
pro/logic/relays.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"net"
 
+	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
@@ -122,6 +123,12 @@ func ValidateRelay(relay models.RelayRequest, update bool) error {
 		if relayedNode.IsInternetGateway {
 			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
 }

+ 11 - 15
release.md

@@ -1,22 +1,18 @@
-# Netmaker v0.24.1
+# Netmaker v0.24.2
 
 ## 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 🛠
-
-- 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 🐞
 
-- 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.
     title: Netmaker
-    version: 0.24.1
+    version: 0.24.2
 paths:
     /api/dns:
         get: