From d4399d132101fde665e017f999591a2e5d1e755d Mon Sep 17 00:00:00 2001 From: Matthew R Kasun Date: Thu, 20 Jan 2022 16:39:54 -0500 Subject: [PATCH] rebase fixes --- controllers/node_grpc.go | 6 +- logic/nodes.go | 201 +++++++++++++++++++++++++++------------ logic/util.go | 121 +++++++++-------------- models/node.go | 41 +++++++- 4 files changed, 225 insertions(+), 144 deletions(-) diff --git a/controllers/node_grpc.go b/controllers/node_grpc.go index 774ce61d..1b1bf2b3 100644 --- a/controllers/node_grpc.go +++ b/controllers/node_grpc.go @@ -161,9 +161,9 @@ func (s *NodeServiceServer) GetPeers(ctx context.Context, req *nodepb.Object) (* return nil, err } - if node.IsServer == "yes" && logic.IsLeader(&node) { - logic.SetNetworkServerPeers(&node) - } + //if node.IsServer == "yes" && logic.IsLeader(&node) { + // logic.setNetworkServerPeers(&node) + //} excludeIsRelayed := node.IsRelay != "yes" var relayedNode string if node.IsRelayed == "yes" { diff --git a/logic/nodes.go b/logic/nodes.go index 0c8c6b81..073eeb20 100644 --- a/logic/nodes.go +++ b/logic/nodes.go @@ -4,38 +4,27 @@ import ( "encoding/json" "errors" "fmt" - "net" "sort" - "strconv" "strings" "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.zx2c4.com/wireguard/wgctrl/wgtypes" + "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) } @@ -77,7 +66,7 @@ func UncordonNode(nodeid string) (models.Node, error) { node.SetLastModified() node.IsPending = "no" node.PullChanges = "yes" - data, err := json.Marshal(&node) + data, err := json.Marshal(&node) if err != nil { return node, err } @@ -86,52 +75,11 @@ func UncordonNode(nodeid string) (models.Node, error) { return node, err } -// GetWGNodePeers - gets the wg peers of a given node -func GetWGNodePeers(node *models.Node) ([]wgtypes.PeerConfig, error){ - var peers []wgtypes.PeerConfig - var peerData wgtypes.PeerConfig - nodes, err := GetPeers(node) - if err != nil { - return peers, err - } - logger.Log(0, "the peers of " + node.Name + " are:") - for _, peer := range nodes { - logger.Log(0, "peer: " + peer.Name) - } - for _, peer := range nodes { - //this should not happen but it does - if peer.Name == node.Name { - logger.Log(0, "GetPeers returned me " + node.Name) - continue - } - pubkey, err :=wgtypes.ParseKey(peer.PublicKey) - if err != nil { - logger.Log(0, "ParseKey failed " +err.Error()) - continue - } - endpoint := peer.Endpoint + ":" + strconv.Itoa(int(peer.ListenPort)) - address, err := net.ResolveUDPAddr("udp", endpoint) - if err != nil { - logger.Log(0, "could not resolve endpoint " + err.Error()) - continue - } - ///Persitent Keepalive - //Allowed IPs - peerData = wgtypes.PeerConfig{ - PublicKey: pubkey, - Endpoint: address, - } - peers = append (peers, peerData) - } - return peers, nil -} - -// GetPeers - gets a list of nodes that are peers of a given node +// GetPeers - gets the peers of a given node func GetPeers(node *models.Node) ([]models.Node, error) { - //if IsLeader(node) { - // logger.Log(0, node.Name + " is a leader") - // SetNetworkServerPeers(node) - //} + if IsLeader(node) { + setNetworkServerPeers(node) + } excludeIsRelayed := node.IsRelay != "yes" var relayedNode string if node.IsRelayed == "yes" { @@ -163,6 +111,13 @@ func IsLeader(node *models.Node) bool { // UpdateNode - takes a node and updates another node with it's values func UpdateNode(currentNode *models.Node, newNode *models.Node) error { + if newNode.Address != currentNode.Address { + if network, err := GetParentNetwork(newNode.Network); err == nil { + if !IsAddressInCIDR(newNode.Address, network.AddressRange) { + return fmt.Errorf("invalid address provided; out of network range for node %s", newNode.ID) + } + } + } newNode.Fill(currentNode) if err := ValidateNode(newNode, true); err != nil { return err @@ -210,6 +165,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 diff --git a/logic/util.go b/logic/util.go index 7fd008e2..3af94960 100644 --- a/logic/util.go +++ b/logic/util.go @@ -4,18 +4,19 @@ package logic import ( "encoding/base64" "encoding/json" + "fmt" "math/rand" + "net" + "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 +32,36 @@ 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() +} + +// IsAddressInCIDR - util to see if an address is in a cidr or not +func IsAddressInCIDR(address, cidr string) bool { + var _, currentCIDR, cidrErr = net.ParseCIDR(cidr) + if cidrErr != nil { + return false + } + var addrParts = strings.Split(address, ".") + var addrPartLength = len(addrParts) + if addrPartLength != 4 { + return false + } else { + if addrParts[addrPartLength-1] == "0" || + addrParts[addrPartLength-1] == "255" { + return false + } + } + ip, _, err := net.ParseCIDR(fmt.Sprintf("%s/32", address)) + if err != nil { + return false + } + return currentCIDR.Contains(ip) } // DeleteNodeByMacAddress - deletes a node from database or moves into delete nodes table @@ -75,72 +97,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 +358,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()) + } +} diff --git a/models/node.go b/models/node.go index 64a30de2..16f65afd 100644 --- a/models/node.go +++ b/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-"