diff --git a/controllers/controller.go b/controllers/controller.go index 317536dd..93e8bba3 100644 --- a/controllers/controller.go +++ b/controllers/controller.go @@ -24,6 +24,7 @@ var HttpMiddlewares = []mux.MiddlewareFunc{ // HttpHandlers - handler functions for REST interactions var HttpHandlers = []interface{}{ nodeHandlers, + gwHandlers, userHandlers, networkHandlers, dnsHandlers, diff --git a/controllers/gateway.go b/controllers/gateway.go new file mode 100644 index 00000000..4a25f33e --- /dev/null +++ b/controllers/gateway.go @@ -0,0 +1,207 @@ +package controller + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/google/uuid" + "github.com/gorilla/mux" + "github.com/gravitl/netmaker/logger" + "github.com/gravitl/netmaker/logic" + "github.com/gravitl/netmaker/models" + "github.com/gravitl/netmaker/mq" + "github.com/gravitl/netmaker/servercfg" + "golang.org/x/exp/slog" +) + +func gwHandlers(r *mux.Router) { + r.HandleFunc("/api/nodes/{network}/{nodeid}/gateway", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceIngress, http.HandlerFunc(createGateway)))).Methods(http.MethodPost) + r.HandleFunc("/api/nodes/{network}/{nodeid}/gateway", logic.SecurityCheck(true, http.HandlerFunc(deleteGateway))).Methods(http.MethodDelete) + // old relay handlers + r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", logic.SecurityCheck(true, http.HandlerFunc(createGateway))).Methods(http.MethodPost) + r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", logic.SecurityCheck(true, http.HandlerFunc(deleteGateway))).Methods(http.MethodDelete) +} + +// @Summary Create a gateway +// @Router /api/nodes/{network}/{nodeid}/gateway [post] +// @Tags Nodes +// @Security oauth2 +// @Success 200 {object} models.ApiNode +// @Failure 500 {object} models.ErrorResponse +func createGateway(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + var params = mux.Vars(r) + nodeid := params["nodeid"] + netid := params["network"] + node, err := logic.ValidateParams(nodeid, netid) + if err != nil { + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) + return + } + var req models.CreateGwReq + err = json.NewDecoder(r.Body).Decode(&req) + if err != nil { + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) + return + } + node, err = logic.CreateIngressGateway(netid, nodeid, req.IngressRequest) + if err != nil { + logger.Log(0, r.Header.Get("user"), + fmt.Sprintf("failed to create gateway on node [%s] on network [%s]: %v", + nodeid, netid, err)) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + req.RelayRequest.NetID = netid + req.RelayRequest.NodeID = nodeid + _, relayNode, err := logic.CreateRelay(req.RelayRequest) + if err != nil { + logger.Log( + 0, + r.Header.Get("user"), + fmt.Sprintf( + "failed to create relay on node [%s] on network [%s]: %v", + req.RelayRequest.NodeID, + req.RelayRequest.NetID, + err, + ), + ) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + for _, relayedNodeID := range relayNode.RelayedNodes { + relayedNode, err := logic.GetNodeByID(relayedNodeID) + if err == nil { + if relayedNode.FailedOverBy != uuid.Nil { + go logic.ResetFailedOverPeer(&relayedNode) + } + + } + } + logger.Log( + 1, + r.Header.Get("user"), + "created gw node", + req.RelayRequest.NodeID, + "on network", + req.RelayRequest.NetID, + ) + apiNode := relayNode.ConvertToAPINode() + + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(apiNode) + go func() { + if err := mq.NodeUpdate(&node); err != nil { + slog.Error("error publishing node update to node", "node", node.ID, "error", err) + } + mq.PublishPeerUpdate(false) + }() + +} + +// @Summary Delete a gateway +// @Router /api/nodes/{network}/{nodeid}/gateway [delete] +// @Tags Nodes +// @Security oauth2 +// @Success 200 {object} models.ApiNode +// @Failure 500 {object} models.ErrorResponse +func deleteGateway(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + var params = mux.Vars(r) + nodeid := params["nodeid"] + netid := params["network"] + node, err := logic.ValidateParams(nodeid, netid) + if err != nil { + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) + return + } + node, removedClients, err := logic.DeleteIngressGateway(nodeid) + if err != nil { + logger.Log(0, r.Header.Get("user"), + fmt.Sprintf("failed to delete ingress gateway on node [%s] on network [%s]: %v", + nodeid, netid, err)) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + + updateNodes, node, err := logic.DeleteRelay(netid, nodeid) + if err != nil { + logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + node, err = logic.GetNodeByID(node.ID.String()) + if err != nil { + logger.Log(0, r.Header.Get("user"), "failed to get node", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + node.IsGw = false + logic.UpsertNode(&node) + logger.Log(1, r.Header.Get("user"), "deleted gw", nodeid, "on network", netid) + + go func() { + host, err := logic.GetHost(node.HostID.String()) + if err == nil { + allNodes, err := logic.GetAllNodes() + if err != nil { + return + } + + for _, relayedNode := range updateNodes { + err = mq.NodeUpdate(&relayedNode) + if err != nil { + logger.Log( + 1, + "relayed node update ", + relayedNode.ID.String(), + "on network", + relayedNode.Network, + ": ", + err.Error(), + ) + + } + h, err := logic.GetHost(relayedNode.HostID.String()) + if err == nil { + if h.OS == models.OS_Types.IoT { + nodes, err := logic.GetAllNodes() + if err != nil { + return + } + node.IsRelay = true // for iot update to recognise that it has to delete relay peer + if err = mq.PublishSingleHostPeerUpdate(h, nodes, &node, nil, false, nil); err != nil { + logger.Log(1, "failed to publish peer update to host", h.ID.String(), ": ", err.Error()) + } + } + } + } + if len(removedClients) > 0 { + if err := mq.PublishSingleHostPeerUpdate(host, allNodes, nil, removedClients[:], false, nil); err != nil { + slog.Error("publishSingleHostUpdate", "host", host.Name, "error", err) + } + } + mq.PublishPeerUpdate(false) + if err := mq.NodeUpdate(&node); err != nil { + slog.Error( + "error publishing node update to node", + "node", + node.ID, + "error", + err, + ) + } + if servercfg.IsDNSMode() { + logic.SetDNS() + } + + } + + }() + + apiNode := node.ConvertToAPINode() + logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid) + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(apiNode) +} diff --git a/controllers/node.go b/controllers/node.go index 904f6375..0bb6c13e 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -28,8 +28,8 @@ func nodeHandlers(r *mux.Router) { r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods(http.MethodDelete) r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceEgress, http.HandlerFunc(createEgressGateway)))).Methods(http.MethodPost) r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", logic.SecurityCheck(true, http.HandlerFunc(deleteEgressGateway))).Methods(http.MethodDelete) - r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceIngress, http.HandlerFunc(createIngressGateway)))).Methods(http.MethodPost) - r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(true, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete) + r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceIngress, http.HandlerFunc(createGateway)))).Methods(http.MethodPost) + r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(true, http.HandlerFunc(deleteGateway))).Methods(http.MethodDelete) r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost) r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost) } @@ -548,113 +548,6 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) { }() } -// == INGRESS == - -// @Summary Create an remote access gateway -// @Router /api/nodes/{network}/{nodeid}/createingress [post] -// @Tags Nodes -// @Security oauth2 -// @Success 200 {object} models.ApiNode -// @Failure 500 {object} models.ErrorResponse -func createIngressGateway(w http.ResponseWriter, r *http.Request) { - var params = mux.Vars(r) - w.Header().Set("Content-Type", "application/json") - nodeid := params["nodeid"] - netid := params["network"] - node, err := logic.ValidateParams(nodeid, netid) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) - return - } - var request models.IngressRequest - json.NewDecoder(r.Body).Decode(&request) - node, err = logic.CreateIngressGateway(netid, nodeid, request) - if err != nil { - logger.Log(0, r.Header.Get("user"), - fmt.Sprintf("failed to create ingress gateway on node [%s] on network [%s]: %v", - nodeid, netid, err)) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - - apiNode := node.ConvertToAPINode() - logger.Log( - 1, - r.Header.Get("user"), - "created ingress gateway on node", - nodeid, - "on network", - netid, - ) - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(apiNode) - go func() { - if err := mq.NodeUpdate(&node); err != nil { - slog.Error("error publishing node update to node", "node", node.ID, "error", err) - } - mq.PublishPeerUpdate(false) - }() -} - -// @Summary Delete an remote access gateway -// @Router /api/nodes/{network}/{nodeid}/deleteingress [delete] -// @Tags Nodes -// @Security oauth2 -// @Success 200 {object} models.ApiNode -// @Failure 500 {object} models.ErrorResponse -func deleteIngressGateway(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - var params = mux.Vars(r) - nodeid := params["nodeid"] - netid := params["network"] - node, err := logic.ValidateParams(nodeid, netid) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) - return - } - node, removedClients, err := logic.DeleteIngressGateway(nodeid) - if err != nil { - logger.Log(0, r.Header.Get("user"), - fmt.Sprintf("failed to delete ingress gateway on node [%s] on network [%s]: %v", - nodeid, netid, err)) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - - apiNode := node.ConvertToAPINode() - logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid) - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(apiNode) - - if len(removedClients) > 0 { - host, err := logic.GetHost(node.HostID.String()) - if err == nil { - allNodes, err := logic.GetAllNodes() - if err != nil { - return - } - go func() { - if err := mq.PublishSingleHostPeerUpdate(host, allNodes, nil, removedClients[:], false, nil); err != nil { - slog.Error("publishSingleHostUpdate", "host", host.Name, "error", err) - } - mq.PublishPeerUpdate(false) - if err := mq.NodeUpdate(&node); err != nil { - slog.Error( - "error publishing node update to node", - "node", - node.ID, - "error", - err, - ) - } - if servercfg.IsDNSMode() { - logic.SetDNS() - } - }() - } - } -} - // @Summary Update an individual node // @Router /api/nodes/{network}/{nodeid} [put] // @Tags Nodes diff --git a/logic/gateway.go b/logic/gateway.go index a92682c5..3b96fc39 100644 --- a/logic/gateway.go +++ b/logic/gateway.go @@ -141,14 +141,14 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq return models.Node{}, err } if node.IsRelayed { - return models.Node{}, errors.New("ingress cannot be created on a relayed node") + return models.Node{}, errors.New("gateway cannot be created on a relayed node") } host, err := GetHost(node.HostID.String()) if err != nil { return models.Node{}, err } if host.OS != "linux" { - return models.Node{}, errors.New("ingress can only be created on linux based node") + return models.Node{}, errors.New("gateway can only be created on linux based node") } network, err := GetParentNetwork(netid) @@ -156,12 +156,16 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq return models.Node{}, err } node.IsIngressGateway = true + node.IsGw = true if !servercfg.IsPro { node.IsInternetGateway = ingress.IsInternetGateway } node.IngressGatewayRange = network.AddressRange node.IngressGatewayRange6 = network.AddressRange6 node.IngressDNS = ingress.ExtclientDNS + if node.IsInternetGateway && node.IngressDNS == "" { + node.IngressDNS = "1.1.1.1" + } node.IngressPersistentKeepalive = 20 if ingress.PersistentKeepalive != 0 { node.IngressPersistentKeepalive = ingress.PersistentKeepalive diff --git a/logic/peers.go b/logic/peers.go index b665e51f..f5d548ba 100644 --- a/logic/peers.go +++ b/logic/peers.go @@ -222,21 +222,19 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, getExtpeersExtraRoutes(node)...) } _, isFailOverPeer := node.FailOverPeers[peer.ID.String()] - if servercfg.IsPro { - if (node.IsRelayed && node.RelayedBy != peer.ID.String()) || - (peer.IsRelayed && peer.RelayedBy != node.ID.String()) || isFailOverPeer { - // if node is relayed and peer is not the relay, set remove to true - if _, ok := peerIndexMap[peerHost.PublicKey.String()]; ok { - continue - } - peerConfig.Remove = true - hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig) - peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1 + if (node.IsRelayed && node.RelayedBy != peer.ID.String()) || + (peer.IsRelayed && peer.RelayedBy != node.ID.String()) || isFailOverPeer { + // if node is relayed and peer is not the relay, set remove to true + if _, ok := peerIndexMap[peerHost.PublicKey.String()]; ok { continue } - if node.IsRelayed && node.RelayedBy == peer.ID.String() { - hostPeerUpdate = SetDefaultGwForRelayedUpdate(node, peer, hostPeerUpdate) - } + peerConfig.Remove = true + hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig) + peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1 + continue + } + if node.IsRelayed && node.RelayedBy == peer.ID.String() { + hostPeerUpdate = SetDefaultGwForRelayedUpdate(node, peer, hostPeerUpdate) } uselocal := false diff --git a/logic/pro/failover b/logic/pro/failover new file mode 100644 index 00000000..e69de29b diff --git a/logic/relay.go b/logic/relay.go index bd3c80bb..94262cc5 100644 --- a/logic/relay.go +++ b/logic/relay.go @@ -1,34 +1,246 @@ package logic import ( + "errors" + "fmt" "net" + "github.com/google/uuid" + "github.com/gravitl/netmaker/logger" + "github.com/gravitl/netmaker/logic/acls/nodeacls" "github.com/gravitl/netmaker/models" ) -var GetRelays = func() ([]models.Node, error) { - return []models.Node{}, nil +// GetRelays - gets all the nodes that are relays +func GetRelays() ([]models.Node, error) { + nodes, err := GetAllNodes() + if err != nil { + return nil, err + } + relays := make([]models.Node, 0) + for _, node := range nodes { + if node.IsRelay { + relays = append(relays, node) + } + } + return relays, nil } -var RelayedAllowedIPs = func(peer, node *models.Node) []net.IPNet { - return []net.IPNet{} +// CreateRelay - creates a relay +func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error) { + var returnnodes []models.Node + + node, err := GetNodeByID(relay.NodeID) + if err != nil { + return returnnodes, models.Node{}, err + } + host, err := GetHost(node.HostID.String()) + if err != nil { + return returnnodes, models.Node{}, err + } + if host.OS != "linux" { + return returnnodes, models.Node{}, fmt.Errorf("only linux machines can be gateway nodes") + } + err = ValidateRelay(relay, false) + if err != nil { + return returnnodes, models.Node{}, err + } + node.IsRelay = true + node.IsGw = true + node.RelayedNodes = relay.RelayedNodes + node.SetLastModified() + err = UpsertNode(&node) + if err != nil { + return returnnodes, node, err + } + returnnodes = SetRelayedNodes(true, relay.NodeID, relay.RelayedNodes) + return returnnodes, node, nil } -var GetAllowedIpsForRelayed = func(relayed, relay *models.Node) []net.IPNet { - return []net.IPNet{} +// SetRelayedNodes- sets and saves node as relayed +func SetRelayedNodes(setRelayed bool, relay string, relayed []string) []models.Node { + var returnnodes []models.Node + for _, id := range relayed { + node, err := GetNodeByID(id) + if err != nil { + logger.Log(0, "setRelayedNodes.GetNodebyID", err.Error()) + continue + } + node.IsRelayed = setRelayed + if setRelayed { + node.RelayedBy = relay + } else { + node.RelayedBy = "" + } + node.SetLastModified() + if err := UpsertNode(&node); err != nil { + logger.Log(0, "setRelayedNodes.Insert", err.Error()) + continue + } + returnnodes = append(returnnodes, node) + } + return returnnodes } -var UpdateRelayed = func(currentNode, newNode *models.Node) { +// func GetRelayedNodes(relayNode *models.Node) (models.Node, error) { +// var returnnodes []models.Node +// networkNodes, err := GetNetworkNodes(relayNode.Network) +// if err != nil { +// return returnnodes, err +// } +// for _, node := range networkNodes { +// for _, addr := range relayNode.RelayAddrs { +// if addr == node.Address.IP.String() || addr == node.Address6.IP.String() { +// returnnodes = append(returnnodes, node) +// } +// } +// } +// return returnnodes, nil +// } + +// ValidateRelay - checks if relay is valid +func ValidateRelay(relay models.RelayRequest, update bool) error { + var err error + + node, err := GetNodeByID(relay.NodeID) + if err != nil { + return err + } + if !update && node.IsRelay { + return errors.New("node is already acting as a relay") + } + for _, relayedNodeID := range relay.RelayedNodes { + relayedNode, err := GetNodeByID(relayedNodeID) + if err != nil { + return err + } + if relayedNode.IsIngressGateway { + return errors.New("cannot relay an ingress gateway (" + relayedNodeID + ")") + } + if relayedNode.IsInternetGateway { + return errors.New("cannot relay an internet gateway (" + relayedNodeID + ")") + } + if relayedNode.InternetGwID != "" && relayedNode.InternetGwID != relay.NodeID { + return errors.New("cannot relay an internet client (" + relayedNodeID + ")") + } + if relayedNode.IsFailOver { + return errors.New("cannot relay a failOver (" + relayedNodeID + ")") + } + if relayedNode.FailedOverBy != uuid.Nil { + ResetFailedOverPeer(&relayedNode) + } + } + return err } -var SetRelayedNodes = func(setRelayed bool, relay string, relayed []string) []models.Node { - return []models.Node{} +// UpdateRelayNodes - updates relay nodes +func updateRelayNodes(relay string, oldNodes []string, newNodes []string) []models.Node { + _ = SetRelayedNodes(false, relay, oldNodes) + return SetRelayedNodes(true, relay, newNodes) } -var RelayUpdates = func(currentNode, newNode *models.Node) bool { - return false +func RelayUpdates(currentNode, newNode *models.Node) bool { + relayUpdates := false + if newNode.IsRelay { + if len(newNode.RelayedNodes) != len(currentNode.RelayedNodes) { + relayUpdates = true + } else { + for i, node := range newNode.RelayedNodes { + if node != currentNode.RelayedNodes[i] { + relayUpdates = true + } + } + } + } + return relayUpdates } -var ValidateRelay = func(relay models.RelayRequest, update bool) error { - return nil +// UpdateRelayed - updates a relay's relayed nodes, and sends updates to the relayed nodes over MQ +func UpdateRelayed(currentNode, newNode *models.Node) { + updatenodes := updateRelayNodes(currentNode.ID.String(), currentNode.RelayedNodes, newNode.RelayedNodes) + if len(updatenodes) > 0 { + for _, relayedNode := range updatenodes { + node := relayedNode + ResetFailedOverPeer(&node) + } + } +} + +// DeleteRelay - deletes a relay +func DeleteRelay(network, nodeid string) ([]models.Node, models.Node, error) { + var returnnodes []models.Node + node, err := GetNodeByID(nodeid) + if err != nil { + return returnnodes, models.Node{}, err + } + returnnodes = SetRelayedNodes(false, nodeid, node.RelayedNodes) + node.IsRelay = false + node.RelayedNodes = []string{} + node.SetLastModified() + if err = UpsertNode(&node); err != nil { + return returnnodes, models.Node{}, err + } + return returnnodes, node, nil +} + +func RelayedAllowedIPs(peer, node *models.Node) []net.IPNet { + var allowedIPs = []net.IPNet{} + for _, relayedNodeID := range peer.RelayedNodes { + if node.ID.String() == relayedNodeID { + continue + } + relayedNode, err := GetNodeByID(relayedNodeID) + if err != nil { + continue + } + allowed := getRelayedAddresses(relayedNodeID) + if relayedNode.IsEgressGateway { + allowed = append(allowed, GetEgressIPs(&relayedNode)...) + } + allowedIPs = append(allowedIPs, allowed...) + } + return allowedIPs +} + +// GetAllowedIpsForRelayed - returns the peerConfig for a node relayed by relay +func GetAllowedIpsForRelayed(relayed, relay *models.Node) (allowedIPs []net.IPNet) { + if relayed.RelayedBy != relay.ID.String() { + logger.Log(0, "RelayedByRelay called with invalid parameters") + return + } + if relay.InternetGwID != "" { + return GetAllowedIpForInetNodeClient(relayed, relay) + } + peers, err := GetNetworkNodes(relay.Network) + if err != nil { + logger.Log(0, "error getting network clients", err.Error()) + return + } + for _, peer := range peers { + if peer.ID == relayed.ID || peer.ID == relay.ID { + continue + } + if nodeacls.AreNodesAllowed(nodeacls.NetworkID(relayed.Network), nodeacls.NodeID(relayed.ID.String()), nodeacls.NodeID(peer.ID.String())) { + allowedIPs = append(allowedIPs, GetAllowedIPs(relayed, &peer, nil)...) + } + } + return +} + +func getRelayedAddresses(id string) []net.IPNet { + addrs := []net.IPNet{} + node, err := GetNodeByID(id) + if err != nil { + logger.Log(0, "getRelayedAddresses: "+err.Error()) + return addrs + } + if node.Address.IP != nil { + node.Address.Mask = net.CIDRMask(32, 32) + addrs = append(addrs, node.Address) + } + if node.Address6.IP != nil { + node.Address6.Mask = net.CIDRMask(128, 128) + addrs = append(addrs, node.Address6) + } + return addrs } diff --git a/migrate/migrate.go b/migrate/migrate.go index 824deeca..24445f25 100644 --- a/migrate/migrate.go +++ b/migrate/migrate.go @@ -28,6 +28,7 @@ func Run() { updateHosts() updateNodes() updateAcls() + migrateToGws() } func assignSuperAdmin() { @@ -441,3 +442,18 @@ func createDefaultTagsAndPolicies() { } logic.MigrateAclPolicies() } + +func migrateToGws() { + nodes, err := logic.GetAllNodes() + if err != nil { + return + } + for _, node := range nodes { + if node.IsIngressGateway || node.IsRelay { + node.IsGw = true + node.IsIngressGateway = true + node.IsRelay = true + logic.UpsertNode(&node) + } + } +} diff --git a/models/gateway.go b/models/gateway.go new file mode 100644 index 00000000..6750c900 --- /dev/null +++ b/models/gateway.go @@ -0,0 +1,9 @@ +package models + +type CreateGwReq struct { + IngressRequest + RelayRequest +} + +type DeleteGw struct { +} diff --git a/models/node.go b/models/node.go index b97b35c2..48243540 100644 --- a/models/node.go +++ b/models/node.go @@ -78,11 +78,12 @@ type CommonNode struct { Action string `json:"action" yaml:"action"` LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"` IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"` - EgressGatewayRanges []string `json:"egressgatewayranges" yaml:"egressgatewayranges" bson:"egressgatewayranges"` + EgressGatewayRanges []string `json:"egressgatewayranges" yaml:"egressgatewayranges"` IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"` - IsRelayed bool `json:"isrelayed" yaml:"isrelayed" bson:"isrelayed"` - RelayedBy string `json:"relayedby" yaml:"relayedby" bson:"relayedby"` - IsRelay bool `json:"isrelay" yaml:"isrelay" bson:"isrelay"` + IsRelayed bool `json:"isrelayed" yaml:"isrelayed"` + RelayedBy string `json:"relayedby" yaml:"relayedby"` + IsRelay bool `json:"isrelay" yaml:"isrelay"` + IsGw bool `json:"is_gw" yaml:"is_gw"` RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"` IngressDNS string `json:"ingressdns" yaml:"ingressdns"` DNSOn bool `json:"dnson" yaml:"dnson"` diff --git a/pro/controllers/inet_gws.go b/pro/controllers/inet_gws.go index 22716514..d1cd8fd8 100644 --- a/pro/controllers/inet_gws.go +++ b/pro/controllers/inet_gws.go @@ -84,6 +84,9 @@ func createInternetGw(w http.ResponseWriter, r *http.Request) { }() } } + if node.IsGw && node.IngressDNS == "" { + node.IngressDNS = "1.1.1.1" + } err = logic.UpsertNode(&node) if err != nil { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) diff --git a/pro/controllers/relay.go b/pro/controllers/relay.go deleted file mode 100644 index 415cf1cc..00000000 --- a/pro/controllers/relay.go +++ /dev/null @@ -1,152 +0,0 @@ -package controllers - -import ( - "encoding/json" - "fmt" - "net/http" - - "github.com/google/uuid" - proLogic "github.com/gravitl/netmaker/pro/logic" - - "github.com/gorilla/mux" - controller "github.com/gravitl/netmaker/controllers" - "github.com/gravitl/netmaker/logger" - "github.com/gravitl/netmaker/logic" - "github.com/gravitl/netmaker/models" - "github.com/gravitl/netmaker/mq" -) - -// RelayHandlers - handle Pro Relays -func RelayHandlers(r *mux.Router) { - - r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", logic.SecurityCheck(true, http.HandlerFunc(createRelay))).Methods(http.MethodPost) - r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", logic.SecurityCheck(true, http.HandlerFunc(deleteRelay))).Methods(http.MethodDelete) - r.HandleFunc("/api/v1/host/{hostid}/failoverme", controller.Authorize(true, false, "host", http.HandlerFunc(failOverME))).Methods(http.MethodPost) -} - -// @Summary Create a relay -// @Router /api/nodes/{network}/{nodeid}/createrelay [post] -// @Tags PRO -// @Accept json -// @Produce json -// @Param network path string true "Network ID" -// @Param nodeid path string true "Node ID" -// @Param body body models.RelayRequest true "Relay request parameters" -// @Success 200 {object} models.ApiNode -// @Failure 400 {object} models.ErrorResponse -// @Failure 500 {object} models.ErrorResponse -func createRelay(w http.ResponseWriter, r *http.Request) { - var relayRequest models.RelayRequest - var params = mux.Vars(r) - w.Header().Set("Content-Type", "application/json") - err := json.NewDecoder(r.Body).Decode(&relayRequest) - if err != nil { - logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) - return - } - relayRequest.NetID = params["network"] - relayRequest.NodeID = params["nodeid"] - _, relayNode, err := proLogic.CreateRelay(relayRequest) - if err != nil { - logger.Log( - 0, - r.Header.Get("user"), - fmt.Sprintf( - "failed to create relay on node [%s] on network [%s]: %v", - relayRequest.NodeID, - relayRequest.NetID, - err, - ), - ) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - for _, relayedNodeID := range relayNode.RelayedNodes { - relayedNode, err := logic.GetNodeByID(relayedNodeID) - if err == nil { - if relayedNode.FailedOverBy != uuid.Nil { - go logic.ResetFailedOverPeer(&relayedNode) - } - - } - } - go mq.PublishPeerUpdate(false) - logger.Log( - 1, - r.Header.Get("user"), - "created relay on node", - relayRequest.NodeID, - "on network", - relayRequest.NetID, - ) - apiNode := relayNode.ConvertToAPINode() - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(apiNode) -} - -// @Summary Remove a relay -// @Router /api/nodes/{network}/{nodeid}/deleterelay [delete] -// @Tags PRO -// @Accept json -// @Produce json -// @Param network path string true "Network ID" -// @Param nodeid path string true "Node ID" -// @Success 200 {object} models.ApiNode -// @Failure 400 {object} models.ErrorResponse -// @Failure 500 {object} models.ErrorResponse -func deleteRelay(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - var params = mux.Vars(r) - nodeid := params["nodeid"] - netid := params["network"] - updateNodes, node, err := proLogic.DeleteRelay(netid, nodeid) - if err != nil { - logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) - return - } - logger.Log(1, r.Header.Get("user"), "deleted relay server", nodeid, "on network", netid) - go func() { - for _, relayedNode := range updateNodes { - err = mq.NodeUpdate(&relayedNode) - if err != nil { - logger.Log( - 1, - "relayed node update ", - relayedNode.ID.String(), - "on network", - relayedNode.Network, - ": ", - err.Error(), - ) - - } - h, err := logic.GetHost(relayedNode.HostID.String()) - if err == nil { - if h.OS == models.OS_Types.IoT { - nodes, err := logic.GetAllNodes() - if err != nil { - return - } - node.IsRelay = true // for iot update to recognise that it has to delete relay peer - if err = mq.PublishSingleHostPeerUpdate(h, nodes, &node, nil, false, nil); err != nil { - logger.Log(1, "failed to publish peer update to host", h.ID.String(), ": ", err.Error()) - } - } - } - } - mq.PublishPeerUpdate(false) - }() - logger.Log( - 1, - r.Header.Get("user"), - "deleted relay on node", - node.ID.String(), - "on network", - node.Network, - ) - apiNode := node.ConvertToAPINode() - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(apiNode) -} diff --git a/pro/initialize.go b/pro/initialize.go index 55b9c868..6c6587f9 100644 --- a/pro/initialize.go +++ b/pro/initialize.go @@ -29,7 +29,6 @@ func InitPro() { controller.HttpHandlers = append( controller.HttpHandlers, proControllers.MetricHandlers, - proControllers.RelayHandlers, proControllers.UserHandlers, proControllers.FailOverHandlers, proControllers.InetHandlers, @@ -106,13 +105,6 @@ func InitPro() { logic.GetMetrics = proLogic.GetMetrics logic.UpdateMetrics = proLogic.UpdateMetrics logic.DeleteMetrics = proLogic.DeleteMetrics - logic.GetRelays = proLogic.GetRelays - logic.GetAllowedIpsForRelayed = proLogic.GetAllowedIpsForRelayed - logic.RelayedAllowedIPs = proLogic.RelayedAllowedIPs - logic.UpdateRelayed = proLogic.UpdateRelayed - logic.SetRelayedNodes = proLogic.SetRelayedNodes - logic.RelayUpdates = proLogic.RelayUpdates - logic.ValidateRelay = proLogic.ValidateRelay logic.GetTrialEndDate = getTrialEndDate logic.SetDefaultGw = proLogic.SetDefaultGw logic.SetDefaultGwForRelayedUpdate = proLogic.SetDefaultGwForRelayedUpdate diff --git a/pro/logic/relays.go b/pro/logic/relays.go deleted file mode 100644 index adb9bc6b..00000000 --- a/pro/logic/relays.go +++ /dev/null @@ -1,255 +0,0 @@ -package logic - -import ( - "errors" - "fmt" - "net" - - "github.com/google/uuid" - "github.com/gravitl/netmaker/logger" - "github.com/gravitl/netmaker/logic" - "github.com/gravitl/netmaker/logic/acls/nodeacls" - "github.com/gravitl/netmaker/models" - "github.com/gravitl/netmaker/mq" - "github.com/gravitl/netmaker/servercfg" - "golang.org/x/exp/slog" -) - -// GetRelays - gets all the nodes that are relays -func GetRelays() ([]models.Node, error) { - nodes, err := logic.GetAllNodes() - if err != nil { - return nil, err - } - relays := make([]models.Node, 0) - for _, node := range nodes { - if node.IsRelay { - relays = append(relays, node) - } - } - return relays, nil -} - -// CreateRelay - creates a relay -func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error) { - var returnnodes []models.Node - - node, err := logic.GetNodeByID(relay.NodeID) - if err != nil { - return returnnodes, models.Node{}, err - } - host, err := logic.GetHost(node.HostID.String()) - if err != nil { - return returnnodes, models.Node{}, err - } - if host.OS != "linux" { - return returnnodes, models.Node{}, fmt.Errorf("only linux machines can be relay nodes") - } - err = ValidateRelay(relay, false) - if err != nil { - return returnnodes, models.Node{}, err - } - node.IsRelay = true - node.RelayedNodes = relay.RelayedNodes - node.SetLastModified() - err = logic.UpsertNode(&node) - if err != nil { - return returnnodes, node, err - } - returnnodes = SetRelayedNodes(true, relay.NodeID, relay.RelayedNodes) - return returnnodes, node, nil -} - -// SetRelayedNodes- sets and saves node as relayed -func SetRelayedNodes(setRelayed bool, relay string, relayed []string) []models.Node { - var returnnodes []models.Node - for _, id := range relayed { - node, err := logic.GetNodeByID(id) - if err != nil { - logger.Log(0, "setRelayedNodes.GetNodebyID", err.Error()) - continue - } - node.IsRelayed = setRelayed - if setRelayed { - node.RelayedBy = relay - } else { - node.RelayedBy = "" - } - node.SetLastModified() - if err := logic.UpsertNode(&node); err != nil { - logger.Log(0, "setRelayedNodes.Insert", err.Error()) - continue - } - returnnodes = append(returnnodes, node) - } - return returnnodes -} - -// func GetRelayedNodes(relayNode *models.Node) (models.Node, error) { -// var returnnodes []models.Node -// networkNodes, err := GetNetworkNodes(relayNode.Network) -// if err != nil { -// return returnnodes, err -// } -// for _, node := range networkNodes { -// for _, addr := range relayNode.RelayAddrs { -// if addr == node.Address.IP.String() || addr == node.Address6.IP.String() { -// returnnodes = append(returnnodes, node) -// } -// } -// } -// return returnnodes, nil -// } - -// ValidateRelay - checks if relay is valid -func ValidateRelay(relay models.RelayRequest, update bool) error { - var err error - - node, err := logic.GetNodeByID(relay.NodeID) - if err != nil { - return err - } - if !update && node.IsRelay { - return errors.New("node is already acting as a relay") - } - for _, relayedNodeID := range relay.RelayedNodes { - relayedNode, err := logic.GetNodeByID(relayedNodeID) - if err != nil { - return err - } - if relayedNode.IsIngressGateway { - return errors.New("cannot relay an ingress gateway (" + relayedNodeID + ")") - } - if relayedNode.IsInternetGateway { - return errors.New("cannot relay an internet gateway (" + relayedNodeID + ")") - } - if relayedNode.InternetGwID != "" && relayedNode.InternetGwID != relay.NodeID { - return errors.New("cannot relay an internet client (" + relayedNodeID + ")") - } - if relayedNode.IsFailOver { - return errors.New("cannot relay a failOver (" + relayedNodeID + ")") - } - if relayedNode.FailedOverBy != uuid.Nil { - ResetFailedOverPeer(&relayedNode) - } - } - return err -} - -// UpdateRelayNodes - updates relay nodes -func updateRelayNodes(relay string, oldNodes []string, newNodes []string) []models.Node { - _ = SetRelayedNodes(false, relay, oldNodes) - return SetRelayedNodes(true, relay, newNodes) -} - -func RelayUpdates(currentNode, newNode *models.Node) bool { - relayUpdates := false - if servercfg.IsPro && newNode.IsRelay { - if len(newNode.RelayedNodes) != len(currentNode.RelayedNodes) { - relayUpdates = true - } else { - for i, node := range newNode.RelayedNodes { - if node != currentNode.RelayedNodes[i] { - relayUpdates = true - } - } - } - } - return relayUpdates -} - -// UpdateRelayed - updates a relay's relayed nodes, and sends updates to the relayed nodes over MQ -func UpdateRelayed(currentNode, newNode *models.Node) { - updatenodes := updateRelayNodes(currentNode.ID.String(), currentNode.RelayedNodes, newNode.RelayedNodes) - if len(updatenodes) > 0 { - for _, relayedNode := range updatenodes { - node := relayedNode - ResetFailedOverPeer(&node) - go func() { - if err := mq.NodeUpdate(&node); err != nil { - slog.Error("error publishing node update to node", "node", node.ID, "error", err) - } - - }() - } - } -} - -// DeleteRelay - deletes a relay -func DeleteRelay(network, nodeid string) ([]models.Node, models.Node, error) { - var returnnodes []models.Node - node, err := logic.GetNodeByID(nodeid) - if err != nil { - return returnnodes, models.Node{}, err - } - returnnodes = SetRelayedNodes(false, nodeid, node.RelayedNodes) - node.IsRelay = false - node.RelayedNodes = []string{} - node.SetLastModified() - if err = logic.UpsertNode(&node); err != nil { - return returnnodes, models.Node{}, err - } - return returnnodes, node, nil -} - -func RelayedAllowedIPs(peer, node *models.Node) []net.IPNet { - var allowedIPs = []net.IPNet{} - for _, relayedNodeID := range peer.RelayedNodes { - if node.ID.String() == relayedNodeID { - continue - } - relayedNode, err := logic.GetNodeByID(relayedNodeID) - if err != nil { - continue - } - allowed := getRelayedAddresses(relayedNodeID) - if relayedNode.IsEgressGateway { - allowed = append(allowed, logic.GetEgressIPs(&relayedNode)...) - } - allowedIPs = append(allowedIPs, allowed...) - } - return allowedIPs -} - -// GetAllowedIpsForRelayed - returns the peerConfig for a node relayed by relay -func GetAllowedIpsForRelayed(relayed, relay *models.Node) (allowedIPs []net.IPNet) { - if relayed.RelayedBy != relay.ID.String() { - logger.Log(0, "RelayedByRelay called with invalid parameters") - return - } - if relay.InternetGwID != "" { - return GetAllowedIpForInetNodeClient(relayed, relay) - } - peers, err := logic.GetNetworkNodes(relay.Network) - if err != nil { - logger.Log(0, "error getting network clients", err.Error()) - return - } - for _, peer := range peers { - if peer.ID == relayed.ID || peer.ID == relay.ID { - continue - } - if nodeacls.AreNodesAllowed(nodeacls.NetworkID(relayed.Network), nodeacls.NodeID(relayed.ID.String()), nodeacls.NodeID(peer.ID.String())) { - allowedIPs = append(allowedIPs, logic.GetAllowedIPs(relayed, &peer, nil)...) - } - } - return -} - -func getRelayedAddresses(id string) []net.IPNet { - addrs := []net.IPNet{} - node, err := logic.GetNodeByID(id) - if err != nil { - logger.Log(0, "getRelayedAddresses: "+err.Error()) - return addrs - } - if node.Address.IP != nil { - node.Address.Mask = net.CIDRMask(32, 32) - addrs = append(addrs, node.Address) - } - if node.Address6.IP != nil { - node.Address6.Mask = net.CIDRMask(128, 128) - addrs = append(addrs, node.Address6) - } - return addrs -}