From 530dbdc65c5dd482543f4ba034b08f2b8f2358bb Mon Sep 17 00:00:00 2001 From: Abhishek K <32607604+abhishek9686@users.noreply.github.com> Date: Wed, 6 Dec 2023 23:57:58 +0400 Subject: [PATCH] NET-710: Internet Gws Re-Design (#2718) * add internet gateway to client gateway * migration func to remove internet egress range from egress gateway * add internet gateways ranges to firewall update * add internet gw ranges to extcleint conf * add ipv6 internet address * remove failover field from ingress req * only let normal to be created on PRO (#2716) * feat(NET-805): send internet gw props to rac * set inet gw field on node update api * move internet gws to EE --------- Co-authored-by: the_aceix --- controllers/ext_client.go | 30 ++++++++++++++++++---------- logic/gateway.go | 20 +++++++++++++------ logic/peers.go | 32 +++++++++++++++++++++++++++++ migrate/migrate.go | 30 ++++++++++++++++++++++++++++ models/api_node.go | 8 +++----- models/node.go | 42 +++++++++++++++++++-------------------- models/structs.go | 15 +++++++------- pro/controllers/users.go | 18 +++++++++-------- pro/initialize.go | 2 ++ pro/logic/nodes.go | 10 ++++++++++ 10 files changed, 150 insertions(+), 57 deletions(-) diff --git a/controllers/ext_client.go b/controllers/ext_client.go index 7e7c88f7..f0b36ae4 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -216,18 +216,28 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) { } else { gwendpoint = fmt.Sprintf("%s:%d", host.EndpointIP.String(), host.ListenPort) } - newAllowedIPs := network.AddressRange - if newAllowedIPs != "" && network.AddressRange6 != "" { - newAllowedIPs += "," - } - if network.AddressRange6 != "" { - newAllowedIPs += network.AddressRange6 - } - if egressGatewayRanges, err := logic.GetEgressRangesOnNetwork(&client); err == nil { - for _, egressGatewayRange := range egressGatewayRanges { - newAllowedIPs += "," + egressGatewayRange + var newAllowedIPs string + if logic.IsInternetGw(gwnode) { + egressrange := "0.0.0.0/0" + if gwnode.Address6.IP != nil && client.Address6 != "" { + egressrange += "," + "::/0" + } + newAllowedIPs = egressrange + } else { + newAllowedIPs = network.AddressRange + if newAllowedIPs != "" && network.AddressRange6 != "" { + newAllowedIPs += "," + } + if network.AddressRange6 != "" { + newAllowedIPs += network.AddressRange6 + } + if egressGatewayRanges, err := logic.GetEgressRangesOnNetwork(&client); err == nil { + for _, egressGatewayRange := range egressGatewayRanges { + newAllowedIPs += "," + egressGatewayRange + } } } + defaultDNS := "" if client.DNS != "" { defaultDNS = "DNS = " + client.DNS diff --git a/logic/gateway.go b/logic/gateway.go index c5fbc1d0..a96511f5 100644 --- a/logic/gateway.go +++ b/logic/gateway.go @@ -10,6 +10,16 @@ import ( "github.com/gravitl/netmaker/models" ) +var ( + // SetInternetGw - sets the node as internet gw based on flag bool + SetInternetGw = func(node *models.Node, flag bool) { + } + // IsInternetGw - checks if node is acting as internet gw + IsInternetGw = func(node models.Node) bool { + return false + } +) + // GetInternetGateways - gets all the nodes that are internet gateways func GetInternetGateways() ([]models.Node, error) { nodes, err := GetAllNodes() @@ -78,12 +88,8 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro } for i := len(gateway.Ranges) - 1; i >= 0; i-- { // check if internet gateway IPv4 - if gateway.Ranges[i] == "0.0.0.0/0" && FreeTier { - return models.Node{}, fmt.Errorf("currently IPv4 internet gateways are not supported on the free tier: %s", gateway.Ranges[i]) - } - // check if internet gateway IPv6 - if gateway.Ranges[i] == "::/0" { - return models.Node{}, fmt.Errorf("currently IPv6 internet gateways are not supported: %s", gateway.Ranges[i]) + if gateway.Ranges[i] == "0.0.0.0/0" || gateway.Ranges[i] == "::/0" { + return models.Node{}, fmt.Errorf("create internet gateways on the remote client gateway") } normalized, err := NormalizeCIDR(gateway.Ranges[i]) if err != nil { @@ -163,6 +169,7 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq return models.Node{}, err } node.IsIngressGateway = true + SetInternetGw(&node, ingress.IsInternetGateway) node.IngressGatewayRange = network.AddressRange node.IngressGatewayRange6 = network.AddressRange6 node.IngressDNS = ingress.ExtclientDNS @@ -215,6 +222,7 @@ func DeleteIngressGateway(nodeid string) (models.Node, []models.ExtClient, error logger.Log(3, "deleting ingress gateway") node.LastModified = time.Now() node.IsIngressGateway = false + node.IsInternetGateway = false node.IngressGatewayRange = "" err = UpsertNode(&node) if err != nil { diff --git a/logic/peers.go b/logic/peers.go index bcbb0cb2..0a260cf1 100644 --- a/logic/peers.go +++ b/logic/peers.go @@ -241,8 +241,18 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N logger.Log(1, "error retrieving external clients:", err.Error()) } } + addedInetGwRanges := false if node.IsEgressGateway && node.EgressGatewayRequest.NatEnabled == "yes" && len(node.EgressGatewayRequest.Ranges) > 0 { hostPeerUpdate.FwUpdate.IsEgressGw = true + if IsInternetGw(node) { + hostPeerUpdate.FwUpdate.IsEgressGw = true + egressrange := []string{"0.0.0.0/0"} + if node.Address6.IP != nil { + egressrange = append(egressrange, "::/0") + } + node.EgressGatewayRequest.Ranges = append(node.EgressGatewayRequest.Ranges, egressrange...) + addedInetGwRanges = true + } hostPeerUpdate.FwUpdate.EgressInfo[node.ID.String()] = models.EgressInfo{ EgressID: node.ID.String(), Network: node.PrimaryNetworkRange(), @@ -252,6 +262,28 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N }, EgressGWCfg: node.EgressGatewayRequest, } + + } + if IsInternetGw(node) && !addedInetGwRanges { + hostPeerUpdate.FwUpdate.IsEgressGw = true + egressrange := []string{"0.0.0.0/0"} + if node.Address6.IP != nil { + egressrange = append(egressrange, "::/0") + } + hostPeerUpdate.FwUpdate.EgressInfo[node.ID.String()] = models.EgressInfo{ + EgressID: node.ID.String(), + Network: node.PrimaryAddressIPNet(), + EgressGwAddr: net.IPNet{ + IP: net.ParseIP(node.PrimaryAddress()), + Mask: getCIDRMaskFromAddr(node.PrimaryAddress()), + }, + EgressGWCfg: models.EgressGatewayRequest{ + NodeID: node.ID.String(), + NetID: node.Network, + NatEnabled: "yes", + Ranges: egressrange, + }, + } } } // == post peer calculations == diff --git a/migrate/migrate.go b/migrate/migrate.go index d805554d..d100d93b 100644 --- a/migrate/migrate.go +++ b/migrate/migrate.go @@ -18,6 +18,7 @@ func Run() { updateEnrollmentKeys() assignSuperAdmin() updateHosts() + updateNodes() } func assignSuperAdmin() { @@ -137,3 +138,32 @@ func updateHosts() { } } } + +func updateNodes() { + nodes, err := logic.GetAllNodes() + if err != nil { + slog.Error("migration failed for nodes", "error", err) + return + } + for _, node := range nodes { + if node.IsEgressGateway { + egressRanges, update := removeInterGw(node.EgressGatewayRanges) + if update { + node.EgressGatewayRequest.Ranges = egressRanges + node.EgressGatewayRanges = egressRanges + logic.UpsertNode(&node) + } + } + } +} + +func removeInterGw(egressRanges []string) ([]string, bool) { + update := false + for i := len(egressRanges) - 1; i >= 0; i-- { + if egressRanges[i] == "0.0.0.0/0" || egressRanges[i] == "::/0" { + update = true + egressRanges = append(egressRanges[:i], egressRanges[i+1:]...) + } + } + return egressRanges, update +} diff --git a/models/api_node.go b/models/api_node.go index 5f474b40..7a841f18 100644 --- a/models/api_node.go +++ b/models/api_node.go @@ -28,6 +28,7 @@ type ApiNode struct { RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"` IsEgressGateway bool `json:"isegressgateway"` IsIngressGateway bool `json:"isingressgateway"` + IsInternetGateway bool `json:"isinternetgateway" yaml:"isinternetgateway"` EgressGatewayRanges []string `json:"egressgatewayranges"` EgressGatewayNatEnabled bool `json:"egressgatewaynatenabled"` DNSOn bool `json:"dnson"` @@ -67,6 +68,7 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node { convertedNode.IngressGatewayRange6 = currentNode.IngressGatewayRange6 convertedNode.DNSOn = a.DNSOn convertedNode.IngressDNS = a.IngressDns + convertedNode.IsInternetGateway = a.IsInternetGateway convertedNode.EgressGatewayRequest = currentNode.EgressGatewayRequest convertedNode.EgressGatewayNatEnabled = currentNode.EgressGatewayNatEnabled convertedNode.RelayedNodes = a.RelayedNodes @@ -88,10 +90,6 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node { } else if !isEmptyAddr(currentNode.LocalAddress.String()) { convertedNode.LocalAddress = currentNode.LocalAddress } - udpAddr, err := net.ResolveUDPAddr("udp", a.InternetGateway) - if err == nil { - convertedNode.InternetGateway = udpAddr - } ip, addr, err := net.ParseCIDR(a.Address) if err == nil { convertedNode.Address = *addr @@ -150,13 +148,13 @@ func (nm *Node) ConvertToAPINode() *ApiNode { apiNode.DNSOn = nm.DNSOn apiNode.IngressDns = nm.IngressDNS apiNode.Server = nm.Server - apiNode.InternetGateway = nm.InternetGateway.String() if isEmptyAddr(apiNode.InternetGateway) { apiNode.InternetGateway = "" } apiNode.Connected = nm.Connected apiNode.PendingDelete = nm.PendingDelete apiNode.DefaultACL = nm.DefaultACL + apiNode.IsInternetGateway = nm.IsInternetGateway apiNode.IsFailOver = nm.IsFailOver apiNode.FailOverPeers = nm.FailOverPeers apiNode.FailedOverBy = nm.FailedOverBy diff --git a/models/node.go b/models/node.go index 4e5dc972..174d651c 100644 --- a/models/node.go +++ b/models/node.go @@ -54,27 +54,27 @@ type Iface struct { // CommonNode - represents a commonn node data elements shared by netmaker and netclient type CommonNode struct { - ID uuid.UUID `json:"id" yaml:"id"` - HostID uuid.UUID `json:"hostid" yaml:"hostid"` - Network string `json:"network" yaml:"network"` - NetworkRange net.IPNet `json:"networkrange" yaml:"networkrange"` - NetworkRange6 net.IPNet `json:"networkrange6" yaml:"networkrange6"` - InternetGateway *net.UDPAddr `json:"internetgateway" yaml:"internetgateway"` - Server string `json:"server" yaml:"server"` - Connected bool `json:"connected" yaml:"connected"` - Address net.IPNet `json:"address" yaml:"address"` - Address6 net.IPNet `json:"address6" yaml:"address6"` - Action string `json:"action" yaml:"action"` - LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"` - IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"` - EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"` - IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"` - IsRelayed bool `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"` - RelayedBy string `json:"relayedby" bson:"relayedby" yaml:"relayedby"` - IsRelay bool `json:"isrelay" bson:"isrelay" yaml:"isrelay"` - RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"` - IngressDNS string `json:"ingressdns" yaml:"ingressdns"` - DNSOn bool `json:"dnson" yaml:"dnson"` + ID uuid.UUID `json:"id" yaml:"id"` + HostID uuid.UUID `json:"hostid" yaml:"hostid"` + Network string `json:"network" yaml:"network"` + NetworkRange net.IPNet `json:"networkrange" yaml:"networkrange"` + NetworkRange6 net.IPNet `json:"networkrange6" yaml:"networkrange6"` + Server string `json:"server" yaml:"server"` + Connected bool `json:"connected" yaml:"connected"` + Address net.IPNet `json:"address" yaml:"address"` + Address6 net.IPNet `json:"address6" yaml:"address6"` + Action string `json:"action" yaml:"action"` + LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"` + IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"` + EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"` + IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"` + IsInternetGateway bool `json:"isinternetgateway" yaml:"isinternetgateway"` + IsRelayed bool `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"` + RelayedBy string `json:"relayedby" bson:"relayedby" yaml:"relayedby"` + IsRelay bool `json:"isrelay" bson:"isrelay" yaml:"isrelay"` + RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"` + IngressDNS string `json:"ingressdns" yaml:"ingressdns"` + DNSOn bool `json:"dnson" yaml:"dnson"` } // Node - a model of a network node diff --git a/models/structs.go b/models/structs.go index 906d16cf..8ce29ced 100644 --- a/models/structs.go +++ b/models/structs.go @@ -64,11 +64,12 @@ type IngressGwUsers struct { // UserRemoteGws - struct to hold user's remote gws type UserRemoteGws struct { - GwID string `json:"remote_access_gw_id"` - GWName string `json:"gw_name"` - Network string `json:"network"` - Connected bool `json:"connected"` - GwClient ExtClient `json:"gw_client"` + GwID string `json:"remote_access_gw_id"` + GWName string `json:"gw_name"` + Network string `json:"network"` + Connected bool `json:"connected"` + IsInternetGateway bool `json:"is_internet_gateway"` + GwClient ExtClient `json:"gw_client"` } // UserRemoteGwsReq - struct to hold user remote acccess gws req @@ -189,8 +190,8 @@ type HostRelayRequest struct { // IngressRequest - ingress request struct type IngressRequest struct { - ExtclientDNS string `json:"extclientdns"` - Failover bool `json:"failover"` + ExtclientDNS string `json:"extclientdns"` + IsInternetGateway bool `json:"is_internet_gw"` } // ServerUpdateData - contains data to configure server diff --git a/pro/controllers/users.go b/pro/controllers/users.go index b880dbbb..6c525cbf 100644 --- a/pro/controllers/users.go +++ b/pro/controllers/users.go @@ -197,11 +197,12 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) { gws := userGws[node.Network] gws = append(gws, models.UserRemoteGws{ - GwID: node.ID.String(), - GWName: host.Name, - Network: node.Network, - GwClient: extClient, - Connected: true, + GwID: node.ID.String(), + GWName: host.Name, + Network: node.Network, + GwClient: extClient, + Connected: true, + IsInternetGateway: node.IsInternetGateway, }) userGws[node.Network] = gws delete(user.RemoteGwIDs, node.ID.String()) @@ -230,9 +231,10 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) { gws := userGws[node.Network] gws = append(gws, models.UserRemoteGws{ - GwID: node.ID.String(), - GWName: host.Name, - Network: node.Network, + GwID: node.ID.String(), + GWName: host.Name, + Network: node.Network, + IsInternetGateway: node.IsInternetGateway, }) userGws[node.Network] = gws } diff --git a/pro/initialize.go b/pro/initialize.go index 28db5f54..e073b379 100644 --- a/pro/initialize.go +++ b/pro/initialize.go @@ -61,6 +61,8 @@ func InitPro() { logic.UpdateRelayed = proLogic.UpdateRelayed logic.SetRelayedNodes = proLogic.SetRelayedNodes logic.RelayUpdates = proLogic.RelayUpdates + logic.IsInternetGw = proLogic.IsInternetGw + logic.SetInternetGw = proLogic.SetInternetGw mq.UpdateMetrics = proLogic.MQUpdateMetrics } diff --git a/pro/logic/nodes.go b/pro/logic/nodes.go index 153b710a..1ab85223 100644 --- a/pro/logic/nodes.go +++ b/pro/logic/nodes.go @@ -5,6 +5,16 @@ import ( "github.com/gravitl/netmaker/models" ) +// IsInternetGw - checks if node is acting as internet gw +func IsInternetGw(node models.Node) bool { + return node.IsInternetGateway +} + +// SetInternetGw - sets the node as internet gw based on flag bool +func SetInternetGw(node *models.Node, flag bool) { + node.IsInternetGateway = flag +} + // GetNetworkIngresses - gets the gateways of a network func GetNetworkIngresses(network string) ([]models.Node, error) { var ingresses []models.Node