|
@@ -4,38 +4,27 @@ import (
|
|
"encoding/json"
|
|
"encoding/json"
|
|
"errors"
|
|
"errors"
|
|
"fmt"
|
|
"fmt"
|
|
- "net"
|
|
|
|
"sort"
|
|
"sort"
|
|
- "strconv"
|
|
|
|
"strings"
|
|
"strings"
|
|
"time"
|
|
"time"
|
|
|
|
|
|
"github.com/go-playground/validator/v10"
|
|
"github.com/go-playground/validator/v10"
|
|
|
|
+ "github.com/google/uuid"
|
|
"github.com/gravitl/netmaker/database"
|
|
"github.com/gravitl/netmaker/database"
|
|
"github.com/gravitl/netmaker/logger"
|
|
"github.com/gravitl/netmaker/logger"
|
|
"github.com/gravitl/netmaker/models"
|
|
"github.com/gravitl/netmaker/models"
|
|
"github.com/gravitl/netmaker/servercfg"
|
|
"github.com/gravitl/netmaker/servercfg"
|
|
"github.com/gravitl/netmaker/validation"
|
|
"github.com/gravitl/netmaker/validation"
|
|
- "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
|
|
|
|
+ "golang.org/x/crypto/bcrypt"
|
|
)
|
|
)
|
|
|
|
|
|
// GetNetworkNodes - gets the nodes of a network
|
|
// GetNetworkNodes - gets the nodes of a network
|
|
func GetNetworkNodes(network string) ([]models.Node, error) {
|
|
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 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 {
|
|
if node.Network == network {
|
|
nodes = append(nodes, node)
|
|
nodes = append(nodes, node)
|
|
}
|
|
}
|
|
@@ -77,7 +66,7 @@ func UncordonNode(nodeid string) (models.Node, error) {
|
|
node.SetLastModified()
|
|
node.SetLastModified()
|
|
node.IsPending = "no"
|
|
node.IsPending = "no"
|
|
node.PullChanges = "yes"
|
|
node.PullChanges = "yes"
|
|
- data, err := json.Marshal(&node)
|
|
|
|
|
|
+ data, err := json.Marshal(&node)
|
|
if err != nil {
|
|
if err != nil {
|
|
return node, err
|
|
return node, err
|
|
}
|
|
}
|
|
@@ -86,52 +75,11 @@ func UncordonNode(nodeid string) (models.Node, error) {
|
|
return node, err
|
|
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) {
|
|
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"
|
|
excludeIsRelayed := node.IsRelay != "yes"
|
|
var relayedNode string
|
|
var relayedNode string
|
|
if node.IsRelayed == "yes" {
|
|
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
|
|
// UpdateNode - takes a node and updates another node with it's values
|
|
func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
|
|
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)
|
|
newNode.Fill(currentNode)
|
|
if err := ValidateNode(newNode, true); err != nil {
|
|
if err := ValidateNode(newNode, true); err != nil {
|
|
return err
|
|
return err
|
|
@@ -210,6 +165,128 @@ func ValidateNode(node *models.Node, isUpdate bool) error {
|
|
return err
|
|
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
|
|
// GetAllNodes - returns all nodes in the DB
|
|
func GetAllNodes() ([]models.Node, error) {
|
|
func GetAllNodes() ([]models.Node, error) {
|
|
var nodes []models.Node
|
|
var nodes []models.Node
|