diff --git a/controllers/network.go b/controllers/network.go index e3e84071..1c78b534 100644 --- a/controllers/network.go +++ b/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) diff --git a/controllers/node.go b/controllers/node.go index 98259ef1..bb65d655 100644 --- a/controllers/node.go +++ b/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) { @@ -535,6 +534,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) { 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,7 +542,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) { } } - if servercfg.IsDNSMode() { + if servercfg.IsDNSMode() { // TODO check when this should be updated.. err = logic.SetDNS() } if err != nil { diff --git a/controllers/server.go b/controllers/server.go index 8735fadf..c7cd40ce 100644 --- a/controllers/server.go +++ b/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 } diff --git a/controllers/server_util.go b/controllers/server_util.go new file mode 100644 index 00000000..bbbdf766 --- /dev/null +++ b/controllers/server_util.go @@ -0,0 +1,58 @@ +package controller + +import ( + "github.com/gravitl/netmaker/logger" + "github.com/gravitl/netmaker/logic" + "github.com/gravitl/netmaker/models" + "github.com/gravitl/netmaker/serverctl" +) + +func runServerUpdateIfNeeded(currentNode *models.Node, newNode *models.Node) error { + // check if a peer/server update is needed + var serverData = serverctl.ServerUpdateData{ + UpdatePeers: logic.ShouldPeersUpdate(currentNode, newNode), + } + if currentNode.IsServer == "yes" { + serverData.ServerNode = *currentNode + } + serverctl.Push(serverData) + + return handleServerUpdate() +} + +func handleServerUpdate() error { + var settings, settingsErr = serverctl.Pop() + if settingsErr != nil { + return settingsErr + } + var currentServerNodeID, err = logic.GetNetworkServerNodeID(settings.ServerNode.Network) + if err != nil { + return err + } + // ensure server client is available + if settings.UpdatePeers || (settings.ServerNode.ID == currentServerNodeID) { + serverctl.SyncServerNetwork(&settings.ServerNode) + } + // if peers should update, update peers on network + if settings.UpdatePeers { + if err = handlePeerUpdate(&settings.ServerNode); err != nil { + return err + } + logger.Log(1, "updated peers on network:", settings.ServerNode.Network) + } + // if the server node had an update, run the update function + if settings.ServerNode.ID == currentServerNodeID { + if err = logic.ServerUpdate(&settings.ServerNode); err != nil { + return err + } + logger.Log(1, "server node:", settings.ServerNode.ID, "was updated") + } + return nil +} + +// tells server to update it's peers +func handlePeerUpdate(serverNode *models.Node) error { + logger.Log(1, "updating peers on network:", serverNode.Network) + logic.SetNetworkServerPeers(serverNode) + return nil +} diff --git a/logic/nodes.go b/logic/nodes.go index 62b7eca6..808d3129 100644 --- a/logic/nodes.go +++ b/logic/nodes.go @@ -9,11 +9,13 @@ 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 @@ -166,8 +168,75 @@ 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 || @@ -177,6 +246,8 @@ func ShouldPeersUpdate(currentNode *models.Node, newNode *models.Node) bool { 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 } diff --git a/logic/server.go b/logic/server.go index 94eb016e..2c3b3f85 100644 --- a/logic/server.go +++ b/logic/server.go @@ -26,7 +26,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 +119,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 +137,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 = ¤tNode - - err = ServerPull(serverNode, false) +// ServerUpdate - updates the server +// replaces legacy Checkin code +func ServerUpdate(serverNode *models.Node) error { + var err = serverPull(serverNode, false) if isDeleteError(err) { - return ServerLeave(currentNode.ID) + return DeleteNodeByID(serverNode, true) } else if err != nil { return err } @@ -158,66 +152,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) } /** @@ -422,7 +357,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 +366,47 @@ func checkNodeActions(node *models.Node) string { return "" } +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) +} diff --git a/logic/util.go b/logic/util.go index fc383060..d5f447eb 100644 --- a/logic/util.go +++ b/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,14 +30,23 @@ func CheckEndpoint(endpoint string) bool { return len(endpointarr) == 2 } +// 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() +} + // 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) +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", node.Network, ":", err.Error()) + logger.Log(1, "could not set peers on network", serverNode.Network, ":", err.Error()) } } @@ -75,72 +83,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 { diff --git a/logic/wireguard.go b/logic/wireguard.go index b796894a..2bb5eb84 100644 --- a/logic/wireguard.go +++ b/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 } diff --git a/serverctl/serverctl.go b/serverctl/serverctl.go index 9db9a3e9..a78c96f3 100644 --- a/serverctl/serverctl.go +++ b/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 -}