diff --git a/controllers/gateway.go b/controllers/gateway.go index 609fd136..20b31d12 100644 --- a/controllers/gateway.go +++ b/controllers/gateway.go @@ -50,6 +50,14 @@ func createGateway(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } + if req.IsInternetGateway && len(req.InetNodeClientIDs) > 0 { + err = logic.ValidateInetGwReq(node, req.InetNodeReq, false) + 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"), @@ -84,6 +92,22 @@ func createGateway(w http.ResponseWriter, r *http.Request) { } } + if len(req.InetNodeClientIDs) > 0 { + logic.SetInternetGw(&node, req.InetNodeReq) + if servercfg.IsPro { + if _, exists := logic.FailOverExists(node.Network); exists { + go func() { + logic.ResetFailedOverPeer(&node) + mq.PublishPeerUpdate(false) + }() + } + } + if node.IsGw && node.IngressDNS == "" { + node.IngressDNS = "1.1.1.1" + } + logic.UpsertNode(&node) + } + logger.Log( 1, r.Header.Get("user"), diff --git a/pro/controllers/inet_gws.go b/controllers/inet_gws.go similarity index 86% rename from pro/controllers/inet_gws.go rename to controllers/inet_gws.go index 4a286580..cf2e1d94 100644 --- a/pro/controllers/inet_gws.go +++ b/controllers/inet_gws.go @@ -1,4 +1,4 @@ -package controllers +package controller import ( "encoding/json" @@ -10,20 +10,9 @@ import ( "github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/mq" - proLogic "github.com/gravitl/netmaker/pro/logic" "github.com/gravitl/netmaker/servercfg" ) -// InetHandlers - handlers for internet gw -func InetHandlers(r *mux.Router) { - r.HandleFunc("/api/nodes/{network}/{nodeid}/inet_gw", logic.SecurityCheck(true, http.HandlerFunc(createInternetGw))). - Methods(http.MethodPost) - r.HandleFunc("/api/nodes/{network}/{nodeid}/inet_gw", logic.SecurityCheck(true, http.HandlerFunc(updateInternetGw))). - Methods(http.MethodPut) - r.HandleFunc("/api/nodes/{network}/{nodeid}/inet_gw", logic.SecurityCheck(true, http.HandlerFunc(deleteInternetGw))). - Methods(http.MethodDelete) -} - // @Summary Create an internet gateway // @Router /api/nodes/{network}/{nodeid}/inet_gw [post] // @Tags PRO @@ -70,16 +59,16 @@ func createInternetGw(w http.ResponseWriter, r *http.Request) { ) return } - err = proLogic.ValidateInetGwReq(node, request, false) + err = logic.ValidateInetGwReq(node, request, false) if err != nil { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } logic.SetInternetGw(&node, request) if servercfg.IsPro { - if _, exists := proLogic.FailOverExists(node.Network); exists { + if _, exists := logic.FailOverExists(node.Network); exists { go func() { - proLogic.ResetFailedOverPeer(&node) + logic.ResetFailedOverPeer(&node) mq.PublishPeerUpdate(false) }() } @@ -140,7 +129,7 @@ func updateInternetGw(w http.ResponseWriter, r *http.Request) { ) return } - err = proLogic.ValidateInetGwReq(node, request, true) + err = logic.ValidateInetGwReq(node, request, true) if err != nil { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return diff --git a/controllers/legacy.go b/controllers/legacy.go index c118fb7d..0cac0f80 100644 --- a/controllers/legacy.go +++ b/controllers/legacy.go @@ -11,6 +11,12 @@ import ( func legacyHandlers(r *mux.Router) { r.HandleFunc("/api/v1/legacy/nodes", logic.SecurityCheck(true, http.HandlerFunc(wipeLegacyNodes))). Methods(http.MethodDelete) + r.HandleFunc("/api/nodes/{network}/{nodeid}/inet_gw", logic.SecurityCheck(true, http.HandlerFunc(createInternetGw))). + Methods(http.MethodPost) + r.HandleFunc("/api/nodes/{network}/{nodeid}/inet_gw", logic.SecurityCheck(true, http.HandlerFunc(updateInternetGw))). + Methods(http.MethodPut) + r.HandleFunc("/api/nodes/{network}/{nodeid}/inet_gw", logic.SecurityCheck(true, http.HandlerFunc(deleteInternetGw))). + Methods(http.MethodDelete) } // @Summary Delete all legacy nodes from DB. diff --git a/controllers/node.go b/controllers/node.go index 3b3260ed..e4ed2851 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -657,7 +657,6 @@ func updateNode(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } - ifaceDelta := logic.IfaceDelta(¤tNode, newNode) aclUpdate := currentNode.DefaultACL != newNode.DefaultACL err = logic.UpdateNode(¤tNode, newNode) @@ -670,7 +669,27 @@ func updateNode(w http.ResponseWriter, r *http.Request) { if relayUpdate { logic.UpdateRelayed(¤tNode, newNode) } - + if !currentNode.IsInternetGateway && newNode.IsInternetGateway { + err = logic.ValidateInetGwReq(*newNode, newNode.InetNodeReq, false) + if err != nil { + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) + return + } + logic.SetInternetGw(newNode, newNode.InetNodeReq) + } + if currentNode.IsInternetGateway && newNode.IsInternetGateway { + err = logic.ValidateInetGwReq(*newNode, newNode.InetNodeReq, true) + if err != nil { + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) + return + } + logic.UnsetInternetGw(newNode) + logic.SetInternetGw(newNode, newNode.InetNodeReq) + } + if !newNode.IsInternetGateway { + logic.UnsetInternetGw(newNode) + } + logic.UpsertNode(newNode) logic.GetNodeStatus(newNode, false) apiNode := newNode.ConvertToAPINode() @@ -707,11 +726,6 @@ func updateNode(w http.ResponseWriter, r *http.Request) { if err := mq.NodeUpdate(newNode); err != nil { slog.Error("error publishing node update to node", "node", newNode.ID, "error", err) } - if aclUpdate || relayupdate || ifaceDelta { - if err := mq.PublishPeerUpdate(false); err != nil { - logger.Log(0, "error during node ACL update for node", newNode.ID.String()) - } - } mq.PublishPeerUpdate(false) if servercfg.IsDNSMode() { logic.SetDNS() diff --git a/logic/gateway.go b/logic/gateway.go index da314c3f..63833fed 100644 --- a/logic/gateway.go +++ b/logic/gateway.go @@ -3,14 +3,22 @@ package logic import ( "errors" "fmt" + "net" "slices" "sort" "time" + "github.com/google/uuid" "github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/servercfg" + "golang.org/x/exp/slog" +) + +var ( + IPv4Network = "0.0.0.0/0" + IPv6Network = "::/0" ) // IsInternetGw - checks if node is acting as internet gw @@ -316,3 +324,157 @@ func IsUserAllowedAccessToExtClient(username string, client models.ExtClient) bo } return true } + +func ValidateInetGwReq(inetNode models.Node, req models.InetNodeReq, update bool) error { + inetHost, err := GetHost(inetNode.HostID.String()) + if err != nil { + return err + } + if inetHost.FirewallInUse == models.FIREWALL_NONE { + return errors.New("iptables or nftables needs to be installed") + } + if inetNode.InternetGwID != "" { + return fmt.Errorf("node %s is using a internet gateway already", inetHost.Name) + } + if inetNode.IsRelayed { + return fmt.Errorf("node %s is being relayed", inetHost.Name) + } + + for _, clientNodeID := range req.InetNodeClientIDs { + clientNode, err := GetNodeByID(clientNodeID) + if err != nil { + return err + } + if clientNode.IsFailOver { + return errors.New("failover node cannot be set to use internet gateway") + } + clientHost, err := GetHost(clientNode.HostID.String()) + if err != nil { + return err + } + if clientHost.IsDefault { + return errors.New("default host cannot be set to use internet gateway") + } + if clientHost.OS != models.OS_Types.Linux && clientHost.OS != models.OS_Types.Windows { + return errors.New("can only attach linux or windows machine to a internet gateway") + } + if clientNode.IsInternetGateway { + return fmt.Errorf("node %s acting as internet gateway cannot use another internet gateway", clientHost.Name) + } + if update { + if clientNode.InternetGwID != "" && clientNode.InternetGwID != inetNode.ID.String() { + return fmt.Errorf("node %s is already using a internet gateway", clientHost.Name) + } + } else { + if clientNode.InternetGwID != "" { + return fmt.Errorf("node %s is already using a internet gateway", clientHost.Name) + } + } + if clientNode.FailedOverBy != uuid.Nil { + ResetFailedOverPeer(&clientNode) + } + + if clientNode.IsRelayed && clientNode.RelayedBy != inetNode.ID.String() { + return fmt.Errorf("node %s is being relayed", clientHost.Name) + } + + for _, nodeID := range clientHost.Nodes { + node, err := GetNodeByID(nodeID) + if err != nil { + continue + } + if node.InternetGwID != "" && node.InternetGwID != inetNode.ID.String() { + return errors.New("nodes on same host cannot use different internet gateway") + } + + } + } + return nil +} + +// SetInternetGw - sets the node as internet gw based on flag bool +func SetInternetGw(node *models.Node, req models.InetNodeReq) { + node.IsInternetGateway = true + node.InetNodeReq = req + for _, clientNodeID := range req.InetNodeClientIDs { + clientNode, err := GetNodeByID(clientNodeID) + if err != nil { + continue + } + clientNode.InternetGwID = node.ID.String() + UpsertNode(&clientNode) + } + +} + +func UnsetInternetGw(node *models.Node) { + nodes, err := GetNetworkNodes(node.Network) + if err != nil { + slog.Error("failed to get network nodes", "network", node.Network, "error", err) + return + } + for _, clientNode := range nodes { + if node.ID.String() == clientNode.InternetGwID { + clientNode.InternetGwID = "" + UpsertNode(&clientNode) + } + + } + node.IsInternetGateway = false + node.InetNodeReq = models.InetNodeReq{} + +} + +func SetDefaultGwForRelayedUpdate(relayed, relay models.Node, peerUpdate models.HostPeerUpdate) models.HostPeerUpdate { + if relay.InternetGwID != "" { + relayedHost, err := GetHost(relayed.HostID.String()) + if err != nil { + return peerUpdate + } + peerUpdate.ChangeDefaultGw = true + peerUpdate.DefaultGwIp = relay.Address.IP + if peerUpdate.DefaultGwIp == nil || relayedHost.EndpointIP == nil { + peerUpdate.DefaultGwIp = relay.Address6.IP + } + + } + return peerUpdate +} + +func SetDefaultGw(node models.Node, peerUpdate models.HostPeerUpdate) models.HostPeerUpdate { + if node.InternetGwID != "" { + + inetNode, err := GetNodeByID(node.InternetGwID) + if err != nil { + return peerUpdate + } + host, err := GetHost(node.HostID.String()) + if err != nil { + return peerUpdate + } + + peerUpdate.ChangeDefaultGw = true + peerUpdate.DefaultGwIp = inetNode.Address.IP + if peerUpdate.DefaultGwIp == nil || host.EndpointIP == nil { + peerUpdate.DefaultGwIp = inetNode.Address6.IP + } + } + return peerUpdate +} + +// GetAllowedIpForInetNodeClient - get inet cidr for node using a inet gw +func GetAllowedIpForInetNodeClient(node, peer *models.Node) []net.IPNet { + var allowedips = []net.IPNet{} + + if peer.Address.IP != nil { + _, ipnet, _ := net.ParseCIDR(IPv4Network) + allowedips = append(allowedips, *ipnet) + } + + if peer.Address6.IP != nil { + _, ipnet, _ := net.ParseCIDR(IPv6Network) + allowedips = append(allowedips, *ipnet) + } + + return allowedips +} diff --git a/logic/peers.go b/logic/peers.go index 0ce52b6c..33180f21 100644 --- a/logic/peers.go +++ b/logic/peers.go @@ -42,25 +42,6 @@ var ( CreateFailOver = func(node models.Node) error { return nil } - // SetDefaulGw - SetDefaultGw = func(node models.Node, peerUpdate models.HostPeerUpdate) models.HostPeerUpdate { - return peerUpdate - } - SetDefaultGwForRelayedUpdate = func(relayed, relay models.Node, peerUpdate models.HostPeerUpdate) models.HostPeerUpdate { - return peerUpdate - } - // UnsetInternetGw - UnsetInternetGw = func(node *models.Node) { - node.IsInternetGateway = false - } - // SetInternetGw - SetInternetGw = func(node *models.Node, req models.InetNodeReq) { - node.IsInternetGateway = true - } - // GetAllowedIpForInetNodeClient - GetAllowedIpForInetNodeClient = func(node, peer *models.Node) []net.IPNet { - return []net.IPNet{} - } ) // GetHostPeerInfo - fetches required peer info per network diff --git a/models/gateway.go b/models/gateway.go index 6750c900..d308a5d4 100644 --- a/models/gateway.go +++ b/models/gateway.go @@ -3,6 +3,7 @@ package models type CreateGwReq struct { IngressRequest RelayRequest + InetNodeReq } type DeleteGw struct { diff --git a/pro/logic/acls.go b/pro/logic/acls.go index abc29277..24eeaf12 100644 --- a/pro/logic/acls.go +++ b/pro/logic/acls.go @@ -1468,12 +1468,12 @@ func GetAclRuleForInetGw(targetnode models.Node) (rules map[string]models.AclRul } if targetnode.NetworkRange.IP != nil { aclRule.IPList = append(aclRule.IPList, targetnode.NetworkRange) - _, allIpv4, _ := net.ParseCIDR(IPv4Network) + _, allIpv4, _ := net.ParseCIDR(logic.IPv4Network) aclRule.Dst = append(aclRule.Dst, *allIpv4) } if targetnode.NetworkRange6.IP != nil { aclRule.IP6List = append(aclRule.IP6List, targetnode.NetworkRange6) - _, allIpv6, _ := net.ParseCIDR(IPv6Network) + _, allIpv6, _ := net.ParseCIDR(logic.IPv6Network) aclRule.Dst6 = append(aclRule.Dst6, *allIpv6) } rules[aclRule.ID] = aclRule diff --git a/pro/logic/nodes.go b/pro/logic/nodes.go index 7feeea04..14a043f7 100644 --- a/pro/logic/nodes.go +++ b/pro/logic/nodes.go @@ -1,19 +1,8 @@ package logic import ( - "errors" - "fmt" - "net" - - "github.com/google/uuid" "github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/models" - "golang.org/x/exp/slog" -) - -var ( - IPv4Network = "0.0.0.0/0" - IPv6Network = "::/0" ) // GetNetworkIngresses - gets the gateways of a network @@ -234,157 +223,3 @@ func GetStaticNodeWithTag(tagID models.TagID) map[string]models.Node { } return nMap } - -func ValidateInetGwReq(inetNode models.Node, req models.InetNodeReq, update bool) error { - inetHost, err := logic.GetHost(inetNode.HostID.String()) - if err != nil { - return err - } - if inetHost.FirewallInUse == models.FIREWALL_NONE { - return errors.New("iptables or nftables needs to be installed") - } - if inetNode.InternetGwID != "" { - return fmt.Errorf("node %s is using a internet gateway already", inetHost.Name) - } - if inetNode.IsRelayed { - return fmt.Errorf("node %s is being relayed", inetHost.Name) - } - - for _, clientNodeID := range req.InetNodeClientIDs { - clientNode, err := logic.GetNodeByID(clientNodeID) - if err != nil { - return err - } - if clientNode.IsFailOver { - return errors.New("failover node cannot be set to use internet gateway") - } - clientHost, err := logic.GetHost(clientNode.HostID.String()) - if err != nil { - return err - } - if clientHost.IsDefault { - return errors.New("default host cannot be set to use internet gateway") - } - if clientHost.OS != models.OS_Types.Linux && clientHost.OS != models.OS_Types.Windows { - return errors.New("can only attach linux or windows machine to a internet gateway") - } - if clientNode.IsInternetGateway { - return fmt.Errorf("node %s acting as internet gateway cannot use another internet gateway", clientHost.Name) - } - if update { - if clientNode.InternetGwID != "" && clientNode.InternetGwID != inetNode.ID.String() { - return fmt.Errorf("node %s is already using a internet gateway", clientHost.Name) - } - } else { - if clientNode.InternetGwID != "" { - return fmt.Errorf("node %s is already using a internet gateway", clientHost.Name) - } - } - if clientNode.FailedOverBy != uuid.Nil { - ResetFailedOverPeer(&clientNode) - } - - if clientNode.IsRelayed && clientNode.RelayedBy != inetNode.ID.String() { - return fmt.Errorf("node %s is being relayed", clientHost.Name) - } - - for _, nodeID := range clientHost.Nodes { - node, err := logic.GetNodeByID(nodeID) - if err != nil { - continue - } - if node.InternetGwID != "" && node.InternetGwID != inetNode.ID.String() { - return errors.New("nodes on same host cannot use different internet gateway") - } - - } - } - return nil -} - -// SetInternetGw - sets the node as internet gw based on flag bool -func SetInternetGw(node *models.Node, req models.InetNodeReq) { - node.IsInternetGateway = true - node.InetNodeReq = req - for _, clientNodeID := range req.InetNodeClientIDs { - clientNode, err := logic.GetNodeByID(clientNodeID) - if err != nil { - continue - } - clientNode.InternetGwID = node.ID.String() - logic.UpsertNode(&clientNode) - } - -} - -func UnsetInternetGw(node *models.Node) { - nodes, err := logic.GetNetworkNodes(node.Network) - if err != nil { - slog.Error("failed to get network nodes", "network", node.Network, "error", err) - return - } - for _, clientNode := range nodes { - if node.ID.String() == clientNode.InternetGwID { - clientNode.InternetGwID = "" - logic.UpsertNode(&clientNode) - } - - } - node.IsInternetGateway = false - node.InetNodeReq = models.InetNodeReq{} - -} - -func SetDefaultGwForRelayedUpdate(relayed, relay models.Node, peerUpdate models.HostPeerUpdate) models.HostPeerUpdate { - if relay.InternetGwID != "" { - relayedHost, err := logic.GetHost(relayed.HostID.String()) - if err != nil { - return peerUpdate - } - peerUpdate.ChangeDefaultGw = true - peerUpdate.DefaultGwIp = relay.Address.IP - if peerUpdate.DefaultGwIp == nil || relayedHost.EndpointIP == nil { - peerUpdate.DefaultGwIp = relay.Address6.IP - } - - } - return peerUpdate -} - -func SetDefaultGw(node models.Node, peerUpdate models.HostPeerUpdate) models.HostPeerUpdate { - if node.InternetGwID != "" { - - inetNode, err := logic.GetNodeByID(node.InternetGwID) - if err != nil { - return peerUpdate - } - host, err := logic.GetHost(node.HostID.String()) - if err != nil { - return peerUpdate - } - - peerUpdate.ChangeDefaultGw = true - peerUpdate.DefaultGwIp = inetNode.Address.IP - if peerUpdate.DefaultGwIp == nil || host.EndpointIP == nil { - peerUpdate.DefaultGwIp = inetNode.Address6.IP - } - } - return peerUpdate -} - -// GetAllowedIpForInetNodeClient - get inet cidr for node using a inet gw -func GetAllowedIpForInetNodeClient(node, peer *models.Node) []net.IPNet { - var allowedips = []net.IPNet{} - - if peer.Address.IP != nil { - _, ipnet, _ := net.ParseCIDR(IPv4Network) - allowedips = append(allowedips, *ipnet) - } - - if peer.Address6.IP != nil { - _, ipnet, _ := net.ParseCIDR(IPv6Network) - allowedips = append(allowedips, *ipnet) - } - - return allowedips -}