Browse Source

relay server functionality ready to test

afeiszli 3 years ago
parent
commit
cf26b31779
5 changed files with 245 additions and 0 deletions
  1. 2 0
      controllers/nodeHttpController.go
  2. 159 0
      controllers/relay.go
  3. 17 0
      models/node.go
  4. 6 0
      models/structs.go
  5. 61 0
      netclient/server/grpc.go

+ 2 - 0
controllers/nodeHttpController.go

@@ -22,6 +22,8 @@ func nodeHandlers(r *mux.Router) {
 	r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(getNode))).Methods("GET")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(updateNode))).Methods("PUT")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(deleteNode))).Methods("DELETE")
+	r.HandleFunc("/api/nodes/{network}/{macaddress}/createrelay", authorize(true, "user", http.HandlerFunc(createRelay))).Methods("POST")
+	r.HandleFunc("/api/nodes/{network}/{macaddress}/deleterelay", authorize(true, "user", http.HandlerFunc(deleteRelay))).Methods("DELETE")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}/creategateway", authorize(true, "user", http.HandlerFunc(createEgressGateway))).Methods("POST")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}/deletegateway", authorize(true, "user", http.HandlerFunc(deleteEgressGateway))).Methods("DELETE")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}/createingress", securityCheck(false, http.HandlerFunc(createIngressGateway))).Methods("POST")

+ 159 - 0
controllers/relay.go

