Skip to content

Commit

Permalink
Net-137: Node relays (#2401)
Browse files Browse the repository at this point in the history
* revert relays

* initial relay commit

* get relayed allowed ips

* add more relay validation checks, peer logic cleanup

* rm relayed nodes from relay node when relayed node is deleted

* fix egress updates for relayed nodes

* rm unused func

* remove  debug logs

* avoid adding egress ranges on the relayed gw node

---------

Co-authored-by: Matthew R Kasun <mkasun@nusak.ca>
  • Loading branch information
abhishek9686 and mattkasun authored Jun 19, 2023
1 parent 20998dd commit 6c25826
Show file tree
Hide file tree
Showing 21 changed files with 399 additions and 649 deletions.
22 changes: 0 additions & 22 deletions cli/cmd/host/create_relay.go

This file was deleted.

22 changes: 22 additions & 0 deletions cli/cmd/node/create_relay.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package node

import (
"strings"

"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)

var hostCreateRelayCmd = &cobra.Command{
Use: "create_relay [NETWORK][NODE ID] [RELAYED NODE IDS (comma separated)]",
Args: cobra.ExactArgs(3),
Short: "Turn a Node into a Relay",
Long: `Turn a Node into a Relay`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.CreateRelay(args[0], args[1], strings.Split(args[2], ",")))
},
}

func init() {
rootCmd.AddCommand(hostCreateRelayCmd)
}
12 changes: 6 additions & 6 deletions cli/cmd/host/delete_relay.go → cli/cmd/node/delete_relay.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package host
package node

import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)

var hostDeleteRelayCmd = &cobra.Command{
Use: "delete_relay [HOST ID]",
Args: cobra.ExactArgs(1),
Short: "Delete Relay role from a host",
Long: `Delete Relay role from a host`,
Use: "delete_relay [NETWORK] [NODE ID]",
Args: cobra.ExactArgs(2),
Short: "Delete Relay from a node",
Long: `Delete Relay from a node`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.DeleteRelay(args[0]))
functions.PrettyPrint(functions.DeleteRelay(args[0], args[1]))
},
}

