Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Net-137: Node relays #2401

Merged
merged 10 commits into from
Jun 19, 2023
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