NET-940: Inet Gws (#2828)

* internet gws apis

* add validate check for inet request

* add default gw changes to peer update

* update json tag

* add OS checks for inet gws

* add set defaul gw pro func

* allow disable and enable inet gw

* add inet handlers to pro

* add fields to api node

* add inet allowed ips

* add default gw to pull

* unset node inet details on deletion

* unset internet gw on network nodes

* unset inet gw fix

* unset inet gw fix

* send default gw ip

* fix inet node endpoint

* add default gw endpoint ip to pull resp

* validate after unset gws

* add inet client peer allowedips to inet node

* validate after unset gws

* fix allowed ips for inet peer and gw node

* fix allowed ips for inet peer and gw node

* fix allowed ips for inet peer and gw node

* fix allowed ips for inet peer and gw node

* fix inet gw and relayed conflict

* fix inet gw and relayed conflict

* fix update req

* fix update inet gw api

* when inet gw is peer ignore other allowedIps

* test relay

* revert test relay

* revert inet peer update changes

* channel internet traffic of relayed node to relay's inetgw

* channel internet traffic of relayed node to relay's inetgw

* channel internet traffic of relayed node to relay's inetgw

* add check for relayed node

* add inet info to peer update

* add inet info to peer update

* fix update node to persist inet info

* fix go tests

* egress ranges with inet gw fix

* egress ranges with inet gw fix

* disallow node acting using inet gw to act as inet gw

* add check to validate inet gw

* fix typos

* add firewall check

* set inetgw on ingress req on community

* set inetgw to false on community on ingress del
This commit is contained in:
Abhishek K 2024-02-28 08:46:51 +07:00 committed by GitHub
parent fd95e80dc7
commit 0638dcac49
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 445 additions and 96 deletions

View file

@ -1,4 +1,4 @@
skynet {
. {
reload 15s
hosts /root/dnsconfig/netmaker.hosts {
fallthrough

View file

@ -222,7 +222,7 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
gwendpoint = fmt.Sprintf("%s:%d", host.EndpointIP.String(), host.ListenPort)
}
var newAllowedIPs string
if logic.IsInternetGw(gwnode) {
if logic.IsInternetGw(gwnode) || gwnode.InternetGwID != "" {
egressrange := "0.0.0.0/0"
if gwnode.Address6.IP != nil && client.Address6 != "" {
egressrange += "," + "::/0"

View file

@ -141,6 +141,9 @@ func pull(w http.ResponseWriter, r *http.Request) {
HostNetworkInfo: hPU.HostNetworkInfo,
EgressRoutes: hPU.EgressRoutes,
FwUpdate: hPU.FwUpdate,
ChangeDefaultGw: hPU.ChangeDefaultGw,
DefaultGwIp: hPU.DefaultGwIp,
IsInternetGw: hPU.IsInternetGw,
}
logger.Log(1, hostID, "completed a pull")

View file

@ -345,7 +345,7 @@ func getNode(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
nodeid := params["nodeid"]
node, err := validateParams(nodeid, params["network"])
node, err := logic.ValidateParams(nodeid, params["network"])
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
@ -402,9 +402,9 @@ func getNode(w http.ResponseWriter, r *http.Request) {
func createEgressGateway(w http.ResponseWriter, r *http.Request) {
var gateway models.EgressGatewayRequest
var params = mux.Vars(r)
node, err := validateParams(params["nodeid"], params["network"])
node, err := logic.ValidateParams(params["nodeid"], params["network"])
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request"))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
w.Header().Set("Content-Type", "application/json")
@ -453,9 +453,9 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
nodeid := params["nodeid"]
netid := params["network"]
node, err := validateParams(nodeid, netid)
node, err := logic.ValidateParams(nodeid, netid)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request"))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
node, err = logic.DeleteEgressGateway(netid, nodeid)
@ -497,9 +497,9 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
nodeid := params["nodeid"]
netid := params["network"]
node, err := validateParams(nodeid, netid)
node, err := logic.ValidateParams(nodeid, netid)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request"))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
var request models.IngressRequest
@ -540,9 +540,9 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
nodeid := params["nodeid"]
netid := params["network"]
node, err := validateParams(nodeid, netid)
node, err := logic.ValidateParams(nodeid, netid)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request"))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
node, removedClients, err := logic.DeleteIngressGateway(nodeid)
@ -618,9 +618,9 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
//start here
nodeid := params["nodeid"]
currentNode, err := validateParams(nodeid, params["network"])
currentNode, err := logic.ValidateParams(nodeid, params["network"])
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request"))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
var newData models.ApiNode
@ -636,6 +636,14 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
return
}
newNode := newData.ConvertToServerNode(&currentNode)
if newNode.IsInternetGateway != currentNode.IsInternetGateway {
if newNode.IsInternetGateway {
logic.SetInternetGw(newNode, models.InetNodeReq{})
} else {
logic.UnsetInternetGw(newNode)
}
}
relayUpdate := logic.RelayUpdates(&currentNode, newNode)
_, err = logic.GetHost(newNode.HostID.String())
if err != nil {
@ -695,9 +703,9 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
// get params
var params = mux.Vars(r)
var nodeid = params["nodeid"]
node, err := validateParams(nodeid, params["network"])
node, err := logic.ValidateParams(nodeid, params["network"])
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request"))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
forceDelete := r.URL.Query().Get("force") == "true"
@ -716,16 +724,3 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
go mq.PublishMqUpdatesForDeletedNode(node, !fromNode, gwClients)
}
func validateParams(nodeid, netid string) (models.Node, error) {
node, err := logic.GetNodeByID(nodeid)
if err != nil {
slog.Error("error fetching node", "node", nodeid, "error", err.Error())
return node, fmt.Errorf("error fetching node during parameter validation: %v", err)
}
if node.Network != netid {
slog.Error("network url param does not match node id", "url nodeid", netid, "node", node.Network)
return node, fmt.Errorf("network url param does not match node network")
}
return node, nil
}

View file

@ -132,11 +132,7 @@ func TestGetNetworkNodes(t *testing.T) {
func TestValidateEgressGateway(t *testing.T) {
var gateway models.EgressGatewayRequest
t.Run("EmptyRange", func(t *testing.T) {
gateway.Ranges = []string{}
err := logic.ValidateEgressGateway(gateway)
assert.EqualError(t, err, "IP Ranges Cannot Be Empty")
})
t.Run("Success", func(t *testing.T) {
gateway.Ranges = []string{"10.100.100.0/24"}
err := logic.ValidateEgressGateway(gateway)

View file

@ -7,17 +7,13 @@ import (
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
)
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
}
)
// IsInternetGw - checks if node is acting as internet gw
func IsInternetGw(node models.Node) bool {
return node.IsInternetGateway
}
// GetInternetGateways - gets all the nodes that are internet gateways
func GetInternetGateways() ([]models.Node, error) {
@ -27,13 +23,8 @@ func GetInternetGateways() ([]models.Node, error) {
}
igs := make([]models.Node, 0)
for _, node := range nodes {
if !node.IsEgressGateway {
continue
}
for _, ran := range node.EgressGatewayRanges {
if ran == "0.0.0.0/0" {
igs = append(igs, node)
}
if node.IsInternetGateway {
igs = append(igs, node)
}
}
return igs, nil
@ -167,7 +158,9 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq
return models.Node{}, err
}
node.IsIngressGateway = true
SetInternetGw(&node, ingress.IsInternetGateway)
if !servercfg.IsPro {
node.IsInternetGateway = ingress.IsInternetGateway
}
node.IngressGatewayRange = network.AddressRange
node.IngressGatewayRange6 = network.AddressRange6
node.IngressDNS = ingress.ExtclientDNS
@ -223,7 +216,9 @@ 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
if !servercfg.IsPro {
node.IsInternetGateway = false
}
node.IngressGatewayRange = ""
node.Metadata = ""
err = UpsertNode(&node)

View file

@ -218,6 +218,23 @@ func DeleteNode(node *models.Node, purge bool) error {
// unset all the relayed nodes
SetRelayedNodes(false, node.ID.String(), node.RelayedNodes)
}
if node.InternetGwID != "" {
inetNode, err := GetNodeByID(node.InternetGwID)
if err == nil {
clientNodeIDs := []string{}
for _, inetNodeClientID := range inetNode.InetNodeReq.InetNodeClientIDs {
if inetNodeClientID == node.ID.String() {
continue
}
clientNodeIDs = append(clientNodeIDs, inetNodeClientID)
}
inetNode.InetNodeReq.InetNodeClientIDs = clientNodeIDs
UpsertNode(&inetNode)
}
}
if node.IsInternetGateway {
UnsetInternetGw(node)
}
if !purge && !alreadyDeleted {
newnode := *node
@ -598,3 +615,16 @@ func SortApiNodes(unsortedNodes []models.ApiNode) {
return unsortedNodes[i].ID < unsortedNodes[j].ID
})
}
func ValidateParams(nodeid, netid string) (models.Node, error) {
node, err := GetNodeByID(nodeid)
if err != nil {
slog.Error("error fetching node", "node", nodeid, "error", err.Error())
return node, fmt.Errorf("error fetching node during parameter validation: %v", err)
}
if node.Network != netid {
slog.Error("network url param does not match node id", "url nodeid", netid, "node", node.Network)
return node, fmt.Errorf("network url param does not match node network")
}
return node, nil
}

View file

@ -2,6 +2,7 @@ package logic
import (
"errors"
"fmt"
"net"
"net/netip"
@ -28,10 +29,30 @@ var (
GetFailOverPeerIps = func(peer, node *models.Node) []net.IPNet {
return []net.IPNet{}
}
// CreateFailOver - creates failover in a network
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{}
}
)
// GetPeerUpdateForHost - gets the consolidated peer update for the host from all networks
@ -122,7 +143,10 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
}
continue
}
hostPeerUpdate = SetDefaultGw(node, hostPeerUpdate)
if !hostPeerUpdate.IsInternetGw {
hostPeerUpdate.IsInternetGw = IsInternetGw(node)
}
currentPeers := GetNetworkNodesMemory(allNodes, node.Network)
for _, peer := range currentPeers {
peer := peer
@ -164,6 +188,9 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1
continue
}
if node.IsRelayed && node.RelayedBy == peer.ID.String() {
hostPeerUpdate = SetDefaultGwForRelayedUpdate(node, peer, hostPeerUpdate)
}
}
uselocal := false
@ -251,18 +278,8 @@ 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(),
@ -274,21 +291,21 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
}
}
if IsInternetGw(node) && !addedInetGwRanges {
if IsInternetGw(node) {
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(),
hostPeerUpdate.FwUpdate.EgressInfo[fmt.Sprintf("%s-%s", node.ID.String(), "inet")] = models.EgressInfo{
EgressID: fmt.Sprintf("%s-%s", node.ID.String(), "inet"),
Network: node.PrimaryAddressIPNet(),
EgressGwAddr: net.IPNet{
IP: net.ParseIP(node.PrimaryAddress()),
Mask: getCIDRMaskFromAddr(node.PrimaryAddress()),
},
EgressGWCfg: models.EgressGatewayRequest{
NodeID: node.ID.String(),
NodeID: fmt.Sprintf("%s-%s", node.ID.String(), "inet"),
NetID: node.Network,
NatEnabled: "yes",
Ranges: egressrange,
@ -354,7 +371,17 @@ func GetPeerListenPort(host *models.Host) int {
// GetAllowedIPs - calculates the wireguard allowedip field for a peer of a node based on the peer and node settings
func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet {
var allowedips []net.IPNet
allowedips = getNodeAllowedIPs(peer, node)
if peer.IsInternetGateway && node.InternetGwID == peer.ID.String() {
allowedips = append(allowedips, GetAllowedIpForInetNodeClient(node, peer)...)
return allowedips
}
if node.IsRelayed && node.RelayedBy == peer.ID.String() {
allowedips = append(allowedips, GetAllowedIpsForRelayed(node, peer)...)
if peer.InternetGwID != "" {
return allowedips
}
}
allowedips = append(allowedips, getNodeAllowedIPs(peer, node)...)
// handle ingress gateway peers
if peer.IsIngressGateway {
@ -366,9 +393,7 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet
allowedips = append(allowedips, extPeer.AllowedIPs...)
}
}
if node.IsRelayed && node.RelayedBy == peer.ID.String() {
allowedips = append(allowedips, GetAllowedIpsForRelayed(node, peer)...)
}
return allowedips
}

View file

@ -1,8 +1,9 @@
package logic
import (
"github.com/gravitl/netmaker/models"
"net"
"github.com/gravitl/netmaker/models"
)
var GetRelays = func() ([]models.Node, error) {

View file

@ -28,21 +28,22 @@ 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"`
IngressDns string `json:"ingressdns"`
Server string `json:"server"`
InternetGateway string `json:"internetgateway"`
Connected bool `json:"connected"`
PendingDelete bool `json:"pendingdelete"`
Metadata string `json:"metadata" validate:"max=256"`
// == PRO ==
DefaultACL string `json:"defaultacl,omitempty" validate:"checkyesornoorunset"`
IsFailOver bool `json:"is_fail_over"`
FailOverPeers map[string]struct{} `json:"fail_over_peers" yaml:"fail_over_peers"`
FailedOverBy uuid.UUID `json:"failed_over_by" yaml:"failed_over_by"`
DefaultACL string `json:"defaultacl,omitempty" validate:"checkyesornoorunset"`
IsFailOver bool `json:"is_fail_over"`
FailOverPeers map[string]struct{} `json:"fail_over_peers" yaml:"fail_over_peers"`
FailedOverBy uuid.UUID `json:"failed_over_by" yaml:"failed_over_by"`
IsInternetGateway bool `json:"isinternetgateway" yaml:"isinternetgateway"`
InetNodeReq InetNodeReq `json:"inet_node_req" yaml:"inet_node_req"`
InternetGwID string `json:"internetgw_node_id" yaml:"internetgw_node_id"`
}
// ApiNode.ConvertToServerNode - converts an api node to a server node
@ -72,6 +73,8 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node {
convertedNode.IsInternetGateway = a.IsInternetGateway
convertedNode.EgressGatewayRequest = currentNode.EgressGatewayRequest
convertedNode.EgressGatewayNatEnabled = currentNode.EgressGatewayNatEnabled
convertedNode.InternetGwID = currentNode.InternetGwID
convertedNode.InetNodeReq = currentNode.InetNodeReq
convertedNode.RelayedNodes = a.RelayedNodes
convertedNode.DefaultACL = a.DefaultACL
convertedNode.OwnerID = currentNode.OwnerID
@ -150,13 +153,12 @@ func (nm *Node) ConvertToAPINode() *ApiNode {
apiNode.DNSOn = nm.DNSOn
apiNode.IngressDns = nm.IngressDNS
apiNode.Server = nm.Server
if isEmptyAddr(apiNode.InternetGateway) {
apiNode.InternetGateway = ""
}
apiNode.Connected = nm.Connected
apiNode.PendingDelete = nm.PendingDelete
apiNode.DefaultACL = nm.DefaultACL
apiNode.IsInternetGateway = nm.IsInternetGateway
apiNode.InternetGwID = nm.InternetGwID
apiNode.InetNodeReq = nm.InetNodeReq
apiNode.IsFailOver = nm.IsFailOver
apiNode.FailOverPeers = nm.FailOverPeers
apiNode.FailedOverBy = nm.FailedOverBy

View file

@ -9,6 +9,9 @@ import (
// HostPeerUpdate - struct for host peer updates
type HostPeerUpdate struct {
Host Host `json:"host" bson:"host" yaml:"host"`
ChangeDefaultGw bool `json:"change_default_gw"`
DefaultGwIp net.IP `json:"default_gw_ip"`
IsInternetGw bool `json:"is_inet_gw"`
NodeAddrs []net.IPNet `json:"nodes_addrs" yaml:"nodes_addrs"`
Server string `json:"server" bson:"server" yaml:"server"`
ServerVersion string `json:"serverversion" bson:"serverversion" yaml:"serverversion"`

View file

@ -66,7 +66,6 @@ type CommonNode struct {
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"`
@ -89,11 +88,14 @@ type Node struct {
IngressGatewayRange6 string `json:"ingressgatewayrange6" bson:"ingressgatewayrange6" yaml:"ingressgatewayrange6"`
Metadata string `json:"metadata"`
// == PRO ==
DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
OwnerID string `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"`
IsFailOver bool `json:"is_fail_over" yaml:"is_fail_over"`
FailOverPeers map[string]struct{} `json:"fail_over_peers" yaml:"fail_over_peers"`
FailedOverBy uuid.UUID `json:"failed_over_by" yaml:"failed_over_by"`
DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
OwnerID string `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"`
IsFailOver bool `json:"is_fail_over" yaml:"is_fail_over"`
FailOverPeers map[string]struct{} `json:"fail_over_peers" yaml:"fail_over_peers"`
FailedOverBy uuid.UUID `json:"failed_over_by" yaml:"failed_over_by"`
IsInternetGateway bool `json:"isinternetgateway" yaml:"isinternetgateway"`
InetNodeReq InetNodeReq `json:"inet_node_req" yaml:"inet_node_req"`
InternetGwID string `json:"internetgw_node_id" yaml:"internetgw_node_id"`
}
// LegacyNode - legacy struct for node model

View file

@ -1,6 +1,7 @@
package models
import (
"net"
"strings"
"time"
@ -196,6 +197,11 @@ type IngressRequest struct {
IsInternetGateway bool `json:"is_internet_gw"`
}
// InetNodeReq - exit node request struct
type InetNodeReq struct {
InetNodeClientIDs []string `json:"inet_node_client_ids"`
}
// ServerUpdateData - contains data to configure server
// and if it should set peers
type ServerUpdateData struct {
@ -234,6 +240,12 @@ type HostPull struct {
HostNetworkInfo HostInfoMap `json:"host_network_info,omitempty" yaml:"host_network_info,omitempty"`
EgressRoutes []EgressNetworkRoutes `json:"egress_network_routes"`
FwUpdate FwUpdate `json:"fw_update"`
ChangeDefaultGw bool `json:"change_default_gw"`
DefaultGwIp net.IP `json:"default_gw_ip"`
IsInternetGw bool `json:"is_inet_gw"`
}
type DefaultGwInfo struct {
}
// NodeGet - struct for a single node get response

164
pro/controllers/inet_gws.go Normal file
View file

@ -0,0 +1,164 @@
package controllers
import (
"encoding/json"
"errors"
"net/http"
"github.com/gorilla/mux"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
proLogic "github.com/gravitl/netmaker/pro/logic"
)
// 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)
}
// swagger:route POST /api/nodes/{network}/{nodeid}/inet_gw nodes createInternetGw
//
// Create an inet node.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
func createInternetGw(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
}
if node.IsInternetGateway {
logic.ReturnSuccessResponse(w, r, "node is already acting as internet gateway")
return
}
var request models.InetNodeReq
err = json.NewDecoder(r.Body).Decode(&request)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
host, err := logic.GetHost(node.HostID.String())
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
if host.OS != models.OS_Types.Linux {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only linux nodes can be made internet gws"), "badrequest"))
return
}
err = proLogic.ValidateInetGwReq(node, request, false)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
proLogic.SetInternetGw(&node, request)
err = logic.UpsertNode(&node)
if err != nil {
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 mq.PublishPeerUpdate(false)
}
// swagger:route PUT /api/nodes/{network}/{nodeid}/inet_gw nodes updateInternetGw
//
// update an inet node.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
func updateInternetGw(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.InetNodeReq
err = json.NewDecoder(r.Body).Decode(&request)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
if !node.IsInternetGateway {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node is not a internet gw"), "badrequest"))
return
}
err = proLogic.ValidateInetGwReq(node, request, true)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
proLogic.UnsetInternetGw(&node)
proLogic.SetInternetGw(&node, request)
err = logic.UpsertNode(&node)
if err != nil {
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 mq.PublishPeerUpdate(false)
}
// swagger:route DELETE /api/nodes/{network}/{nodeid}/inet_gw nodes deleteInternetGw
//
// Delete an internet gw.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
func deleteInternetGw(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
}
proLogic.UnsetInternetGw(&node)
err = logic.UpsertNode(&node)
if err != nil {
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 mq.PublishPeerUpdate(false)
}

View file

@ -31,6 +31,7 @@ func InitPro() {
proControllers.RelayHandlers,
proControllers.UserHandlers,
proControllers.FailOverHandlers,
proControllers.InetHandlers,
)
logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {
// == License Handling ==
@ -100,9 +101,12 @@ func InitPro() {
logic.UpdateRelayed = proLogic.UpdateRelayed
logic.SetRelayedNodes = proLogic.SetRelayedNodes
logic.RelayUpdates = proLogic.RelayUpdates
logic.IsInternetGw = proLogic.IsInternetGw
logic.SetInternetGw = proLogic.SetInternetGw
logic.GetTrialEndDate = getTrialEndDate
logic.SetDefaultGw = proLogic.SetDefaultGw
logic.SetDefaultGwForRelayedUpdate = proLogic.SetDefaultGwForRelayedUpdate
logic.UnsetInternetGw = proLogic.UnsetInternetGw
logic.SetInternetGw = proLogic.SetInternetGw
logic.GetAllowedIpForInetNodeClient = proLogic.GetAllowedIpForInetNodeClient
mq.UpdateMetrics = proLogic.MQUpdateMetrics
mq.UpdateMetricsFallBack = proLogic.MQUpdateMetricsFallBack
}

View file

@ -1,24 +1,132 @@
package logic
import (
celogic "github.com/gravitl/netmaker/logic"
"errors"
"fmt"
"net"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"golang.org/x/exp/slog"
)
// IsInternetGw - checks if node is acting as internet gw
func IsInternetGw(node models.Node) bool {
return node.IsInternetGateway
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
}
clientHost, err := logic.GetHost(clientNode.HostID.String())
if err != nil {
return err
}
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.IsRelayed {
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, flag bool) {
node.IsInternetGateway = flag
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 != "" {
peerUpdate.ChangeDefaultGw = true
peerUpdate.DefaultGwIp = relay.Address.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
}
peerUpdate.ChangeDefaultGw = true
peerUpdate.DefaultGwIp = inetNode.Address.IP
}
return peerUpdate
}
// GetNetworkIngresses - gets the gateways of a network
func GetNetworkIngresses(network string) ([]models.Node, error) {
var ingresses []models.Node
netNodes, err := celogic.GetNetworkNodes(network)
netNodes, err := logic.GetNetworkNodes(network)
if err != nil {
return []models.Node{}, err
}
@ -29,3 +137,9 @@ func GetNetworkIngresses(network string) ([]models.Node, error) {
}
return ingresses, nil
}
// GetAllowedIpsForInet - get inet cidr for node using a inet gw
func GetAllowedIpForInetNodeClient(node, peer *models.Node) []net.IPNet {
_, ipnet, _ := net.ParseCIDR("0.0.0.0/0")
return []net.IPNet{*ipnet}
}

View file

@ -204,6 +204,9 @@ func GetAllowedIpsForRelayed(relayed, relay *models.Node) (allowedIPs []net.IPNe
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())