Expand Down
2 changes: 1 addition & 1 deletion cli/cmd/node/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var (
postUp string
postDown string
keepAlive int
relayAddrs string
relayedNodes string
egressGatewayRanges string
expirationDateTime int
defaultACL bool
Expand Down
6 changes: 3 additions & 3 deletions cli/cmd/node/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ var nodeUpdateCmd = &cobra.Command{
node.Address6 = address6
node.LocalAddress = localAddress
node.PersistentKeepalive = int32(keepAlive)
if relayAddrs != "" {
node.RelayAddrs = strings.Split(relayAddrs, ",")
if relayedNodes != "" {
node.RelayedNodes = strings.Split(relayedNodes, ",")
}
if egressGatewayRanges != "" {
node.EgressGatewayRanges = strings.Split(egressGatewayRanges, ",")
Expand All @@ -62,7 +62,7 @@ func init() {
nodeUpdateCmd.Flags().StringVar(&postUp, "post_up", "", "Commands to run after node is up `;` separated")
nodeUpdateCmd.Flags().StringVar(&postDown, "post_down", "", "Commands to run after node is down `;` separated")
nodeUpdateCmd.Flags().IntVar(&keepAlive, "keep_alive", 0, "Interval in which packets are sent to keep connections open with peers")
nodeUpdateCmd.Flags().StringVar(&relayAddrs, "relay_addrs", "", "Addresses for relaying connections if node acts as a relay")
nodeUpdateCmd.Flags().StringVar(&relayedNodes, "relayed_nodes", "", "relayed nodes if node acts as a relay")
nodeUpdateCmd.Flags().StringVar(&egressGatewayRanges, "egress_addrs", "", "Addresses for egressing traffic if node acts as an egress")
nodeUpdateCmd.Flags().IntVar(&expirationDateTime, "expiry", 0, "UNIX timestamp after which node will lose access to the network")
nodeUpdateCmd.Flags().BoolVar(&defaultACL, "acl", false, "Enable default ACL ?")
Expand Down
17 changes: 9 additions & 8 deletions cli/functions/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,18 @@ func DeleteHostFromNetwork(hostID, network string) *hostNetworksUpdatePayload {
return request[hostNetworksUpdatePayload](http.MethodDelete, "/api/hosts/"+hostID+"/networks/"+network, nil)
}

// CreateRelay - turn a host into a relay
func CreateRelay(hostID string, relayedHosts []string) *models.ApiHost {
return request[models.ApiHost](http.MethodPost, fmt.Sprintf("/api/hosts/%s/relay", hostID), &models.HostRelayRequest{
HostID: hostID,
RelayedHosts: relayedHosts,
// CreateRelay - add relay to a node
func CreateRelay(netID, nodeID string, relayedNodes []string) *models.ApiNode {
return request[models.ApiNode](http.MethodPost, fmt.Sprintf("/api/nodes/%s/%s/createrelay", netID, nodeID), &models.RelayRequest{
NodeID: nodeID,
NetID: netID,
RelayedNodes: relayedNodes,
})
}

// DeleteRelay - remove relay role from a host
func DeleteRelay(hostID string) *models.ApiHost {
return request[models.ApiHost](http.MethodDelete, fmt.Sprintf("/api/hosts/%s/relay", hostID), nil)
// DeleteRelay - remove relay from a node
func DeleteRelay(netID, nodeID string) *models.ApiNode {
return request[models.ApiNode](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s/deleterelay", netID, nodeID), nil)
}

// RefreshKeys - refresh wireguard keys
Expand Down
44 changes: 2 additions & 42 deletions controllers/hosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"net/http"
"reflect"

"github.com/gorilla/mux"
"github.com/gravitl/netmaker/logger"
Expand All @@ -26,11 +25,9 @@ func hostHandlers(r *mux.Router) {
r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(deleteHost))).Methods(http.MethodDelete)
r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(addHostToNetwork))).Methods(http.MethodPost)
r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(deleteHostFromNetwork))).Methods(http.MethodDelete)
r.HandleFunc("/api/hosts/{hostid}/relay", logic.SecurityCheck(false, http.HandlerFunc(createHostRelay))).Methods(http.MethodPost)
r.HandleFunc("/api/hosts/{hostid}/relay", logic.SecurityCheck(false, http.HandlerFunc(deleteHostRelay))).Methods(http.MethodDelete)
r.HandleFunc("/api/hosts/adm/authenticate", authenticateHost).Methods(http.MethodPost)
r.HandleFunc("/api/v1/host", authorize(true, false, "host", http.HandlerFunc(pull))).Methods(http.MethodGet)
r.HandleFunc("/api/v1/host/{hostid}/signalpeer", authorize(true, false, "host", http.HandlerFunc(signalPeer))).Methods(http.MethodPost)
r.HandleFunc("/api/v1/host", Authorize(true, false, "host", http.HandlerFunc(pull))).Methods(http.MethodGet)
r.HandleFunc("/api/v1/host/{hostid}/signalpeer", Authorize(true, false, "host", http.HandlerFunc(signalPeer))).Methods(http.MethodPost)
r.HandleFunc("/api/v1/auth-register/host", socketHandler)
}

Expand Down Expand Up @@ -174,23 +171,13 @@ func updateHost(w http.ResponseWriter, r *http.Request) {
}

newHost := newHostData.ConvertAPIHostToNMHost(currHost)
// check if relay information is changed
updateRelay := false
if newHost.IsRelay && len(newHost.RelayedHosts) > 0 {
if len(newHost.RelayedHosts) != len(currHost.RelayedHosts) || !reflect.DeepEqual(newHost.RelayedHosts, currHost.RelayedHosts) {
updateRelay = true
}
}

logic.UpdateHost(newHost, currHost) // update the in memory struct values
if err = logic.UpsertHost(newHost); err != nil {
logger.Log(0, r.Header.Get("user"), "failed to update a host:", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
if updateRelay {
logic.UpdateHostRelay(currHost.ID.String(), currHost.RelayedHosts, newHost.RelayedHosts)
}
// publish host update through MQ
if err := mq.HostUpdate(&models.HostUpdate{
Action: models.UpdateHost,
Expand Down Expand Up @@ -244,33 +231,6 @@ func deleteHost(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
if currHost.IsRelay {
if _, _, err := logic.DeleteHostRelay(hostid); err != nil {
logger.Log(0, r.Header.Get("user"), "failed to dissociate host from relays:", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
}
if currHost.IsRelayed {
relayHost, err := logic.GetHost(currHost.RelayedBy)
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to fetch relay host:", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
newRelayedHosts := make([]string, 0)
for _, relayedHostID := range relayHost.RelayedHosts {
if relayedHostID != hostid {
newRelayedHosts = append(newRelayedHosts, relayedHostID)
}
}
relayHost.RelayedHosts = newRelayedHosts
if err := logic.UpsertHost(relayHost); err != nil {
logger.Log(0, r.Header.Get("user"), "failed to update host relays:", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
}
if err = logic.RemoveHost(currHost); err != nil {
logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
Expand Down
83 changes: 33 additions & 50 deletions controllers/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,16 @@ var hostIDHeader = "host-id"

func nodeHandlers(r *mux.Router) {

r.HandleFunc("/api/nodes", authorize(false, false, "user", http.HandlerFunc(getAllNodes))).Methods(http.MethodGet)
r.HandleFunc("/api/nodes/{network}", authorize(false, true, "network", http.HandlerFunc(getNetworkNodes))).Methods(http.MethodGet)
r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(getNode))).Methods(http.MethodGet)
r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(false, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPut)
r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", authorize(false, true, "user", http.HandlerFunc(createRelay))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", authorize(false, true, "user", http.HandlerFunc(deleteRelay))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", authorize(false, true, "user", http.HandlerFunc(createEgressGateway))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", authorize(false, true, "user", http.HandlerFunc(deleteEgressGateway))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes", Authorize(false, false, "user", http.HandlerFunc(getAllNodes))).Methods(http.MethodGet)
r.HandleFunc("/api/nodes/{network}", Authorize(false, true, "network", http.HandlerFunc(getNetworkNodes))).Methods(http.MethodGet)
r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(getNode))).Methods(http.MethodGet)
r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(false, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPut)
r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", Authorize(false, true, "user", http.HandlerFunc(createEgressGateway))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", Authorize(false, true, "user", http.HandlerFunc(deleteEgressGateway))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(false, http.HandlerFunc(createIngressGateway))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost)
r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost)
}
Expand Down Expand Up @@ -154,7 +152,7 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
// even if it's technically ok
// This is kind of a poor man's RBAC. There's probably a better/smarter way.
// TODO: Consider better RBAC implementations
func authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Handler) http.HandlerFunc {
func Authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var errorResponse = models.ErrorResponse{
Code: http.StatusForbidden, Message: logic.Forbidden_Msg,
Expand Down Expand Up @@ -633,12 +631,12 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
}
newNode := newData.ConvertToServerNode(&currentNode)
relayupdate := false
if currentNode.IsRelay && len(newNode.RelayAddrs) > 0 {
if len(newNode.RelayAddrs) != len(currentNode.RelayAddrs) {
if servercfg.Is_EE && newNode.IsRelay && len(newNode.RelayedNodes) > 0 {
if len(newNode.RelayedNodes) != len(currentNode.RelayedNodes) {
relayupdate = true
} else {
for i, addr := range newNode.RelayAddrs {
if addr != currentNode.RelayAddrs[i] {
for i, node := range newNode.RelayedNodes {
if node != currentNode.RelayedNodes[i] {
relayupdate = true
}
}
Expand All @@ -651,10 +649,6 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
relayedUpdate := false
if currentNode.IsRelayed && (currentNode.Address.String() != newNode.Address.String() || currentNode.Address6.String() != newNode.Address6.String()) {
relayedUpdate = true
}
ifaceDelta := logic.IfaceDelta(&currentNode, newNode)
aclUpdate := currentNode.DefaultACL != newNode.DefaultACL
if ifaceDelta && servercfg.Is_EE {
Expand All @@ -671,16 +665,13 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
return
}
if relayupdate {
updatenodes := logic.UpdateRelay(currentNode.Network, currentNode.RelayAddrs, newNode.RelayAddrs)
updatenodes := logic.UpdateRelayed(currentNode.ID.String(), currentNode.RelayedNodes, newNode.RelayedNodes)
if len(updatenodes) > 0 {
for _, relayedNode := range updatenodes {
runUpdates(&relayedNode, false)
}
}
}
if relayedUpdate {
updateRelay(&currentNode, newNode)
}
if servercfg.IsDNSMode() {
logic.SetDNS()
}
Expand All @@ -690,16 +681,16 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
runUpdates(newNode, ifaceDelta)
go func(aclUpdate bool, newNode *models.Node) {
if aclUpdate {
go func(aclUpdate, relayupdate bool, newNode *models.Node) {
if aclUpdate || relayupdate {
if err := mq.PublishPeerUpdate(); err != nil {
logger.Log(0, "error during node ACL update for node", newNode.ID.String())
}
}
if err := mq.PublishReplaceDNS(&currentNode, newNode, host); err != nil {
logger.Log(1, "failed to publish dns update", err.Error())
}
}(aclUpdate, newNode)
}(aclUpdate, relayupdate, newNode)
}

// swagger:route DELETE /api/nodes/{network}/{nodeid} nodes deleteNode
Expand Down Expand Up @@ -743,6 +734,22 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal"))
return
}
if node.IsRelayed {
// cleanup node from relayednodes on relay node
relayNode, err := logic.GetNodeByID(node.RelayedBy)
if err == nil {
relayedNodes := []string{}
for _, relayedNodeID := range relayNode.RelayedNodes {
if relayedNodeID == node.ID.String() {
continue
}
relayedNodes = append(relayedNodes, relayedNodeID)
}
relayNode.RelayedNodes = relayedNodes
logic.UpsertNode(&relayNode)
}

}
logic.ReturnSuccessResponse(w, r, nodeid+" deleted.")
logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
if !fromNode { // notify node change
Expand Down Expand Up @@ -778,30 +785,6 @@ func runUpdates(node *models.Node, ifaceDelta bool) {
}()
}

func updateRelay(oldnode, newnode *models.Node) {
relay := logic.FindRelay(oldnode)
newrelay := relay
//check if node's address has been updated and if so, update the relayAddrs of the relay node with the updated address of the relayed node
if oldnode.Address.String() != newnode.Address.String() {
for i, ip := range newrelay.RelayAddrs {
if ip == oldnode.Address.IP.String() {
newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], relay.RelayAddrs[i+1:]...)
newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address.IP.String())
}
}
}
//check if node's address(v6) has been updated and if so, update the relayAddrs of the relay node with the updated address(v6) of the relayed node
if oldnode.Address6.String() != newnode.Address6.String() {
for i, ip := range newrelay.RelayAddrs {
if ip == oldnode.Address.IP.String() {
newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], newrelay.RelayAddrs[i+1:]...)
newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address6.IP.String())
}
}
}
logic.UpdateNode(relay, newrelay)
}

func doesUserOwnNode(username, network, nodeID string) bool {
u, err := logic.GetUser(username)
if err != nil {
Expand Down
Loading

0 comments on commit 6c25826

Please sign in to comment.