Browse Source

Merge pull request #620 from gravitl/feature_v0.10.0_better_server

Feature v0.10.0 better server
dcarns 3 years ago
parent
commit
de8c4d782d

+ 2 - 4
controllers/network.go

@@ -12,7 +12,6 @@ import (
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
-	"github.com/gravitl/netmaker/serverctl"
 )
 
 const ALL_NETWORK_ACCESS = "THIS_USER_HAS_ALL"
@@ -226,9 +225,8 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if servercfg.IsClientMode() != "off" {
-		var success bool
-		success, err = serverctl.AddNetwork(&network)
-		if err != nil || !success {
+		err = logic.ServerJoin(&network)
+		if err != nil {
 			logic.DeleteNetwork(network.NetID)
 			if err == nil {
 				err = errors.New("Failed to add server to network " + network.DisplayName)

+ 31 - 4
controllers/node.go

@@ -32,7 +32,6 @@ func nodeHandlers(r *mux.Router) {
 	r.HandleFunc("/api/nodes/{network}", createNode).Methods("POST")
 	r.HandleFunc("/api/nodes/adm/{network}/lastmodified", authorize(true, "network", http.HandlerFunc(getLastModified))).Methods("GET")
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods("POST")
-
 }
 
 func authenticate(response http.ResponseWriter, request *http.Request) {
@@ -186,7 +185,7 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
 				r.Header.Set("ismasterkey", "yes")
 			}
 			if !isadmin && params["network"] != "" {
-				if functions.SliceContains(networks, params["network"]) {
+				if logic.StringSliceContains(networks, params["network"]) {
 					isnetadmin = true
 				}
 			}
@@ -404,6 +403,11 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+
+	if err = runServerPeerUpdate(node.Network, true); err != nil {
+		logger.Log(1, "internal error when creating node:", node.ID)
+	}
+
 	logger.Log(1, r.Header.Get("user"), "created new node", node.Name, "on network", node.Network)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)
@@ -414,11 +418,15 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 func uncordonNode(w http.ResponseWriter, r *http.Request) {
 	var params = mux.Vars(r)
 	w.Header().Set("Content-Type", "application/json")
-	node, err := logic.UncordonNode(params["nodeid"])
+	var nodeid = params["nodeid"]
+	node, err := logic.UncordonNode(nodeid)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+	if err = runServerPeerUpdate(node.Network, false); err != nil {
+		logger.Log(1, "internal error when approving node:", nodeid)
+	}
 	logger.Log(1, r.Header.Get("user"), "uncordoned node", node.Name)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode("SUCCESS")
@@ -440,6 +448,9 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+	if err = runServerPeerUpdate(gateway.NetID, true); err != nil {
+		logger.Log(1, "internal error when setting peers after creating egress on node:", gateway.NodeID)
+	}
 	logger.Log(1, r.Header.Get("user"), "created egress gateway on node", gateway.NodeID, "on network", gateway.NetID)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)
@@ -455,6 +466,9 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+	if err = runServerPeerUpdate(netid, true); err != nil {
+		logger.Log(1, "internal error when setting peers after removing egress on node:", nodeid)
+	}
 	logger.Log(1, r.Header.Get("user"), "deleted egress gateway", nodeid, "on network", netid)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)
@@ -472,6 +486,7 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+
 	logger.Log(1, r.Header.Get("user"), "created ingress gateway on node", nodeid, "on network", netid)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)
@@ -486,6 +501,7 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+
 	logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)
@@ -530,11 +546,14 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		newNode.PostUp = node.PostUp
 	}
 
+	var shouldPeersUpdate = logic.ShouldPeersUpdate(&node, &newNode)
+
 	err = logic.UpdateNode(&node, &newNode)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+
 	if relayupdate {
 		logic.UpdateRelay(node.Network, node.RelayAddrs, newNode.RelayAddrs)
 		if err = logic.NetworkNodesUpdatePullChanges(node.Network); err != nil {
@@ -542,9 +561,11 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
-	if servercfg.IsDNSMode() {
+	if servercfg.IsDNSMode() { // TODO check when this should be updated..
 		err = logic.SetDNS()
 	}
+
+	err = runServerPeerUpdate(node.Network, shouldPeersUpdate)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -572,6 +593,12 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	err = runServerPeerUpdate(node.Network, true)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+
 	logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
 	returnSuccessResponse(w, r, nodeid+" deleted.")
 }

+ 16 - 3
controllers/node_grpc.go

@@ -86,6 +86,12 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.Object)
 		return nil, err
 	}
 
+	err = runServerPeerUpdate(node.Network, true)
+	if err != nil {
+		logger.Log(1, "internal error when setting peers after node,", node.ID, "was created (gRPC)")
+	}
+	logger.Log(0, "new node,", node.Name, ", added on network,"+node.Network)
+
 	return response, nil
 }
 
@@ -106,6 +112,7 @@ func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.Object)
 		newnode.PostDown = node.PostDown
 		newnode.PostUp = node.PostUp
 	}
+	var shouldPeersUpdate = logic.ShouldPeersUpdate(&node, &newnode)
 
 	err = logic.UpdateNode(&node, &newnode)
 	if err != nil {
@@ -119,6 +126,10 @@ func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.Object)
 	if errN != nil {
 		return nil, err
 	}
+	err = runServerPeerUpdate(newnode.Network, shouldPeersUpdate)
+	if err != nil {
+		logger.Log(1, "could not update peers on gRPC after node,", newnode.ID, "updated (gRPC), \nerror:", err.Error())
+	}
 	return &nodepb.Object{
 		Data: string(nodeData),
 		Type: nodepb.NODE_TYPE,
@@ -138,6 +149,11 @@ func (s *NodeServiceServer) DeleteNode(ctx context.Context, req *nodepb.Object)
 		return nil, err
 	}
 