@@ -0,0 +1,159 @@
+package controller
+
+import (
+	"encoding/json"
+	"errors"
+	"net/http"
+	"github.com/gorilla/mux"
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/models"
+)
+
+func createRelay(w http.ResponseWriter, r *http.Request) {
+	var relay models.RelayRequest
+	var params = mux.Vars(r)
+	w.Header().Set("Content-Type", "application/json")
+	err := json.NewDecoder(r.Body).Decode(&relay)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+	relay.NetID = params["network"]
+	relay.NodeID = params["macaddress"]
+	node, err := CreateRelay(relay)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+	functions.PrintUserLog(r.Header.Get("user"), "created relay on node "+relay.NodeID+" on network "+relay.NetID, 1)
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(node)
+}
+
+func CreateRelay(relay models.RelayRequest) (models.Node, error) {
+	node, err := functions.GetNodeByMacAddress(relay.NetID, relay.NodeID)
+	if node.OS == "windows" { // add in darwin later
+		return models.Node{}, errors.New(node.OS + " is unsupported for relay")
+	}
+	if err != nil {
+		return models.Node{}, err
+	}
+	err = ValidateRelay(relay)
+	if err != nil {
+		return models.Node{}, err
+	}
+	node.IsRelay = "yes"
+	node.RelayAddrs = relay.Addrs
+
+	key, err := functions.GetRecordKey(relay.NodeID, relay.NetID)
+	if err != nil {
+		return node, err
+	}
+	node.SetLastModified()
+	node.PullChanges = "yes"
+	nodeData, err := json.Marshal(&node)
+	if err != nil {
+		return node, err
+	}
+	if err = database.Insert(key, string(nodeData), database.NODES_TABLE_NAME); err != nil {
+		return models.Node{}, err
+	}
+	err = SetNodesDoNotPropagate("yes", node.Network, node.RelayAddrs)
+	if err != nil {
+		return node, err
+	}
+
+	if err = functions.NetworkNodesUpdatePullChanges(node.Network); err != nil {
+		return models.Node{}, err
+	}
+	return node, nil
+}
+
+func SetNodesDoNotPropagate(yesOrno string, networkName string, addrs []string) error {
+
+	collections, err := database.FetchRecords(database.NODES_TABLE_NAME)
+	if err != nil {
+		return err
+	}
+
+	for _, value := range collections {
+
+		var node models.Node
+		err := json.Unmarshal([]byte(value), &node)
+		if err != nil {
+			return err
+		}
+		if node.Network == networkName {
+			for _, addr := range addrs {
+				if addr == node.Address || addr == node.Address6 {
+					node.DoNotPropogate = yesOrno
+					data, err := json.Marshal(&node)
+					if err != nil {
+						return err
+					}
+					node.SetID()
+					database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func ValidateRelay(relay models.RelayRequest) error {
+	var err error
+	//isIp := functions.IsIpCIDR(gateway.RangeString)
+	empty := len(relay.Addrs) == 0
+	if empty {
+		err = errors.New("IP Ranges Cannot Be Empty")
+	}
+	return err
+}
+
+func deleteRelay(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", "application/json")
+	var params = mux.Vars(r)
+	nodeMac := params["macaddress"]
+	netid := params["network"]
+	node, err := DeleteRelay(netid, nodeMac)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+	functions.PrintUserLog(r.Header.Get("user"), "deleted egress gateway "+nodeMac+" on network "+netid, 1)
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(node)
+}
+
+func DeleteRelay(network, macaddress string) (models.Node, error) {
+
+	node, err := functions.GetNodeByMacAddress(network, macaddress)
+	if err != nil {
+		return models.Node{}, err
+	}
+	err = SetNodesDoNotPropagate("yes", node.Network, node.RelayAddrs)
+	if err != nil {
+		return node, err
+	}
+
+	node.IsRelay = "no"
+	node.RelayAddrs = []string{}
+	node.SetLastModified()
+	node.PullChanges = "yes"
+	key, err := functions.GetRecordKey(node.MacAddress, node.Network)
+	if err != nil {
+		return models.Node{}, err
+	}
+	data, err := json.Marshal(&node)
+	if err != nil {
+		return models.Node{}, err
+	}
+	if err = database.Insert(key, string(data), database.NODES_TABLE_NAME); err != nil {
+		return models.Node{}, err
+	}
+	if err = functions.NetworkNodesUpdatePullChanges(network); err != nil {
+		return models.Node{}, err
+	}
+	return node, nil
+}

+ 17 - 0
models/node.go

@@ -52,10 +52,13 @@ type Node struct {
 	CheckInInterval     int32    `json:"checkininterval" bson:"checkininterval" yaml:"checkininterval"`
 	Password            string   `json:"password" bson:"password" yaml:"password" validate:"required,min=6"`
 	Network             string   `json:"network" bson:"network" yaml:"network" validate:"network_exists"`
+	DoNotPropogate      string   `json:"donotpropogate" bson:"donotpropogate" yaml:"donotpropogate"`
 	IsPending           string   `json:"ispending" bson:"ispending" yaml:"ispending"`
+	IsRelay				string   `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
 	IsEgressGateway     string   `json:"isegressgateway" bson:"isegressgateway" yaml:"isegressgateway"`
 	IsIngressGateway    string   `json:"isingressgateway" bson:"isingressgateway" yaml:"isingressgateway"`
 	EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
+	RelayAddrs 			[]string `json:"relayaddrs" bson:"relayaddrs" yaml:"relayaddrs"`
 	IngressGatewayRange string   `json:"ingressgatewayrange" bson:"ingressgatewayrange" yaml:"ingressgatewayrange"`
 	IsStatic            string   `json:"isstatic" bson:"isstatic" yaml:"isstatic" validate:"checkyesorno"`
 	UDPHolePunch        string   `json:"udpholepunch" bson:"udpholepunch" yaml:"udpholepunch" validate:"checkyesorno"`
@@ -84,6 +87,18 @@ func (node *Node) SetDefaulIsPending() {
 	}
 }
 
+func (node *Node) SetDefaultDoNotPropogate() {
+	if node.DoNotPropogate == "" {
+		node.DoNotPropogate = "no"
+	}
+}
+
+func (node *Node) SetDefaultIsRelay() {
+	if node.IsRelay == "" {
+		node.IsRelay = "no"
+	}
+}
+
 func (node *Node) SetDefaultEgressGateway() {
 	if node.IsEgressGateway == "" {
 		node.IsEgressGateway = "no"
@@ -269,6 +284,8 @@ func (node *Node) SetDefaults() {
 	node.SetDefaultIngressGateway()
 	node.SetDefaulIsPending()
 	node.SetDefaultMTU()
+	node.SetDefaultDoNotPropogate()
+	node.SetDefaultIsRelay()
 	node.KeyUpdateTimeStamp = time.Now().Unix()
 }
 

+ 6 - 0
models/structs.go

@@ -128,3 +128,9 @@ type EgressGatewayRequest struct {
 	PostUp      string   `json:"postup" bson:"postup"`
 	PostDown    string   `json:"postdown" bson:"postdown"`
 }
+
+type RelayRequest struct {
+	NodeID      string   `json:"nodeid" bson:"nodeid"`
+	NetID       string   `json:"netid" bson:"netid"`
+	Addrs      []string `json:"addrs" bson:"addrs"`
+}

+ 61 - 0
netclient/server/grpc.go

@@ -19,6 +19,8 @@ import (
 	"google.golang.org/grpc/metadata"
 )
 
+const RELAY_KEEPALIVE_MARKER = "20007ms"
+
 func getGrpcClient(cfg *config.ClientConfig) (nodepb.NodeServiceClient, error) {
 	var wcclient nodepb.NodeServiceClient
 	// == GRPC SETUP ==
@@ -121,7 +123,9 @@ func RemoveNetwork(network string) error {
 func GetPeers(macaddress string, network string, server string, dualstack bool, isIngressGateway bool) ([]wgtypes.PeerConfig, bool, []string, error) {
 	//need to  implement checkin on server side
 	hasGateway := false
+	hasRelay := true
 	var gateways []string
+	var relayAddrs []string
 	var peers []wgtypes.PeerConfig
 	var wcclient nodepb.NodeServiceClient
 	cfg, err := config.ReadConfig(network)
@@ -223,6 +227,18 @@ func GetPeers(macaddress string, network string, server string, dualstack bool,
 				}
 			}
 		}
+		// handle relay servers
+		if node.IsRelay == "yes" {
+			hasRelay = true
+			relayAddrs = node.RelayAddrs
+			for _, ipstring := range node.RelayAddrs { // go through each ip for relay server
+				_, ip, err := net.ParseCIDR(ipstring) // confirming it's a valid IP
+				if ip == nil || err != nil {
+					continue // if can't parse CIDR
+				}
+				allowedips = append(allowedips, *ip)
+			}
+		}
 		if node.Address6 != "" && dualstack {
 			var addr6 = net.IPNet{
 				IP:   net.ParseIP(node.Address6),
@@ -237,6 +253,21 @@ func GetPeers(macaddress string, network string, server string, dualstack bool,
 				ReplaceAllowedIPs:           true,
 				AllowedIPs:                  allowedips,
 			}
+		} else if node.IsRelay == "yes" {
+			relaykeepalive, err := time.ParseDuration(RELAY_KEEPALIVE_MARKER)
+			if err != nil {
+				return nil, hasGateway, gateways, err
+			}
+			peer = wgtypes.PeerConfig{
+				PublicKey:                   pubkey,
+				PersistentKeepaliveInterval: &relaykeepalive,
+				Endpoint: &net.UDPAddr{
+					IP:   net.ParseIP(node.Endpoint),
+					Port: int(node.ListenPort),
+				},
+				ReplaceAllowedIPs: true,
+				AllowedIPs:        allowedips,
+			}
 		} else if keepalive != 0 {
 			peer = wgtypes.PeerConfig{
 				PublicKey:                   pubkey,
@@ -269,9 +300,39 @@ func GetPeers(macaddress string, network string, server string, dualstack bool,
 			log.Println("ERROR RETRIEVING EXTERNAL PEERS",err)
 		}
 	}
+	if hasRelay {
+		peers = RemoveRelayAddrsFromPeers(relayAddrs, peers)
+	}
+
 	return peers, hasGateway, gateways, err
 }
 
+func RemoveRelayAddrsFromPeers(relayAddrs []string, peers []wgtypes.PeerConfig)([]wgtypes.PeerConfig){
+	relayMarker, err := time.ParseDuration(RELAY_KEEPALIVE_MARKER)
+	if err != nil {
+		log.Println(err)
+		log.Println("Could not remove relayed peers. Relay will not be used")
+		return peers
+	}
+	for _, ipstring := range relayAddrs { // go through each ip for relay server
+		_, ip, err := net.ParseCIDR(ipstring) // confirming it's a valid IP
+		if ip == nil || err != nil {
+			continue // if can't parse CIDR
+		}
+		for i, peer := range peers {
+			if *peer.PersistentKeepaliveInterval == relayMarker {
+				continue
+			}
+			for _, nodeip := range peer.AllowedIPs {
+				if ip.Contains(nodeip.IP) {
+					peers = append(peers[:i], peers[i+1:]...)
+				}
+			}
+		}
+	}
+	return peers
+}
+
 func GetExtPeers(macaddress string, network string, server string, dualstack bool) ([]wgtypes.PeerConfig, error) {
 	var peers []wgtypes.PeerConfig
 	var wcclient nodepb.NodeServiceClient