+	err = runServerPeerUpdate(node.Network, true)
+	if err != nil {
+		logger.Log(1, "internal error when setting peers after deleting node:", node.ID, "over gRPC")
+	}
+
 	return &nodepb.Object{
 		Data: "success",
 		Type: nodepb.STRING_TYPE,
@@ -152,9 +168,6 @@ func (s *NodeServiceServer) GetPeers(ctx context.Context, req *nodepb.Object) (*
 		return nil, err
 	}
 
-	if node.IsServer == "yes" && logic.IsLeader(&node) {
-		logic.SetNetworkServerPeers(&node)
-	}
 	excludeIsRelayed := node.IsRelay != "yes"
 	var relayedNode string
 	if node.IsRelayed == "yes" {

+ 6 - 0
controllers/relay.go

@@ -26,6 +26,9 @@ func createRelay(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+	if err = runServerPeerUpdate(relay.NetID, true); err != nil {
+		logger.Log(1, "internal error when creating relay on node:", relay.NodeID)
+	}
 	logger.Log(1, r.Header.Get("user"), "created relay on node", relay.NodeID, "on network", relay.NetID)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)
@@ -41,6 +44,9 @@ func deleteRelay(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+	if err = runServerPeerUpdate(netid, true); err != nil {
+		logger.Log(1, "internal error when deleting relay on node:", nodeid)
+	}
 	logger.Log(1, r.Header.Get("user"), "deleted egress gateway", nodeid, "on network", netid)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)

+ 2 - 4
controllers/server.go

@@ -9,7 +9,6 @@ import (
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
-	"github.com/gravitl/netmaker/serverctl"
 )
 
 func serverHandlers(r *mux.Router) {
@@ -70,9 +69,8 @@ func removeNetwork(w http.ResponseWriter, r *http.Request) {
 	// get params
 	var params = mux.Vars(r)
 
-	success, err := serverctl.RemoveNetwork(params["network"])
-
-	if err != nil || !success {
+	err := logic.DeleteNetwork(params["network"])
+	if err != nil {
 		json.NewEncoder(w).Encode("Could not remove server from network " + params["network"])
 		return
 	}

+ 23 - 0
controllers/server_util.go

@@ -0,0 +1,23 @@
+package controller
+
+import (
+	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/logic"
+)
+
+func runServerPeerUpdate(network string, shouldPeerUpdate bool) error {
+
+	var currentServerNodeID, err = logic.GetNetworkServerNodeID(network)
+	if err != nil {
+		return err
+	}
+	var currentServerNode, currErr = logic.GetNodeByID(currentServerNodeID)
+	if currErr != nil {
+		return currErr
+	}
+	if err = logic.ServerUpdate(&currentServerNode, shouldPeerUpdate); err != nil {
+		logger.Log(1, "server node:", currentServerNode.ID, "failed update")
+		return err
+	}
+	return nil
+}

+ 0 - 14
functions/helpers.go

@@ -31,20 +31,6 @@ func ParseIntClient(value string) (models.IntClient, error) {
 	return intClient, err
 }
 
-//Takes in an arbitrary field and value for field and checks to see if any other
-//node has that value for the same field within the network
-
-// SliceContains - sees if a slice contains something
-func SliceContains(slice []string, item string) bool {
-	set := make(map[string]struct{}, len(slice))
-	for _, s := range slice {
-		set[s] = struct{}{}
-	}
-
-	_, ok := set[item]
-	return ok
-}
-
 // GetPeersList - gets peers for given network
 func GetPeersList(networkName string) ([]models.PeersResponse, error) {
 

+ 128 - 14
logic/nodes.go

@@ -9,30 +9,22 @@ import (
 	"time"
 
 	"github.com/go-playground/validator/v10"
+	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/validation"
+	"golang.org/x/crypto/bcrypt"
 )
 
 // GetNetworkNodes - gets the nodes of a network
 func GetNetworkNodes(network string) ([]models.Node, error) {
-	var nodes = []models.Node{}
-	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
+	var nodes, err = GetAllNodes()
 	if err != nil {
-		if database.IsEmptyRecord(err) {
-			return []models.Node{}, nil
-		}
-		return nodes, err
+		return []models.Node{}, err
 	}
-	for _, value := range collection {
-
-		var node models.Node
-		err := json.Unmarshal([]byte(value), &node)
-		if err != nil {
-			continue
-		}
+	for _, node := range nodes {
 		if node.Network == network {
 			nodes = append(nodes, node)
 		}
@@ -86,7 +78,7 @@ func UncordonNode(nodeid string) (models.Node, error) {
 // GetPeers - gets the peers of a given node
 func GetPeers(node *models.Node) ([]models.Node, error) {
 	if IsLeader(node) {
-		SetNetworkServerPeers(node)
+		setNetworkServerPeers(node)
 	}
 	excludeIsRelayed := node.IsRelay != "yes"
 	var relayedNode string
@@ -166,6 +158,128 @@ func ValidateNode(node *models.Node, isUpdate bool) error {
 	return err
 }
 
+// CreateNode - creates a node in database
+func CreateNode(node *models.Node) error {
+
+	//encrypt that password so we never see it
+	hash, err := bcrypt.GenerateFromPassword([]byte(node.Password), 5)
+	if err != nil {
+		return err
+	}
+	//set password to encrypted password
+	node.Password = string(hash)
+	if node.Name == models.NODE_SERVER_NAME {
+		node.IsServer = "yes"
+	}
+	if node.DNSOn == "" {
+		if servercfg.IsDNSMode() {
+			node.DNSOn = "yes"
+		} else {
+			node.DNSOn = "no"
+		}
+	}
+	SetNodeDefaults(node)
+	node.Address, err = UniqueAddress(node.Network)
+	if err != nil {
+		return err
+	}
+	node.Address6, err = UniqueAddress6(node.Network)
+	if err != nil {
+		return err
+	}
+
+	// TODO: This covers legacy nodes, eventually want to remove legacy check
+	if node.IsServer == "yes" {
+		node.ID = uuid.NewString()
+	} else if node.IsServer != "yes" || (node.ID == "" || strings.Contains(node.ID, "###")) {
+		node.ID = uuid.NewString()
+	}
+
+	//Create a JWT for the node
+	tokenString, _ := CreateJWT(node.ID, node.MacAddress, node.Network)
+	if tokenString == "" {
+		//returnErrorResponse(w, r, errorResponse)
+		return err
+	}
+	err = ValidateNode(node, false)
+	if err != nil {
+		return err
+	}
+
+	nodebytes, err := json.Marshal(&node)
+	if err != nil {
+		return err
+	}
+	err = database.Insert(node.ID, string(nodebytes), database.NODES_TABLE_NAME)
+	if err != nil {
+		return err
+	}
+	if node.IsPending != "yes" {
+		DecrimentKey(node.Network, node.AccessKey)
+	}
+	SetNetworkNodesLastModified(node.Network)
+	if servercfg.IsDNSMode() {
+		err = SetDNS()
+	}
+	return err
+}
+
+// ShouldPeersUpdate - takes old node and sees if certain fields changing would trigger a peer update
+func ShouldPeersUpdate(currentNode *models.Node, newNode *models.Node) bool {
+	SetNodeDefaults(newNode)
+	// single comparison statements
+	if newNode.Endpoint != currentNode.Endpoint ||
+		newNode.LocalAddress != currentNode.LocalAddress ||
+		newNode.PublicKey != currentNode.PublicKey ||
+		newNode.Address != currentNode.Address ||
+		newNode.IsEgressGateway != currentNode.IsEgressGateway ||
+		newNode.IsIngressGateway != currentNode.IsIngressGateway ||
+		newNode.IsRelay != currentNode.IsRelay ||
+		newNode.UDPHolePunch != currentNode.UDPHolePunch ||
+		newNode.IsPending != currentNode.IsPending ||
+		len(newNode.ExcludedAddrs) != len(currentNode.ExcludedAddrs) ||
+		len(newNode.AllowedIPs) != len(currentNode.AllowedIPs) {
+		return true
+	}
+
+	// multi-comparison statements
+	if newNode.IsDualStack == "yes" {
+		if newNode.Address6 != currentNode.Address6 {
+			return true
+		}
+	}
+
+	if newNode.IsEgressGateway == "yes" {
+		if len(currentNode.EgressGatewayRanges) != len(newNode.EgressGatewayRanges) {
+			return true
+		}
+		for _, address := range newNode.EgressGatewayRanges {
+			if !StringSliceContains(currentNode.EgressGatewayRanges, address) {
+				return true
+			}
+		}
+	}
+
+	if newNode.IsRelay == "yes" {
+		if len(currentNode.RelayAddrs) != len(newNode.RelayAddrs) {
+			return true
+		}
+		for _, address := range newNode.RelayAddrs {
+			if !StringSliceContains(currentNode.RelayAddrs, address) {
+				return true
+			}
+		}
+	}
+
+	for _, address := range newNode.AllowedIPs {
+		if !StringSliceContains(currentNode.AllowedIPs, address) {
+			return true
+		}
+	}
+
+	return false
+}
+
 // GetAllNodes - returns all nodes in the DB
 func GetAllNodes() ([]models.Node, error) {
 	var nodes []models.Node

+ 67 - 85
logic/server.go

@@ -17,6 +17,7 @@ import (
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 )
 
+// == Public ==
 // == Join, Checkin, and Leave for Server ==
 
 // KUBERNETES_LISTEN_PORT - starting port for Kubernetes in order to use NodePort range
@@ -26,7 +27,7 @@ const KUBERNETES_LISTEN_PORT = 31821
 const KUBERNETES_SERVER_MTU = 1024
 
 // ServerJoin - responsible for joining a server to a network
-func ServerJoin(networkSettings *models.Network, serverID string) error {
+func ServerJoin(networkSettings *models.Network) error {
 
 	if networkSettings == nil || networkSettings.NetID == "" {
 		return errors.New("no network provided")
@@ -119,7 +120,7 @@ func ServerJoin(networkSettings *models.Network, serverID string) error {
 	if err = StorePrivKey(node.ID, privateKey); err != nil {
 		return err
 	}
-	if err = ServerPush(node); err != nil {
+	if err = serverPush(node); err != nil {
 		return err
 	}
 
@@ -137,18 +138,12 @@ func ServerJoin(networkSettings *models.Network, serverID string) error {
 	return nil
 }
 
-// ServerCheckin - runs pulls and pushes for server
-func ServerCheckin(serverID string, mac string, network string) error {
-	var serverNode = &models.Node{}
-	var currentNode, err = GetNodeByIDorMacAddress(serverID, mac, network)
-	if err != nil {
-		return err
-	}
-	serverNode = &currentNode
-
-	err = ServerPull(serverNode, false)
+// ServerUpdate - updates the server
+// replaces legacy Checkin code
+func ServerUpdate(serverNode *models.Node, shouldPeerUpdate bool) error {
+	var err = serverPull(serverNode, shouldPeerUpdate)
 	if isDeleteError(err) {
-		return ServerLeave(currentNode.ID)
+		return DeleteNodeByID(serverNode, true)
 	} else if err != nil {
 		return err
 	}
@@ -158,66 +153,7 @@ func ServerCheckin(serverID string, mac string, network string) error {
 		return errors.New("node has been removed")
 	}
 
-	return ServerPush(serverNode)
-}
-
-// ServerPull - pulls current config/peers for server
-func ServerPull(serverNode *models.Node, onErr bool) error {
-
-	var err error
-	if serverNode.IPForwarding == "yes" {
-		if err = setIPForwardingLinux(); err != nil {
-			return err
-		}
-	}
-	serverNode.OS = runtime.GOOS
-
-	if serverNode.PullChanges == "yes" || onErr {
-		// check for interface change
-		// checks if address is in use by another interface
-		var oldIfaceName, isIfacePresent = isInterfacePresent(serverNode.Interface, serverNode.Address)
-		if !isIfacePresent {
-			if err = deleteInterface(oldIfaceName, serverNode.PostDown); err != nil {
-				logger.Log(1, "could not delete old interface", oldIfaceName)
-			}
-			logger.Log(1, "removed old interface", oldIfaceName)
-		}
-		serverNode.PullChanges = "no"
-		if err = setWGConfig(serverNode, false); err != nil {
-			return err
-		}
-		// handle server side update
-		if err = UpdateNode(serverNode, serverNode); err != nil {
-			return err
-		}
-	} else {
-		if err = setWGConfig(serverNode, true); err != nil {
-			if errors.Is(err, os.ErrNotExist) {
-				return ServerPull(serverNode, true)
-			} else {
-				return err
-			}
-		}
-	}
-
-	return nil
-}
-
-// ServerPush - pushes config changes for server checkins/join
-func ServerPush(serverNode *models.Node) error {
-	serverNode.OS = runtime.GOOS
-	serverNode.SetLastCheckIn()
-	return UpdateNode(serverNode, serverNode)
-}
-
-// ServerLeave - removes a server node
-func ServerLeave(serverID string) error {
-
-	var serverNode, err = GetNodeByID(serverID)
-	if err != nil {
-		return err
-	}
-	return DeleteNodeByID(&serverNode, true)
+	return serverPush(serverNode)
 }
 
 /**
@@ -231,17 +167,14 @@ func GetServerPeers(serverNode *models.Node) ([]wgtypes.PeerConfig, bool, []stri
 	var gateways []string
 	var peers []wgtypes.PeerConfig
 	var nodes []models.Node // fill above fields from server or client
+	var err error
 
-	var nodecfg, err = GetNodeByIDorMacAddress(serverNode.ID, serverNode.MacAddress, serverNode.Network)
-	if err != nil {
-		return nil, hasGateway, gateways, err
-	}
-	nodes, err = GetPeers(&nodecfg)
+	nodes, err = GetPeers(serverNode)
 	if err != nil {
 		return nil, hasGateway, gateways, err
 	}
 
-	keepalive := nodecfg.PersistentKeepalive
+	keepalive := serverNode.PersistentKeepalive
 	keepalivedur, err := time.ParseDuration(strconv.FormatInt(int64(keepalive), 10) + "s")
 	if err != nil {
 		logger.Log(1, "Issue with format of keepalive duration value, Please view server config:", err.Error())
@@ -255,11 +188,11 @@ func GetServerPeers(serverNode *models.Node) ([]wgtypes.PeerConfig, bool, []stri
 			return peers, hasGateway, gateways, err
 		}
 
-		if nodecfg.PublicKey == node.PublicKey {
+		if serverNode.PublicKey == node.PublicKey {
 			continue
 		}
-		if nodecfg.Endpoint == node.Endpoint {
-			if nodecfg.LocalAddress != node.LocalAddress && node.LocalAddress != "" {
+		if serverNode.Endpoint == node.Endpoint {
+			if serverNode.LocalAddress != node.LocalAddress && node.LocalAddress != "" {
 				node.Endpoint = node.LocalAddress
 			} else {
 				continue
@@ -304,8 +237,8 @@ func GetServerPeers(serverNode *models.Node) ([]wgtypes.PeerConfig, bool, []stri
 					logger.Log(2, "egress IP range of", iprange, "overlaps with", node.Endpoint, ", omitting")
 					continue // skip adding egress range if overlaps with node's ip
 				}
-				if ipnet.Contains(net.ParseIP(nodecfg.LocalAddress)) { // ensuring egress gateway range does not contain public ip of node
-					logger.Log(2, "egress IP range of", iprange, "overlaps with", nodecfg.LocalAddress, ", omitting")
+				if ipnet.Contains(net.ParseIP(serverNode.LocalAddress)) { // ensuring egress gateway range does not contain public ip of node
+					logger.Log(2, "egress IP range of", iprange, "overlaps with", serverNode.LocalAddress, ", omitting")
 					continue // skip adding egress range if overlaps with node's local ip
 				}
 				gateways = append(gateways, iprange)
@@ -422,7 +355,7 @@ func checkNodeActions(node *models.Node) string {
 		}
 	}
 	if node.Action == models.NODE_DELETE {
-		err := ServerLeave(node.ID)
+		err := DeleteNodeByID(node, true)
 		if err != nil {
 			logger.Log(1, "error deleting locally:", err.Error())
 		}
@@ -431,6 +364,49 @@ func checkNodeActions(node *models.Node) string {
 	return ""
 }
 
+// == Private ==
+
+func serverPull(serverNode *models.Node, onErr bool) error {
+
+	var err error
+	if serverNode.IPForwarding == "yes" {
+		if err = setIPForwardingLinux(); err != nil {
+			return err
+		}
+	}
+	serverNode.OS = runtime.GOOS
+
+	if serverNode.PullChanges == "yes" || onErr {
+		// check for interface change
+		// checks if address is in use by another interface
+		var oldIfaceName, isIfacePresent = isInterfacePresent(serverNode.Interface, serverNode.Address)
+		if !isIfacePresent {
+			if err = deleteInterface(oldIfaceName, serverNode.PostDown); err != nil {
+				logger.Log(1, "could not delete old interface", oldIfaceName)
+			}
+			logger.Log(1, "removed old interface", oldIfaceName)
+		}
+		serverNode.PullChanges = "no"
+		if err = setWGConfig(serverNode, false); err != nil {
+			return err
+		}
+		// handle server side update
+		if err = UpdateNode(serverNode, serverNode); err != nil {
+			return err
+		}
+	} else {
+		if err = setWGConfig(serverNode, true); err != nil {
+			if errors.Is(err, os.ErrNotExist) {
+				return serverPull(serverNode, true)
+			} else {
+				return err
+			}
+		}
+	}
+
+	return nil
+}
+
 func getServerLocalIP(networkSettings *models.Network) (string, error) {
 
 	var networkCIDR = networkSettings.LocalRange
@@ -452,3 +428,9 @@ func getServerLocalIP(networkSettings *models.Network) (string, error) {
 	}
 	return "", errors.New("could not find a local ip for server")
 }
+
+func serverPush(serverNode *models.Node) error {
+	serverNode.OS = runtime.GOOS
+	serverNode.SetLastCheckIn()
+	return UpdateNode(serverNode, serverNode)
+}

+ 20 - 76
logic/util.go

@@ -5,17 +5,16 @@ import (
 	"encoding/base64"
 	"encoding/json"
 	"math/rand"
+	"os"
 	"strconv"
 	"strings"
 	"time"
 
-	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/servercfg"
-	"golang.org/x/crypto/bcrypt"
 )
 
 // IsBase64 - checks if a string is in base64 format
@@ -31,15 +30,13 @@ func CheckEndpoint(endpoint string) bool {
 	return len(endpointarr) == 2
 }
 
-// SetNetworkServerPeers - sets the network server peers of a given node
-func SetNetworkServerPeers(node *models.Node) {
-	if currentPeersList, err := GetSystemPeers(node); err == nil {
-		if database.SetPeers(currentPeersList, node.Network) {
-			logger.Log(1, "set new peers on network", node.Network)
-		}
-	} else {
-		logger.Log(1, "could not set peers on network", node.Network, ":", err.Error())
+// FileExists - checks if local file exists
+func FileExists(f string) bool {
+	info, err := os.Stat(f)
+	if os.IsNotExist(err) {
+		return false
 	}
+	return !info.IsDir()
 }
 
 // DeleteNodeByMacAddress - deletes a node from database or moves into delete nodes table
@@ -75,72 +72,6 @@ func DeleteNodeByMacAddress(node *models.Node, exterminate bool) error {
 	return removeLocalServer(node)
 }
 
-// CreateNode - creates a node in database
-func CreateNode(node *models.Node) error {
-
-	//encrypt that password so we never see it
-	hash, err := bcrypt.GenerateFromPassword([]byte(node.Password), 5)
-	if err != nil {
-		return err
-	}
-	//set password to encrypted password
-	node.Password = string(hash)
-	if node.Name == models.NODE_SERVER_NAME {
-		node.IsServer = "yes"
-	}
-	if node.DNSOn == "" {
-		if servercfg.IsDNSMode() {
-			node.DNSOn = "yes"
-		} else {
-			node.DNSOn = "no"
-		}
-	}
-	SetNodeDefaults(node)
-	node.Address, err = UniqueAddress(node.Network)
-	if err != nil {
-		return err
-	}
-	node.Address6, err = UniqueAddress6(node.Network)
-	if err != nil {
-		return err
-	}
-
-	// TODO: This covers legacy nodes, eventually want to remove legacy check
-	if node.IsServer == "yes" {
-		node.ID = uuid.NewString()
-	} else if node.IsServer != "yes" || (node.ID == "" || strings.Contains(node.ID, "###")) {
-		node.ID = uuid.NewString()
-	}
-
-	//Create a JWT for the node
-	tokenString, _ := CreateJWT(node.ID, node.MacAddress, node.Network)
-	if tokenString == "" {
-		//returnErrorResponse(w, r, errorResponse)
-		return err
-	}
-	err = ValidateNode(node, false)
-	if err != nil {
-		return err
-	}
-
-	nodebytes, err := json.Marshal(&node)
-	if err != nil {
-		return err
-	}
-	err = database.Insert(node.ID, string(nodebytes), database.NODES_TABLE_NAME)
-	if err != nil {
-		return err
-	}
-	if node.IsPending != "yes" {
-		DecrimentKey(node.Network, node.AccessKey)
-	}
-	SetNetworkNodesLastModified(node.Network)
-	if servercfg.IsDNSMode() {
-		err = SetDNS()
-	}
-	return err
-}
-
 // SetNetworkNodesLastModified - sets the network nodes last modified
 func SetNetworkNodesLastModified(networkName string) error {
 
@@ -402,3 +333,16 @@ func StringSliceContains(slice []string, item string) bool {
 	}
 	return false
 }
+
+// == private ==
+
+// sets the network server peers of a given node
+func setNetworkServerPeers(serverNode *models.Node) {
+	if currentPeersList, err := getSystemPeers(serverNode); err == nil {
+		if database.SetPeers(currentPeersList, serverNode.Network) {
+			logger.Log(1, "set new peers on network", serverNode.Network)
+		}
+	} else {
+		logger.Log(1, "could not set peers on network", serverNode.Network, ":", err.Error())
+	}
+}

+ 16 - 25
logic/wireguard.go

@@ -16,8 +16,18 @@ import (
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 )
 
-// GetSystemPeers - gets the server peers
-func GetSystemPeers(node *models.Node) (map[string]string, error) {
+// RemoveConf - removes a configuration for a given WireGuard interface
+func RemoveConf(iface string, printlog bool) error {
+	var err error
+	confPath := ncutils.GetNetclientPathSpecific() + iface + ".conf"
+	err = removeWGQuickConf(confPath, printlog)
+	return err
+}
+
+// == Private Functions ==
+
+// gets the server peers locally
+func getSystemPeers(node *models.Node) (map[string]string, error) {
 	peers := make(map[string]string)
 
 	client, err := wgctrl.New()
@@ -37,16 +47,6 @@ func GetSystemPeers(node *models.Node) (map[string]string, error) {
 	return peers, nil
 }
 
-// RemoveConf - removes a configuration for a given WireGuard interface
-func RemoveConf(iface string, printlog bool) error {
-	var err error
-	confPath := ncutils.GetNetclientPathSpecific() + iface + ".conf"
-	err = removeWGQuickConf(confPath, printlog)
-	return err
-}
-
-// == Private Functions ==
-
 func initWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig, hasGateway bool, gateways []string) error {
 
 	key, err := wgtypes.ParseKey(privkey)
@@ -93,16 +93,6 @@ func initWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
 			logger.Log(1, "error writing wg conf file to", confPath, ":", err.Error())
 			return err
 		}
-		if ncutils.IsWindows() {
-			wgConfPath := ncutils.GetWGPathSpecific() + ifacename + ".conf"
-			logger.Log(1, "writing wg conf file to:", confPath)
-			err = os.WriteFile(wgConfPath, []byte(newConf), 0644)
-			if err != nil {
-				logger.Log(1, "error writing wg conf file to", wgConfPath, ":", err.Error())
-				return err
-			}
-			confPath = wgConfPath
-		}
 		// spin up userspace + apply the conf file
 		var deviceiface = ifacename
 		d, _ := wgclient.Device(deviceiface)
@@ -182,9 +172,10 @@ func setKernelDevice(ifacename string, address string) error {
 		return err
 	}
 
-	_, _ = ncutils.RunCmd("ip link delete dev "+ifacename, false)
-	_, _ = ncutils.RunCmd(ipExec+" link add dev "+ifacename+" type wireguard", true)
-	_, _ = ncutils.RunCmd(ipExec+" address add dev "+ifacename+" "+address+"/24", true) // this is a bug waiting to happen
+	// == best effort ==
+	ncutils.RunCmd("ip link delete dev "+ifacename, false)
+	ncutils.RunCmd(ipExec+" link add dev "+ifacename+" type wireguard", true)
+	ncutils.RunCmd(ipExec+" address add dev "+ifacename+" "+address+"/24", true) // this is a bug waiting to happen
 
 	return nil
 }

+ 0 - 17
main.go

@@ -9,7 +9,6 @@ import (
 	"runtime/debug"
 	"strconv"
 	"sync"
-	"time"
 
 	"github.com/gravitl/netmaker/auth"
 	controller "github.com/gravitl/netmaker/controllers"
@@ -110,25 +109,9 @@ func startControllers() {
 		logger.Log(0, "No Server Mode selected, so nothing is being served! Set either Agent mode (AGENT_BACKEND) or Rest mode (REST_BACKEND) to 'true'.")
 	}
 
-	if servercfg.IsClientMode() == "on" {
-		var checkintime = time.Duration(servercfg.GetServerCheckinInterval()) * time.Second
-		for { // best effort currently
-			var serverGroup sync.WaitGroup
-			serverGroup.Add(1)
-			go runClient(&serverGroup)
-			serverGroup.Wait()
-			time.Sleep(checkintime)
-		}
-	}
-
 	waitnetwork.Wait()
 }
 
-func runClient(wg *sync.WaitGroup) {
-	defer wg.Done()
-	go serverctl.HandleContainedClient()
-}
-
 func runGRPC(wg *sync.WaitGroup) {
 
 	defer wg.Done()

+ 38 - 3
models/node.go

@@ -73,6 +73,7 @@ type Node struct {
 	OS                  string   `json:"os" bson:"os" yaml:"os"`
 	MTU                 int32    `json:"mtu" bson:"mtu" yaml:"mtu"`
 	Version             string   `json:"version" bson:"version" yaml:"version"`
+	ExcludedAddrs       []string `json:"excludedaddrs" bson:"excludedaddrs" yaml:"excludedaddrs"`
 }
 
 // NodesArray - used for node sorting
@@ -107,78 +108,91 @@ func (node *Node) SetDefaulIsPending() {
 	}
 }
 
+// Node.SetDefaultIsRelayed - set default is relayed
 func (node *Node) SetDefaultIsRelayed() {
 	if node.IsRelayed == "" {
 		node.IsRelayed = "no"
 	}
 }
 
+// Node.SetDefaultIsRelay - set default isrelay
 func (node *Node) SetDefaultIsRelay() {
 	if node.IsRelay == "" {
 		node.IsRelay = "no"
 	}
 }
 
+// Node.SetDefaultEgressGateway - sets default egress gateway status
 func (node *Node) SetDefaultEgressGateway() {
 	if node.IsEgressGateway == "" {
 		node.IsEgressGateway = "no"
 	}
 }
 
+// Node.SetDefaultIngressGateway - sets default ingress gateway status
 func (node *Node) SetDefaultIngressGateway() {
 	if node.IsIngressGateway == "" {
 		node.IsIngressGateway = "no"
 	}
 }
 
+// Node.SetDefaultAction - sets default action status
 func (node *Node) SetDefaultAction() {
 	if node.Action == "" {
 		node.Action = NODE_NOOP
 	}
 }
 
+// Node.SetRoamingDefault - sets default roaming status
 func (node *Node) SetRoamingDefault() {
 	if node.Roaming == "" {
 		node.Roaming = "yes"
 	}
 }
 
+// Node.SetPullChangesDefault - sets default pull changes status
 func (node *Node) SetPullChangesDefault() {
 	if node.PullChanges == "" {
 		node.PullChanges = "no"
 	}
 }
 
+// Node.SetIPForwardingDefault - set ip forwarding default
 func (node *Node) SetIPForwardingDefault() {
 	if node.IPForwarding == "" {
 		node.IPForwarding = "yes"
 	}
 }
 
+// Node.SetIsLocalDefault - set is local default
 func (node *Node) SetIsLocalDefault() {
 	if node.IsLocal == "" {
 		node.IsLocal = "no"
 	}
 }
 
+// Node.SetDNSOnDefault - sets dns on default
 func (node *Node) SetDNSOnDefault() {
 	if node.DNSOn == "" {
 		node.DNSOn = "yes"
 	}
 }
 
+// Node.SetIsDualStackDefault - set is dual stack default status
 func (node *Node) SetIsDualStackDefault() {
 	if node.IsDualStack == "" {
 		node.IsDualStack = "no"
 	}
 }
 
+// Node.SetIsServerDefault - sets node isserver default
 func (node *Node) SetIsServerDefault() {
 	if node.IsServer != "yes" {
 		node.IsServer = "no"
 	}
 }
 
+// Node.SetIsStaticDefault - set is static default
 func (node *Node) SetIsStaticDefault() {
 	if node.IsServer == "yes" {
 		node.IsStatic = "yes"
@@ -187,28 +201,41 @@ func (node *Node) SetIsStaticDefault() {
 	}
 }
 
+// Node.SetLastModified - set last modified initial time
 func (node *Node) SetLastModified() {
 	node.LastModified = time.Now().Unix()
 }
 
+// Node.SetLastCheckIn - time.Now().Unix()
 func (node *Node) SetLastCheckIn() {
 	node.LastCheckIn = time.Now().Unix()
 }
 
+// Node.SetLastPeerUpdate - sets last peer update time
 func (node *Node) SetLastPeerUpdate() {
 	node.LastPeerUpdate = time.Now().Unix()
 }
 
+// Node.SetExpirationDateTime - sets node expiry time
 func (node *Node) SetExpirationDateTime() {
 	node.ExpirationDateTime = time.Now().Unix() + TEN_YEARS_IN_SECONDS
 }
 
+// Node.SetDefaultName - sets a random name to node
 func (node *Node) SetDefaultName() {
 	if node.Name == "" {
 		node.Name = GenerateNodeName()
 	}
 }
 
+// Node.SetDefaultExcludedAddrs - sets ExcludedAddrs to empty array if nil
+func (node *Node) SetDefaultExcludedAddrs() {
+	if node.ExcludedAddrs == nil {
+		node.ExcludedAddrs = make([]string, 0)
+	}
+}
+
+// Node.Fill - fills other node data into calling node data if not set on calling node
 func (newNode *Node) Fill(currentNode *Node) {
 	newNode.ID = currentNode.ID
 
@@ -354,8 +381,15 @@ func (newNode *Node) Fill(currentNode *Node) {
 	if newNode.IsRelayed == "" {
 		newNode.IsRelayed = currentNode.IsRelayed
 	}
+	if newNode.Version == "" {
+		newNode.Version = currentNode.Version
+	}
+	if newNode.ExcludedAddrs == nil || len(newNode.ExcludedAddrs) != len(currentNode.ExcludedAddrs) {
+		newNode.ExcludedAddrs = currentNode.ExcludedAddrs
+	}
 }
 
+// StringWithCharset - returns random string inside defined charset
 func StringWithCharset(length int, charset string) string {
 	b := make([]byte, length)
 	for i := range b {
@@ -364,13 +398,14 @@ func StringWithCharset(length int, charset string) string {
 	return string(b)
 }
 
-//Check for valid IPv4 address
-//Note: We dont handle IPv6 AT ALL!!!!! This definitely is needed at some point
-//But for iteration 1, lets just stick to IPv4. Keep it simple stupid.
+// IsIpv4Net - check for valid IPv4 address
+// Note: We dont handle IPv6 AT ALL!!!!! This definitely is needed at some point
+// But for iteration 1, lets just stick to IPv4. Keep it simple stupid.
 func IsIpv4Net(host string) bool {
 	return net.ParseIP(host) != nil
 }
 
+// Node.NameInNodeCharset - returns if name is in charset below or not
 func (node *Node) NameInNodeCharSet() bool {
 
 	charset := "abcdefghijklmnopqrstuvwxyz1234567890-"

+ 7 - 0
models/structs.go

@@ -156,3 +156,10 @@ type RelayRequest struct {
 	NetID      string   `json:"netid" bson:"netid"`
 	RelayAddrs []string `json:"relayaddrs" bson:"relayaddrs"`
 }
+
+// ServerUpdateData - contains data to configure server
+// and if it should set peers
+type ServerUpdateData struct {
+	UpdatePeers bool `json:"updatepeers" bson:"updatepeers"`
+	Node        Node `json:"servernode" bson:"servernode"`
+}

+ 21 - 98
serverctl/serverctl.go

@@ -1,51 +1,17 @@
 package serverctl
 
 import (
-	"encoding/json"
 	"errors"
 	"net"
 	"os"
 	"strings"
 
-	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/ncutils"
-	"github.com/gravitl/netmaker/servercfg"
 )
 
-// GetServerWGConf - gets the server WG configuration
-func GetServerWGConf() (models.IntClient, error) {
-	var server models.IntClient
-	collection, err := database.FetchRecords(database.INT_CLIENTS_TABLE_NAME)
-	if err != nil {
-		return models.IntClient{}, errors.New("could not find comms server")
-	}
-	for _, value := range collection {
-		json.Unmarshal([]byte(value), &server)
-		if server.Network == "comms" && server.IsServer == "yes" {
-			return server, nil
-		}
-	}
-	return models.IntClient{}, errors.New("could not find comms server")
-}
-
-// FileExists - checks if local file exists
-func FileExists(f string) bool {
-	info, err := os.Stat(f)
-	if os.IsNotExist(err) {
-		return false
-	}
-	return !info.IsDir()
-}
-
-// RemoveNetwork - removes a network locally on server
-func RemoveNetwork(network string) (bool, error) {
-	err := logic.ServerLeave(network)
-	return true, err
-}
-
 // InitServerNetclient - intializes the server netclient
 func InitServerNetclient() error {
 	netclientDir := ncutils.GetNetclientPath()
@@ -59,78 +25,42 @@ func InitServerNetclient() error {
 	return nil
 }
 
-// HandleContainedClient - function for checkins on server
-func HandleContainedClient() error {
-	servernets, err := logic.GetNetworks()
-	if err != nil && !database.IsEmptyRecord(err) {
+// SyncServerNetwork - ensures a wg interface and node exists for server
+func SyncServerNetwork(serverNode *models.Node) error {
+	serverNetworkSettings, err := logic.GetNetwork(serverNode.Network)
+	if err != nil {
 		return err
 	}
-	if len(servernets) > 0 {
-		if err != nil {
-			return err
-		}
-		for _, serverNet := range servernets {
-			var serverID, err = logic.GetNetworkServerNodeID(serverNet.NetID)
-			if err != nil {
-				logger.Log(1, "error occurred during server checkin:", err.Error())
-				continue
-			}
-			err = logic.ServerCheckin(serverID, servercfg.GetNodeID(), serverNet.NetID)
-			if err != nil {
-				logger.Log(1, "error occurred during server checkin:", err.Error())
-			} else {
-				logger.Log(3, "completed peers check of network", serverNet.NetID)
-			}
-		}
-		syncErr := SyncNetworks(servernets[:])
-		if syncErr != nil {
-			logger.Log(1, "error syncing networks:", syncErr.Error())
-			syncErr = nil
-		}
-		// logger.Log("completed a checkin call", 3)
-	}
-	return nil
-}
-
-// SyncNetworks - syncs the networks for servers
-func SyncNetworks(servernets []models.Network) error {
-
 	localnets, err := net.Interfaces()
 	if err != nil {
 		return err
 	}
-	// check networks to join
-	for _, servernet := range servernets {
-		exists := false
-		for _, localnet := range localnets {
-			if servernet.DefaultInterface == localnet.Name {
-				exists = true
-			}
+	exists := false
+	for _, localnet := range localnets {
+		if serverNetworkSettings.DefaultInterface == localnet.Name {
+			exists = true
 		}
-		if !exists {
-			success, err := AddNetwork(&servernet)
-			if err != nil || !success {
-				if err == nil {
-					err = errors.New("network add failed for " + servernet.NetID)
-				}
-				if !strings.Contains(err.Error(), "macaddress_unique") { // ignore macaddress unique error throws
-					logger.Log(1, "error adding network", servernet.NetID, "during sync:", err.Error())
-				}
+	}
+	if !exists {
+		err := logic.ServerJoin(&serverNetworkSettings)
+		if err != nil {
+			if err == nil {
+				err = errors.New("network add failed for " + serverNetworkSettings.NetID)
+			}
+			if !strings.Contains(err.Error(), "macaddress_unique") { // ignore macaddress unique error throws
+				logger.Log(1, "error adding network", serverNetworkSettings.NetID, "during sync:", err.Error())
 			}
 		}
 	}
-	// check networks to leave
 	for _, localnet := range localnets {
 		if strings.Contains(localnet.Name, "nm-") {
 			var exists = ""
-			for _, servernet := range servernets {
-				if servernet.DefaultInterface == localnet.Name {
-					exists = servernet.NetID
-				}
+			if serverNetworkSettings.DefaultInterface == localnet.Name {
+				exists = serverNetworkSettings.NetID
 			}
 			if exists == "" {
-				success, err := RemoveNetwork(exists)
-				if err != nil || !success {
+				err := logic.DeleteNodeByID(serverNode, true)
+				if err != nil {
 					if err == nil {
 						err = errors.New("network delete failed for " + exists)
 					}
@@ -139,12 +69,5 @@ func SyncNetworks(servernets []models.Network) error {
 			}
 		}
 	}
-
 	return nil
 }
-
-// AddNetwork - add a network to server in client mode
-func AddNetwork(networkSettings *models.Network) (bool, error) {
-	var err = logic.ServerJoin(networkSettings, servercfg.GetNodeID())
-	return true, err
-}

+ 29 - 0
serverctl/serverq.go

@@ -0,0 +1,29 @@
+package serverctl
+
+import (
+	"fmt"
+
+	"github.com/gravitl/netmaker/models"
+)
+
+// ServerQueue - holds data to be updated across the server
+var ServerQueue chan models.ServerUpdateData
+
+func init() {
+	ServerQueue = make(chan models.ServerUpdateData, 100)
+}
+
+// Push - Pushes ServerUpdateData to be used later
+func Push(serverData models.ServerUpdateData) {
+	ServerQueue <- serverData
+}
+
+// Pop - fetches first available data from queue
+func Pop() (models.ServerUpdateData, error) {
+	select {
+	case serverData := <-ServerQueue:
+		return serverData, nil
+	default:
+		return models.ServerUpdateData{}, fmt.Errorf("empty server queue")
+	}
+}