NET-1932: Merge egress and internet gateways (#3436)

* feat: api access tokens

* revoke all user tokens

* redefine access token api routes, add auto egress option to enrollment keys

* add server settings apis, add db table for settigs

* handle server settings updates

* switch to using settings from DB

* fix sever settings migration

* revet force migration for settings

* fix server settings database write

* egress model

* fix revoked tokens to be unauthorized

* update egress model

* remove unused functions

* convert access token to sql schema

* switch access token to sql schema

* fix merge conflicts

* fix server settings types

* bypass basic auth setting for super admin

* add TODO comment

* setup api handlers for egress revamp

* use single DB, fix update nat boolean field

* extend validaiton checks for egress ranges

* add migration to convert to new egress model

* fix panic interface conversion

* publish peer update on settings update

* revoke token generated by an user

* add user token creation restriction by user role

* add forbidden check for access token creation

* revoke user token when group or role is changed

* add default group to admin users on update

* chore(go): import style changes from migration branch;

1. Singular file names for table schema.
2. No table name method.
3. Use .Model instead of .Table.
4. No unnecessary tagging.

* remove nat check on egress gateway request

* Revert "remove nat check on egress gateway request"

This reverts commit 0aff12a189.

* remove nat check on egress gateway request

* feat(go): add db middleware;

* feat(go): restore method;

* feat(go): add user access token schema;

* add inet gw status to egress model

* fetch node ids in the tag, add inet gw info clients

* add inet gw info to node from egress list

* add migration logic internet gws

* create default acl policies

* add egress info

* add egress TODO

* add egress TODO

* fix user auth api:

* add reference id to acl policy

* add egress response from DB

* publish peer update on egress changes

* re initalise oauth and email config

* set verbosity

* normalise cidr on egress req

* add egress id to acl group

* change acls to use egress id

* resolve merge conflicts

* fix egress reference errors

* move egress model to schema

* add api context to DB

* sync auto update settings with hosts

* sync auto update settings with hosts

* check acl for egress node

* check for egress policy in the acl dst groups

* fix acl rules for egress policies with new models

* add status to egress model

* fix inet node func

* mask secret and convert jwt duration to minutes

* enable egress policies on creation

* convert jwt duration to minutes

* add relevant ranges to inet egress

* skip non active egress routes

* resolve merge conflicts

* fix static check

* update gorm tag for primary key on egress model

* create user policies for egress resources

* resolve merge conflicts

* get egress info on failover apis, add egress src validation for inet gws

* add additional validation checks on egress req

* add additional validation checks on egress req

* skip all resources for inet policy

* delete associated egress acl policies

* fix failover of inetclient

* avoid setting inet client asd inet gw

* fix all resource egress policy

* fix inet gw egress rule

* check for node egress on relay req

* fix egress acl rules comms

* add new field for egress info on node

* check acl policy in failover ctx

* avoid default host to be set as inet client

* fix relayed egress node

* add valid error messaging for egress validate func

* return if inet default host

* jump port detection to 51821

* check host ports on pull

* check user access gws via acls

* add validation check for default host and failover for inet clients

* add error messaging for acl policy check

* fix inet gw status

* ignore failover req for peer using inet gw

* check for allowed egress ranges for a peer

* add egress routes to static nodes by access

* avoid setting failvoer as inet client

* fix egress error messaging

* fix extclients egress comms

* fix inet gw acting as inet client

* return formatted error on update acl validation

* add default route for static nodes on inetclient

* check relay node acting as inetclient

* move inet node info to separate field, fix all resouces policy

* remove debug logs

---------

Co-authored-by: Vishal Dalwadi <dalwadivishal26@gmail.com>
This commit is contained in:
Abhishek K
2025-05-21 12:50:21 +05:30
committed by GitHub
parent 1df8991202
commit 307a3d1e4b
44 changed files with 1651 additions and 444 deletions

View File

@@ -165,7 +165,7 @@ func SessionHandler(conn *websocket.Conn) {
return return
} }
} }
logic.CheckHostPorts(&result.Host) _ = logic.CheckHostPorts(&result.Host)
if err := logic.CreateHost(&result.Host); err != nil { if err := logic.CreateHost(&result.Host); err != nil {
handleHostRegErr(conn, err) handleHostRegErr(conn, err)
return return

View File

@@ -51,7 +51,7 @@ func aclPolicyTypes(w http.ResponseWriter, r *http.Request) {
DstGroupTypes: []models.AclGroupType{ DstGroupTypes: []models.AclGroupType{
models.NodeTagID, models.NodeTagID,
models.NodeID, models.NodeID,
models.EgressRange, models.EgressID,
// models.NetmakerIPAclID, // models.NetmakerIPAclID,
// models.NetmakerSubNetRangeAClID, // models.NetmakerSubNetRangeAClID,
}, },
@@ -171,6 +171,7 @@ func aclDebug(w http.ResponseWriter, r *http.Request) {
IsPeerAllowed bool IsPeerAllowed bool
Policies []models.Acl Policies []models.Acl
IngressRules []models.FwRule IngressRules []models.FwRule
NodeAllPolicy bool
} }
allowed, ps := logic.IsNodeAllowedToCommunicateV1(node, peer, true) allowed, ps := logic.IsNodeAllowedToCommunicateV1(node, peer, true)
@@ -253,8 +254,8 @@ func createAcl(w http.ResponseWriter, r *http.Request) {
acl.Proto = models.ALL acl.Proto = models.ALL
} }
// validate create acl policy // validate create acl policy
if !logic.IsAclPolicyValid(acl) { if err := logic.IsAclPolicyValid(acl); err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid policy"), "badrequest")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return return
} }
err = logic.InsertAcl(acl) err = logic.InsertAcl(acl)
@@ -292,8 +293,8 @@ func updateAcl(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return return
} }
if !logic.IsAclPolicyValid(updateAcl.Acl) { if err := logic.IsAclPolicyValid(updateAcl.Acl); err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid policy"), "badrequest")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return return
} }
if updateAcl.Acl.NetworkID != acl.NetworkID { if updateAcl.Acl.NetworkID != acl.NetworkID {

View File

@@ -39,6 +39,7 @@ var HttpHandlers = []interface{}{
enrollmentKeyHandlers, enrollmentKeyHandlers,
tagHandlers, tagHandlers,
aclHandlers, aclHandlers,
egressHandlers,
legacyHandlers, legacyHandlers,
} }

257
controllers/egress.go Normal file
View File

@@ -0,0 +1,257 @@
package controller
import (
"context"
"encoding/json"
"errors"
"net/http"
"time"
"github.com/google/uuid"
"github.com/gorilla/mux"
"github.com/gravitl/netmaker/db"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
"github.com/gravitl/netmaker/schema"
"gorm.io/datatypes"
)
func egressHandlers(r *mux.Router) {
r.HandleFunc("/api/v1/egress", logic.SecurityCheck(true, http.HandlerFunc(createEgress))).Methods(http.MethodPost)
r.HandleFunc("/api/v1/egress", logic.SecurityCheck(true, http.HandlerFunc(listEgress))).Methods(http.MethodGet)
r.HandleFunc("/api/v1/egress", logic.SecurityCheck(true, http.HandlerFunc(updateEgress))).Methods(http.MethodPut)
r.HandleFunc("/api/v1/egress", logic.SecurityCheck(true, http.HandlerFunc(deleteEgress))).Methods(http.MethodDelete)
}
// @Summary Create Egress Resource
// @Router /api/v1/egress [post]
// @Tags Auth
// @Accept json
// @Param body body models.Egress
// @Success 200 {object} models.SuccessResponse
// @Failure 400 {object} models.ErrorResponse
// @Failure 401 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func createEgress(w http.ResponseWriter, r *http.Request) {
var req models.EgressReq
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
logger.Log(0, "error decoding request body: ",
err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
var egressRange string
if !req.IsInetGw {
egressRange, err = logic.NormalizeCIDR(req.Range)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
} else {
egressRange = "*"
}
e := schema.Egress{
ID: uuid.New().String(),
Name: req.Name,
Network: req.Network,
Description: req.Description,
Range: egressRange,
Nat: req.Nat,
IsInetGw: req.IsInetGw,
Nodes: make(datatypes.JSONMap),
Tags: make(datatypes.JSONMap),
Status: true,
CreatedBy: r.Header.Get("user"),
CreatedAt: time.Now().UTC(),
}
for nodeID, metric := range req.Nodes {
e.Nodes[nodeID] = metric
}
if err := logic.ValidateEgressReq(&e); err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
err = e.Create(db.WithContext(r.Context()))
if err != nil {
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("error creating egress resource"+err.Error()), "internal"),
)
return
}
// for nodeID := range e.Nodes {
// node, err := logic.GetNodeByID(nodeID)
// if err != nil {
// logic.AddEgressInfoToNode(&node, e)
// logic.UpsertNode(&node)
// }
// }
go mq.PublishPeerUpdate(false)
logic.ReturnSuccessResponseWithJson(w, r, e, "created egress resource")
}
// @Summary List Egress Resource
// @Router /api/v1/egress [get]
// @Tags Auth
// @Accept json
// @Param query network string
// @Success 200 {object} models.SuccessResponse
// @Failure 400 {object} models.ErrorResponse
// @Failure 401 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func listEgress(w http.ResponseWriter, r *http.Request) {
network := r.URL.Query().Get("network")
if network == "" {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network is required"), "badrequest"))
return
}
e := schema.Egress{Network: network}
list, err := e.ListByNetwork(db.WithContext(r.Context()))
if err != nil {
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("error listing egress resource"+err.Error()), "internal"),
)
return
}
logic.ReturnSuccessResponseWithJson(w, r, list, "fetched egress resource list")
}
// @Summary Update Egress Resource
// @Router /api/v1/egress [put]
// @Tags Auth
// @Accept json
// @Param body body models.Egress
// @Success 200 {object} models.SuccessResponse
// @Failure 400 {object} models.ErrorResponse
// @Failure 401 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func updateEgress(w http.ResponseWriter, r *http.Request) {
var req models.EgressReq
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
logger.Log(0, "error decoding request body: ",
err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
var egressRange string
if !req.IsInetGw {
egressRange, err = logic.NormalizeCIDR(req.Range)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
} else {
egressRange = "*"
}
e := schema.Egress{ID: req.ID}
err = e.Get(db.WithContext(r.Context()))
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
var updateNat bool
var updateInetGw bool
var updateStatus bool
if req.Nat != e.Nat {
updateNat = true
}
if req.IsInetGw != e.IsInetGw {
updateInetGw = true
}
if req.Status != e.Status {
updateStatus = true
}
e.Nodes = make(datatypes.JSONMap)
e.Tags = make(datatypes.JSONMap)
for nodeID, metric := range req.Nodes {
e.Nodes[nodeID] = metric
}
e.Range = egressRange
e.Description = req.Description
e.Name = req.Name
e.Nat = req.Nat
e.Status = req.Status
e.IsInetGw = req.IsInetGw
e.UpdatedAt = time.Now().UTC()
if err := logic.ValidateEgressReq(&e); err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
err = e.Update(db.WithContext(context.TODO()))
if err != nil {
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("error creating egress resource"+err.Error()), "internal"),
)
return
}
if updateNat {
e.Nat = req.Nat
e.UpdateNatStatus(db.WithContext(context.TODO()))
}
if updateInetGw {
e.IsInetGw = req.IsInetGw
e.UpdateINetGwStatus(db.WithContext(context.TODO()))
}
if updateStatus {
e.Status = req.Status
e.UpdateEgressStatus(db.WithContext(context.TODO()))
}
go mq.PublishPeerUpdate(false)
logic.ReturnSuccessResponseWithJson(w, r, e, "updated egress resource")
}
// @Summary Delete Egress Resource
// @Router /api/v1/egress [delete]
// @Tags Auth
// @Accept json
// @Param body body models.Egress
// @Success 200 {object} models.SuccessResponse
// @Failure 400 {object} models.ErrorResponse
// @Failure 401 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func deleteEgress(w http.ResponseWriter, r *http.Request) {
id := r.URL.Query().Get("id")
if id == "" {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("id is required"), "badrequest"))
return
}
e := schema.Egress{ID: id}
err := e.Delete(db.WithContext(r.Context()))
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
// delete related acl policies
acls := logic.ListAcls()
for _, acl := range acls {
for i := len(acl.Dst) - 1; i >= 0; i-- {
if acl.Dst[i].ID == models.EgressID && acl.Dst[i].Value == id {
acl.Dst = append(acl.Dst[:i], acl.Dst[i+1:]...)
}
}
if len(acl.Dst) == 0 {
logic.DeleteAcl(acl)
} else {
logic.UpsertAcl(acl)
}
}
go mq.PublishPeerUpdate(false)
logic.ReturnSuccessResponseWithJson(w, r, nil, "deleted egress resource")
}

View File

@@ -302,7 +302,7 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
if !hostExists { if !hostExists {
newHost.PersistentKeepalive = models.DefaultPersistentKeepAlive newHost.PersistentKeepalive = models.DefaultPersistentKeepAlive
// register host // register host
logic.CheckHostPorts(&newHost) _ = logic.CheckHostPorts(&newHost)
// create EMQX credentials and ACLs for host // create EMQX credentials and ACLs for host
if servercfg.GetBrokerType() == servercfg.EmqxBrokerType { if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
if err := mq.GetEmqxHandler().CreateEmqxUser(newHost.ID.String(), newHost.HostPass); err != nil { if err := mq.GetEmqxHandler().CreateEmqxUser(newHost.ID.String(), newHost.HostPass); err != nil {

View File

@@ -174,6 +174,7 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return return
} }
logic.GetNodeEgressInfo(&gwnode)
host, err := logic.GetHost(gwnode.HostID.String()) host, err := logic.GetHost(gwnode.HostID.String())
if err != nil { if err != nil {
logger.Log( logger.Log(
@@ -261,7 +262,7 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
} }
var newAllowedIPs string var newAllowedIPs string
if logic.IsInternetGw(gwnode) || gwnode.InternetGwID != "" { if logic.IsInternetGw(gwnode) || gwnode.EgressDetails.InternetGwID != "" {
egressrange := "0.0.0.0/0" egressrange := "0.0.0.0/0"
if gwnode.Address6.IP != nil && client.Address6 != "" { if gwnode.Address6.IP != nil && client.Address6 != "" {
egressrange += "," + "::/0" egressrange += "," + "::/0"
@@ -540,7 +541,7 @@ func getExtClientHAConf(w http.ResponseWriter, r *http.Request) {
keepalive = "PersistentKeepalive = " + strconv.Itoa(int(gwnode.IngressPersistentKeepalive)) keepalive = "PersistentKeepalive = " + strconv.Itoa(int(gwnode.IngressPersistentKeepalive))
} }
var newAllowedIPs string var newAllowedIPs string
if logic.IsInternetGw(gwnode) || gwnode.InternetGwID != "" { if logic.IsInternetGw(gwnode) || gwnode.EgressDetails.InternetGwID != "" {
egressrange := "0.0.0.0/0" egressrange := "0.0.0.0/0"
if gwnode.Address6.IP != nil && client.Address6 != "" { if gwnode.Address6.IP != nil && client.Address6 != "" {
egressrange += "," + "::/0" egressrange += "," + "::/0"
@@ -688,7 +689,7 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
var gateway models.EgressGatewayRequest var gateway models.EgressGatewayRequest
gateway.NetID = params["network"] gateway.NetID = params["network"]
gateway.Ranges = customExtClient.ExtraAllowedIPs gateway.Ranges = customExtClient.ExtraAllowedIPs
err := logic.ValidateEgressRange(gateway) err := logic.ValidateEgressRange(gateway.NetID, gateway.Ranges)
if err != nil { if err != nil {
logger.Log(0, r.Header.Get("user"), "error validating egress range: ", err.Error()) logger.Log(0, r.Header.Get("user"), "error validating egress range: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
@@ -876,7 +877,7 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
var gateway models.EgressGatewayRequest var gateway models.EgressGatewayRequest
gateway.NetID = params["network"] gateway.NetID = params["network"]
gateway.Ranges = update.ExtraAllowedIPs gateway.Ranges = update.ExtraAllowedIPs
err = logic.ValidateEgressRange(gateway) err = logic.ValidateEgressRange(gateway.NetID, gateway.Ranges)
if err != nil { if err != nil {
logger.Log(0, r.Header.Get("user"), "error validating egress range: ", err.Error()) logger.Log(0, r.Header.Get("user"), "error validating egress range: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))

View File

@@ -216,7 +216,7 @@ func pull(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(keyErr, "internal")) logic.ReturnErrorResponse(w, r, logic.FormatError(keyErr, "internal"))
return return
} }
_ = logic.CheckHostPorts(host)
serverConf.TrafficKey = key serverConf.TrafficKey = key
response := models.HostPull{ response := models.HostPull{
Host: *host, Host: *host,

View File

@@ -208,7 +208,7 @@ func convertLegacyNode(legacy models.LegacyNode, hostID uuid.UUID) models.Node {
node.IsRelay = false node.IsRelay = false
node.RelayedNodes = []string{} node.RelayedNodes = []string{}
node.DNSOn = models.ParseBool(legacy.DNSOn) node.DNSOn = models.ParseBool(legacy.DNSOn)
node.LastModified = time.Now() node.LastModified = time.Now().UTC()
node.ExpirationDateTime = time.Unix(legacy.ExpirationDateTime, 0) node.ExpirationDateTime = time.Unix(legacy.ExpirationDateTime, 0)
node.EgressGatewayNatEnabled = models.ParseBool(legacy.EgressGatewayNatEnabled) node.EgressGatewayNatEnabled = models.ParseBool(legacy.EgressGatewayNatEnabled)
node.EgressGatewayRequest = legacy.EgressGatewayRequest node.EgressGatewayRequest = legacy.EgressGatewayRequest

View File

@@ -516,7 +516,7 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
} }
gateway.NetID = params["network"] gateway.NetID = params["network"]
gateway.NodeID = params["nodeid"] gateway.NodeID = params["nodeid"]
err = logic.ValidateEgressRange(gateway) err = logic.ValidateEgressRange(gateway.NetID, gateway.Ranges)
if err != nil { if err != nil {
logger.Log(0, r.Header.Get("user"), "error validating egress range: ", err.Error()) logger.Log(0, r.Header.Get("user"), "error validating egress range: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
@@ -638,13 +638,6 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
) )
return return
} }
if newNode.IsInternetGateway != currentNode.IsInternetGateway {
if newNode.IsInternetGateway {
logic.SetInternetGw(newNode, models.InetNodeReq{})
} else {
logic.UnsetInternetGw(newNode)
}
}
relayUpdate := logic.RelayUpdates(&currentNode, newNode) relayUpdate := logic.RelayUpdates(&currentNode, newNode)
if relayUpdate && newNode.IsRelay { if relayUpdate && newNode.IsRelay {
err = logic.ValidateRelay(models.RelayRequest{ err = logic.ValidateRelay(models.RelayRequest{

View File

@@ -18,100 +18,6 @@ import (
var nonLinuxHost models.Host var nonLinuxHost models.Host
var linuxHost models.Host var linuxHost models.Host
func TestCreateEgressGateway(t *testing.T) {
var gateway models.EgressGatewayRequest
gateway.Ranges = []string{"10.100.100.0/24"}
gateway.RangesWithMetric = append(gateway.RangesWithMetric, models.EgressRangeMetric{
Network: "10.100.100.0/24",
RouteMetric: 256,
})
gateway.NetID = "skynet"
deleteAllNetworks()
createNet()
t.Run("NoNodes", func(t *testing.T) {
node, err := logic.CreateEgressGateway(gateway)
assert.Equal(t, models.Node{}, node)
assert.EqualError(t, err, "could not find any records")
})
t.Run("Non-linux node", func(t *testing.T) {
createnode := createNodeWithParams("", "")
createNodeHosts()
createnode.HostID = nonLinuxHost.ID
err := logic.AssociateNodeToHost(createnode, &nonLinuxHost)
assert.Nil(t, err)
gateway.NodeID = createnode.ID.String()
node, err := logic.CreateEgressGateway(gateway)
assert.Equal(t, models.Node{}, node)
assert.EqualError(t, err, "windows is unsupported for egress gateways")
})
t.Run("Success-Nat-Enabled", func(t *testing.T) {
deleteAllNodes()
testnode := createTestNode()
gateway.NodeID = testnode.ID.String()
gateway.NatEnabled = "yes"
node, err := logic.CreateEgressGateway(gateway)
t.Log(node.EgressGatewayNatEnabled)
assert.Nil(t, err)
})
t.Run("Success-Nat-Disabled", func(t *testing.T) {
deleteAllNodes()
testnode := createTestNode()
gateway.NodeID = testnode.ID.String()
gateway.NatEnabled = "no"
node, err := logic.CreateEgressGateway(gateway)
t.Log(node.EgressGatewayNatEnabled)
assert.Nil(t, err)
})
t.Run("Success", func(t *testing.T) {
var gateway models.EgressGatewayRequest
gateway.Ranges = []string{"10.100.100.0/24"}
gateway.NetID = "skynet"
deleteAllNodes()
testnode := createTestNode()
gateway.NodeID = testnode.ID.String()
node, err := logic.CreateEgressGateway(gateway)
t.Log(node)
assert.Nil(t, err)
assert.Equal(t, true, node.IsEgressGateway)
assert.Equal(t, gateway.Ranges, node.EgressGatewayRanges)
})
}
func TestDeleteEgressGateway(t *testing.T) {
var gateway models.EgressGatewayRequest
deleteAllNetworks()
createNet()
testnode := createTestNode()
gateway.Ranges = []string{"10.100.100.0/24"}
gateway.NetID = "skynet"
gateway.NodeID = testnode.ID.String()
t.Run("Success", func(t *testing.T) {
node, err := logic.CreateEgressGateway(gateway)
assert.Nil(t, err)
assert.Equal(t, true, node.IsEgressGateway)
assert.Equal(t, []string{"10.100.100.0/24"}, node.EgressGatewayRanges)
node, err = logic.DeleteEgressGateway(gateway.NetID, gateway.NodeID)
assert.Nil(t, err)
assert.Equal(t, false, node.IsEgressGateway)
assert.Equal(t, []string([]string{}), node.EgressGatewayRanges)
})
t.Run("NotGateway", func(t *testing.T) {
node, err := logic.DeleteEgressGateway(gateway.NetID, gateway.NodeID)
assert.Nil(t, err)
assert.Equal(t, false, node.IsEgressGateway)
assert.Equal(t, []string([]string{}), node.EgressGatewayRanges)
})
t.Run("BadNode", func(t *testing.T) {
node, err := logic.DeleteEgressGateway(gateway.NetID, "01:02:03")
assert.EqualError(t, err, "no result found")
assert.Equal(t, models.Node{}, node)
deleteAllNodes()
})
}
func TestGetNetworkNodes(t *testing.T) { func TestGetNetworkNodes(t *testing.T) {
deleteAllNetworks() deleteAllNetworks()
createNet() createNet()

View File

@@ -89,7 +89,7 @@ func createTag(w http.ResponseWriter, r *http.Request) {
Network: req.Network, Network: req.Network,
CreatedBy: user.UserName, CreatedBy: user.UserName,
ColorCode: req.ColorCode, ColorCode: req.ColorCode,
CreatedAt: time.Now(), CreatedAt: time.Now().UTC(),
} }
_, err = logic.GetTag(tag.ID) _, err = logic.GetTag(tag.ID)
if err == nil { if err == nil {

3
go.mod
View File

@@ -48,6 +48,7 @@ require (
github.com/olekukonko/tablewriter v0.0.5 github.com/olekukonko/tablewriter v0.0.5
github.com/spf13/cobra v1.9.1 github.com/spf13/cobra v1.9.1
gopkg.in/mail.v2 v2.3.1 gopkg.in/mail.v2 v2.3.1
gorm.io/datatypes v1.2.5
gorm.io/driver/postgres v1.5.11 gorm.io/driver/postgres v1.5.11
gorm.io/driver/sqlite v1.5.7 gorm.io/driver/sqlite v1.5.7
gorm.io/gorm v1.26.1 gorm.io/gorm v1.26.1
@@ -57,6 +58,7 @@ require (
cloud.google.com/go/compute/metadata v0.3.0 // indirect cloud.google.com/go/compute/metadata v0.3.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-jose/go-jose/v4 v4.0.5 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect
@@ -71,6 +73,7 @@ require (
github.com/seancfoley/bintree v1.3.1 // indirect github.com/seancfoley/bintree v1.3.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/pflag v1.0.6 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gorm.io/driver/mysql v1.5.6 // indirect
) )
require ( require (

16
go.sum
View File

@@ -29,8 +29,15 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -79,6 +86,8 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -140,9 +149,16 @@ gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/datatypes v1.2.5 h1:9UogU3jkydFVW1bIVVeoYsTpLRgwDVW3rHfJG6/Ek9I=
gorm.io/datatypes v1.2.5/go.mod h1:I5FUdlKpLb5PMqeMQhm30CQ6jXP8Rj89xkTeCSAaAD4=
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314= gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314=
gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I= gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
gorm.io/driver/sqlserver v1.5.4 h1:xA+Y1KDNspv79q43bPyjDMUgHoYHLhXYmdFcYPobg8g=
gorm.io/driver/sqlserver v1.5.4/go.mod h1:+frZ/qYmuna11zHPlh5oc2O6ZA/lS88Keb0XSH1Zh/g=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.26.1 h1:ghB2gUI9FkS46luZtn6DLZ0f6ooBJ5IbVej2ENFDjRw= gorm.io/gorm v1.26.1 h1:ghB2gUI9FkS46luZtn6DLZ0f6ooBJ5IbVej2ENFDjRw=
gorm.io/gorm v1.26.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= gorm.io/gorm v1.26.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=

View File

@@ -1,6 +1,7 @@
package logic package logic
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@@ -12,10 +13,23 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/db"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/schema"
"github.com/gravitl/netmaker/servercfg" "github.com/gravitl/netmaker/servercfg"
) )
/*
TODO: EGRESS
1. allow only selection of egress ranges in a policy
ranges should be replaced by egress identifier
2. check logic required for MAC exit node
3.
*/
var ( var (
aclCacheMutex = &sync.RWMutex{} aclCacheMutex = &sync.RWMutex{}
aclCacheMap = make(map[string]models.Acl) aclCacheMap = make(map[string]models.Acl)
@@ -236,10 +250,10 @@ func GetEgressRanges(netID models.NetworkID) (map[string][]string, map[string]st
if currentNode.Network != netID.String() { if currentNode.Network != netID.String() {
continue continue
} }
if currentNode.IsEgressGateway { // add the egress gateway range(s) to the result if currentNode.EgressDetails.IsEgressGateway { // add the egress gateway range(s) to the result
if len(currentNode.EgressGatewayRanges) > 0 { if len(currentNode.EgressDetails.EgressGatewayRanges) > 0 {
nodeEgressMap[currentNode.ID.String()] = currentNode.EgressGatewayRanges nodeEgressMap[currentNode.ID.String()] = currentNode.EgressDetails.EgressGatewayRanges
for _, egressRangeI := range currentNode.EgressGatewayRanges { for _, egressRangeI := range currentNode.EgressDetails.EgressGatewayRanges {
resultMap[egressRangeI] = struct{}{} resultMap[egressRangeI] = struct{}{}
} }
} }
@@ -257,78 +271,102 @@ func GetEgressRanges(netID models.NetworkID) (map[string][]string, map[string]st
return nodeEgressMap, resultMap, nil return nodeEgressMap, resultMap, nil
} }
func checkIfAclTagisValid(t models.AclPolicyTag, netID models.NetworkID, policyType models.AclPolicyType, isSrc bool) bool { func checkIfAclTagisValid(a models.Acl, t models.AclPolicyTag, isSrc bool) (err error) {
switch t.ID { switch t.ID {
case models.NodeTagID: case models.NodeTagID:
if policyType == models.UserPolicy && isSrc { if a.RuleType == models.UserPolicy && isSrc {
return false return errors.New("user policy source mismatch")
} }
// check if tag is valid // check if tag is valid
_, err := GetTag(models.TagID(t.Value)) _, err := GetTag(models.TagID(t.Value))
if err != nil { if err != nil {
return false return errors.New("invalid tag " + t.Value)
} }
case models.NodeID: case models.NodeID:
if policyType == models.UserPolicy && isSrc { if a.RuleType == models.UserPolicy && isSrc {
return false return errors.New("user policy source mismatch")
} }
_, nodeErr := GetNodeByID(t.Value) _, nodeErr := GetNodeByID(t.Value)
if nodeErr != nil { if nodeErr != nil {
_, staticNodeErr := GetExtClient(t.Value, netID.String()) _, staticNodeErr := GetExtClient(t.Value, a.NetworkID.String())
if staticNodeErr != nil { if staticNodeErr != nil {
return false return errors.New("invalid node " + t.Value)
} }
} }
case models.EgressRange: case models.EgressID, models.EgressRange:
if isSrc { e := schema.Egress{
return false ID: t.Value,
} }
// _, rangesMap, err := GetEgressRanges(netID) err := e.Get(db.WithContext(context.TODO()))
// if err != nil { if err != nil {
// return false return errors.New("invalid egress")
// } }
// if _, ok := rangesMap[t.Value]; !ok { if e.IsInetGw {
// return false req := models.InetNodeReq{}
// } for _, srcI := range a.Src {
if srcI.ID == models.NodeTagID {
nodesMap := GetNodesWithTag(models.TagID(srcI.Value))
for _, node := range nodesMap {
req.InetNodeClientIDs = append(req.InetNodeClientIDs, node.ID.String())
}
} else if srcI.ID == models.NodeID {
req.InetNodeClientIDs = append(req.InetNodeClientIDs, srcI.Value)
}
}
if len(e.Nodes) > 0 {
for k := range e.Nodes {
inetNode, err := GetNodeByID(k)
if err != nil {
return errors.New("invalid node " + t.Value)
}
if err = ValidateInetGwReq(inetNode, req, false); err != nil {
return err
}
}
}
}
case models.UserAclID: case models.UserAclID:
if policyType == models.DevicePolicy { if a.RuleType == models.DevicePolicy {
return false return errors.New("device policy source mismatch")
} }
if !isSrc { if !isSrc {
return false return errors.New("user cannot be added to destination")
} }
_, err := GetUser(t.Value) _, err := GetUser(t.Value)
if err != nil { if err != nil {
return false return errors.New("invalid user " + t.Value)
} }
case models.UserGroupAclID: case models.UserGroupAclID:
if policyType == models.DevicePolicy { if a.RuleType == models.DevicePolicy {
return false return errors.New("device policy source mismatch")
} }
if !isSrc { if !isSrc {
return false return errors.New("user cannot be added to destination")
} }
err := IsGroupValid(models.UserGroupID(t.Value)) err := IsGroupValid(models.UserGroupID(t.Value))
if err != nil { if err != nil {
return false return errors.New("invalid user group " + t.Value)
} }
// check if group belongs to this network // check if group belongs to this network
netGrps := GetUserGroupsInNetwork(netID) netGrps := GetUserGroupsInNetwork(a.NetworkID)
if _, ok := netGrps[models.UserGroupID(t.Value)]; !ok { if _, ok := netGrps[models.UserGroupID(t.Value)]; !ok {
return false return errors.New("invalid user group " + t.Value)
} }
default: default:
return false return errors.New("invalid policy")
} }
return true return nil
} }
// IsAclPolicyValid - validates if acl policy is valid // IsAclPolicyValid - validates if acl policy is valid
func IsAclPolicyValid(acl models.Acl) bool { func IsAclPolicyValid(acl models.Acl) (err error) {
//check if src and dst are valid //check if src and dst are valid
if acl.AllowedDirection != models.TrafficDirectionBi && if acl.AllowedDirection != models.TrafficDirectionBi &&
acl.AllowedDirection != models.TrafficDirectionUni { acl.AllowedDirection != models.TrafficDirectionUni {
return false return errors.New("invalid traffic direction")
} }
switch acl.RuleType { switch acl.RuleType {
case models.UserPolicy: case models.UserPolicy:
@@ -339,8 +377,8 @@ func IsAclPolicyValid(acl models.Acl) bool {
continue continue
} }
// check if user group is valid // check if user group is valid
if !checkIfAclTagisValid(srcI, acl.NetworkID, acl.RuleType, true) { if err = checkIfAclTagisValid(acl, srcI, true); err != nil {
return false return
} }
} }
for _, dstI := range acl.Dst { for _, dstI := range acl.Dst {
@@ -350,8 +388,8 @@ func IsAclPolicyValid(acl models.Acl) bool {
} }
// check if user group is valid // check if user group is valid
if !checkIfAclTagisValid(dstI, acl.NetworkID, acl.RuleType, false) { if err = checkIfAclTagisValid(acl, dstI, false); err != nil {
return false return
} }
} }
case models.DevicePolicy: case models.DevicePolicy:
@@ -360,8 +398,8 @@ func IsAclPolicyValid(acl models.Acl) bool {
continue continue
} }
// check if user group is valid // check if user group is valid
if !checkIfAclTagisValid(srcI, acl.NetworkID, acl.RuleType, true) { if err = checkIfAclTagisValid(acl, srcI, true); err != nil {
return false return err
} }
} }
for _, dstI := range acl.Dst { for _, dstI := range acl.Dst {
@@ -370,12 +408,26 @@ func IsAclPolicyValid(acl models.Acl) bool {
continue continue
} }
// check if user group is valid // check if user group is valid
if !checkIfAclTagisValid(dstI, acl.NetworkID, acl.RuleType, false) { if err = checkIfAclTagisValid(acl, dstI, false); err != nil {
return false return
} }
} }
} }
return true return nil
}
func UniqueAclPolicyTags(tags []models.AclPolicyTag) []models.AclPolicyTag {
seen := make(map[string]bool)
var result []models.AclPolicyTag
for _, tag := range tags {
key := fmt.Sprintf("%v-%s", tag.ID, tag.Value)
if !seen[key] {
seen[key] = true
result = append(result, tag)
}
}
return result
} }
// UpdateAcl - updates allowed fields on acls and commits to DB // UpdateAcl - updates allowed fields on acls and commits to DB
@@ -623,6 +675,17 @@ func IsUserAllowedToCommunicate(userName string, peer models.Node) (bool, []mode
continue continue
} }
dstMap := convAclTagToValueMap(policy.Dst) dstMap := convAclTagToValueMap(policy.Dst)
for _, dst := range policy.Dst {
if dst.ID == models.EgressID {
e := schema.Egress{ID: dst.Value}
err := e.Get(db.WithContext(context.TODO()))
if err == nil && e.Status {
for nodeID := range e.Nodes {
dstMap[nodeID] = struct{}{}
}
}
}
}
if _, ok := dstMap["*"]; ok { if _, ok := dstMap["*"]; ok {
allowedPolicies = append(allowedPolicies, policy) allowedPolicies = append(allowedPolicies, policy)
continue continue
@@ -712,8 +775,20 @@ func IsPeerAllowed(node, peer models.Node, checkDefaultPolicy bool) bool {
if !policy.Enabled { if !policy.Enabled {
continue continue
} }
srcMap = convAclTagToValueMap(policy.Src) srcMap = convAclTagToValueMap(policy.Src)
dstMap = convAclTagToValueMap(policy.Dst) dstMap = convAclTagToValueMap(policy.Dst)
for _, dst := range policy.Dst {
if dst.ID == models.EgressID {
e := schema.Egress{ID: dst.Value}
err := e.Get(db.WithContext(context.TODO()))
if err == nil && e.Status {
for nodeID := range e.Nodes {
dstMap[nodeID] = struct{}{}
}
}
}
}
if checkTagGroupPolicy(srcMap, dstMap, node, peer, nodeTags, peerTags) { if checkTagGroupPolicy(srcMap, dstMap, node, peer, nodeTags, peerTags) {
return true return true
} }
@@ -975,6 +1050,17 @@ func IsNodeAllowedToCommunicateV1(node, peer models.Node, checkDefaultPolicy boo
allowed := false allowed := false
srcMap = convAclTagToValueMap(policy.Src) srcMap = convAclTagToValueMap(policy.Src)
dstMap = convAclTagToValueMap(policy.Dst) dstMap = convAclTagToValueMap(policy.Dst)
for _, dst := range policy.Dst {
if dst.ID == models.EgressID {
e := schema.Egress{ID: dst.Value}
err := e.Get(db.WithContext(context.TODO()))
if err == nil && e.Status {
for nodeID := range e.Nodes {
dstMap[nodeID] = struct{}{}
}
}
}
}
_, srcAll := srcMap["*"] _, srcAll := srcMap["*"]
_, dstAll := dstMap["*"] _, dstAll := dstMap["*"]
if policy.AllowedDirection == models.TrafficDirectionBi { if policy.AllowedDirection == models.TrafficDirectionBi {
@@ -1158,7 +1244,7 @@ func getEgressUserRulesForNode(targetnode *models.Node,
acls := listUserPolicies(models.NetworkID(targetnode.Network)) acls := listUserPolicies(models.NetworkID(targetnode.Network))
var targetNodeTags = make(map[models.TagID]struct{}) var targetNodeTags = make(map[models.TagID]struct{})
targetNodeTags["*"] = struct{}{} targetNodeTags["*"] = struct{}{}
for _, rangeI := range targetnode.EgressGatewayRanges { for _, rangeI := range targetnode.EgressDetails.EgressGatewayRanges {
targetNodeTags[models.TagID(rangeI)] = struct{}{} targetNodeTags[models.TagID(rangeI)] = struct{}{}
} }
for _, acl := range acls { for _, acl := range acls {
@@ -1166,6 +1252,18 @@ func getEgressUserRulesForNode(targetnode *models.Node,
continue continue
} }
dstTags := convAclTagToValueMap(acl.Dst) dstTags := convAclTagToValueMap(acl.Dst)
for _, dst := range acl.Dst {
if dst.ID == models.EgressID {
e := schema.Egress{ID: dst.Value}
err := e.Get(db.WithContext(context.TODO()))
if err == nil && e.Status {
for nodeID := range e.Nodes {
dstTags[nodeID] = struct{}{}
}
dstTags[e.Range] = struct{}{}
}
}
}
_, all := dstTags["*"] _, all := dstTags["*"]
addUsers := false addUsers := false
if !all { if !all {
@@ -1225,8 +1323,24 @@ func getEgressUserRulesForNode(targetnode *models.Node,
r.IP6List = append(r.IP6List, userNode.StaticNode.AddressIPNet6()) r.IP6List = append(r.IP6List, userNode.StaticNode.AddressIPNet6())
} }
for _, dstI := range acl.Dst { for _, dstI := range acl.Dst {
if dstI.ID == models.EgressRange { if dstI.ID == models.EgressID {
ip, cidr, err := net.ParseCIDR(dstI.Value) e := schema.Egress{ID: dstI.Value}
err := e.Get(db.WithContext(context.TODO()))
if err != nil {
continue
}
if e.IsInetGw {
r.Dst = append(r.Dst, net.IPNet{
IP: net.IPv4zero,
Mask: net.CIDRMask(0, 32),
})
r.Dst6 = append(r.Dst6, net.IPNet{
IP: net.IPv6zero,
Mask: net.CIDRMask(0, 128),
})
} else {
ip, cidr, err := net.ParseCIDR(e.Range)
if err == nil { if err == nil {
if ip.To4() != nil { if ip.To4() != nil {
r.Dst = append(r.Dst, *cidr) r.Dst = append(r.Dst, *cidr)
@@ -1238,6 +1352,8 @@ func getEgressUserRulesForNode(targetnode *models.Node,
} }
} }
}
if aclRule, ok := rules[acl.ID]; ok { if aclRule, ok := rules[acl.ID]; ok {
aclRule.IPList = append(aclRule.IPList, r.IPList...) aclRule.IPList = append(aclRule.IPList, r.IPList...)
aclRule.IP6List = append(aclRule.IP6List, r.IP6List...) aclRule.IP6List = append(aclRule.IP6List, r.IP6List...)
@@ -1348,7 +1464,7 @@ func getUserAclRulesForNode(targetnode *models.Node,
} }
func checkIfAnyActiveEgressPolicy(targetNode models.Node) bool { func checkIfAnyActiveEgressPolicy(targetNode models.Node) bool {
if !targetNode.IsEgressGateway { if !targetNode.EgressDetails.IsEgressGateway {
return false return false
} }
var targetNodeTags = make(map[models.TagID]struct{}) var targetNodeTags = make(map[models.TagID]struct{})
@@ -1371,8 +1487,20 @@ func checkIfAnyActiveEgressPolicy(targetNode models.Node) bool {
} }
srcTags := convAclTagToValueMap(acl.Src) srcTags := convAclTagToValueMap(acl.Src)
dstTags := convAclTagToValueMap(acl.Dst) dstTags := convAclTagToValueMap(acl.Dst)
for _, dst := range acl.Dst {
if dst.ID == models.EgressID {
e := schema.Egress{ID: dst.Value}
err := e.Get(db.WithContext(context.TODO()))
if err == nil && e.Status {
for nodeID := range e.Nodes {
dstTags[nodeID] = struct{}{}
}
dstTags[e.Range] = struct{}{}
}
}
}
for nodeTag := range targetNodeTags { for nodeTag := range targetNodeTags {
if acl.RuleType == models.DevicePolicy { if acl.RuleType == models.DevicePolicy && acl.AllowedDirection == models.TrafficDirectionBi {
if _, ok := srcTags[nodeTag.String()]; ok { if _, ok := srcTags[nodeTag.String()]; ok {
return true return true
} }
@@ -1440,21 +1568,7 @@ func checkIfAnyPolicyisUniDirectional(targetNode models.Node) bool {
return false return false
} }
func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRule) { func checkIfNodeHasAccessToAllResources(targetnode *models.Node) bool {
targetnode := *targetnodeI
defer func() {
if !targetnode.IsIngressGateway {
rules = getUserAclRulesForNode(&targetnode, rules)
}
}()
rules = make(map[string]models.AclRule)
var taggedNodes map[models.TagID][]models.Node
if targetnode.IsIngressGateway {
taggedNodes = GetTagMapWithNodesByNetwork(models.NetworkID(targetnode.Network), false)
} else {
taggedNodes = GetTagMapWithNodesByNetwork(models.NetworkID(targetnode.Network), true)
}
acls := listDevicePolicies(models.NetworkID(targetnode.Network)) acls := listDevicePolicies(models.NetworkID(targetnode.Network))
var targetNodeTags = make(map[models.TagID]struct{}) var targetNodeTags = make(map[models.TagID]struct{})
if targetnode.Mutex != nil { if targetnode.Mutex != nil {
@@ -1477,6 +1591,85 @@ func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRu
dstTags := convAclTagToValueMap(acl.Dst) dstTags := convAclTagToValueMap(acl.Dst)
_, srcAll := srcTags["*"] _, srcAll := srcTags["*"]
_, dstAll := dstTags["*"] _, dstAll := dstTags["*"]
for nodeTag := range targetNodeTags {
var existsInSrcTag bool
var existsInDstTag bool
if _, ok := srcTags[nodeTag.String()]; ok {
existsInSrcTag = true
}
if _, ok := srcTags[targetnode.ID.String()]; ok {
existsInSrcTag = true
}
if _, ok := dstTags[nodeTag.String()]; ok {
existsInDstTag = true
}
if _, ok := dstTags[targetnode.ID.String()]; ok {
existsInDstTag = true
}
if acl.AllowedDirection == models.TrafficDirectionBi {
if existsInSrcTag && dstAll || existsInDstTag && srcAll {
return true
}
} else {
if existsInDstTag && srcAll {
return true
}
}
}
}
return false
}
func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRule) {
targetnode := *targetnodeI
defer func() {
if !targetnode.IsIngressGateway {
rules = getUserAclRulesForNode(&targetnode, rules)
}
}()
rules = make(map[string]models.AclRule)
var taggedNodes map[models.TagID][]models.Node
if targetnode.IsIngressGateway {
taggedNodes = GetTagMapWithNodesByNetwork(models.NetworkID(targetnode.Network), false)
} else {
taggedNodes = GetTagMapWithNodesByNetwork(models.NetworkID(targetnode.Network), true)
}
fmt.Printf("TAGGED NODES: %+v\n", taggedNodes)
acls := listDevicePolicies(models.NetworkID(targetnode.Network))
var targetNodeTags = make(map[models.TagID]struct{})
if targetnode.Mutex != nil {
targetnode.Mutex.Lock()
targetNodeTags = maps.Clone(targetnode.Tags)
targetnode.Mutex.Unlock()
} else {
targetNodeTags = maps.Clone(targetnode.Tags)
}
if targetNodeTags == nil {
targetNodeTags = make(map[models.TagID]struct{})
}
targetNodeTags[models.TagID(targetnode.ID.String())] = struct{}{}
targetNodeTags["*"] = struct{}{}
for _, acl := range acls {
if !acl.Enabled {
continue
}
srcTags := convAclTagToValueMap(acl.Src)
dstTags := convAclTagToValueMap(acl.Dst)
for _, dst := range acl.Dst {
if dst.ID == models.EgressID {
e := schema.Egress{ID: dst.Value}
err := e.Get(db.WithContext(context.TODO()))
if err == nil && e.Status {
for nodeID := range e.Nodes {
dstTags[nodeID] = struct{}{}
}
}
}
}
_, srcAll := srcTags["*"]
_, dstAll := dstTags["*"]
aclRule := models.AclRule{ aclRule := models.AclRule{
ID: acl.ID, ID: acl.ID,
AllowedProtocol: acl.Proto, AllowedProtocol: acl.Proto,
@@ -1502,7 +1695,7 @@ func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRu
existsInDstTag = true existsInDstTag = true
} }
if existsInSrcTag && !existsInDstTag { if existsInSrcTag /* && !existsInDstTag*/ {
// get all dst tags // get all dst tags
for dst := range dstTags { for dst := range dstTags {
if dst == nodeTag.String() { if dst == nodeTag.String() {
@@ -1539,7 +1732,7 @@ func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRu
} }
} }
} }
if existsInDstTag && !existsInSrcTag { if existsInDstTag /*&& !existsInSrcTag*/ {
// get all src tags // get all src tags
for src := range srcTags { for src := range srcTags {
if src == nodeTag.String() { if src == nodeTag.String() {
@@ -1575,47 +1768,47 @@ func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRu
} }
} }
} }
if existsInDstTag && existsInSrcTag { // if existsInDstTag && existsInSrcTag {
nodes := taggedNodes[nodeTag] // nodes := taggedNodes[nodeTag]
for srcID := range srcTags { // for srcID := range srcTags {
if srcID == targetnode.ID.String() { // if srcID == targetnode.ID.String() {
continue // continue
} // }
node, err := GetNodeByID(srcID) // node, err := GetNodeByID(srcID)
if err == nil { // if err == nil {
nodes = append(nodes, node) // nodes = append(nodes, node)
} // }
} // }
for dstID := range dstTags { // for dstID := range dstTags {
if dstID == targetnode.ID.String() { // if dstID == targetnode.ID.String() {
continue // continue
} // }
node, err := GetNodeByID(dstID) // node, err := GetNodeByID(dstID)
if err == nil { // if err == nil {
nodes = append(nodes, node) // nodes = append(nodes, node)
} // }
} // }
for _, node := range nodes { // for _, node := range nodes {
if node.ID == targetnode.ID { // if node.ID == targetnode.ID {
continue // continue
} // }
if node.IsStatic && node.StaticNode.IngressGatewayID == targetnode.ID.String() { // if node.IsStatic && node.StaticNode.IngressGatewayID == targetnode.ID.String() {
continue // continue
} // }
if node.Address.IP != nil { // if node.Address.IP != nil {
aclRule.IPList = append(aclRule.IPList, node.AddressIPNet4()) // aclRule.IPList = append(aclRule.IPList, node.AddressIPNet4())
} // }
if node.Address6.IP != nil { // if node.Address6.IP != nil {
aclRule.IP6List = append(aclRule.IP6List, node.AddressIPNet6()) // aclRule.IP6List = append(aclRule.IP6List, node.AddressIPNet6())
} // }
if node.IsStatic && node.StaticNode.Address != "" { // if node.IsStatic && node.StaticNode.Address != "" {
aclRule.IPList = append(aclRule.IPList, node.StaticNode.AddressIPNet4()) // aclRule.IPList = append(aclRule.IPList, node.StaticNode.AddressIPNet4())
} // }
if node.IsStatic && node.StaticNode.Address6 != "" { // if node.IsStatic && node.StaticNode.Address6 != "" {
aclRule.IP6List = append(aclRule.IP6List, node.StaticNode.AddressIPNet6()) // aclRule.IP6List = append(aclRule.IP6List, node.StaticNode.AddressIPNet6())
} // }
} // }
} // }
} else { } else {
_, all := dstTags["*"] _, all := dstTags["*"]
if _, ok := dstTags[nodeTag.String()]; ok || all { if _, ok := dstTags[nodeTag.String()]; ok || all {
@@ -1677,9 +1870,23 @@ func GetEgressRulesForNode(targetnode models.Node) (rules map[string]models.AclR
if acl policy has egress route and it is present in target node egress ranges if acl policy has egress route and it is present in target node egress ranges
fetch all the nodes in that policy and add rules fetch all the nodes in that policy and add rules
*/ */
egs, _ := (&schema.Egress{Network: targetnode.Network}).ListByNetwork(db.WithContext(context.TODO()))
for _, rangeI := range targetnode.EgressGatewayRanges { if len(egs) == 0 {
targetNodeTags[models.TagID(rangeI)] = struct{}{} return
}
for _, egI := range egs {
if !egI.Status {
continue
}
if _, ok := egI.Nodes[targetnode.ID.String()]; ok {
if egI.Range == "*" {
targetNodeTags[models.TagID("0.0.0.0/0")] = struct{}{}
targetNodeTags[models.TagID("::/0")] = struct{}{}
} else {
targetNodeTags[models.TagID(egI.Range)] = struct{}{}
}
targetNodeTags[models.TagID(egI.ID)] = struct{}{}
}
} }
for _, acl := range acls { for _, acl := range acls {
if !acl.Enabled { if !acl.Enabled {
@@ -1689,7 +1896,6 @@ func GetEgressRulesForNode(targetnode models.Node) (rules map[string]models.AclR
dstTags := convAclTagToValueMap(acl.Dst) dstTags := convAclTagToValueMap(acl.Dst)
_, srcAll := srcTags["*"] _, srcAll := srcTags["*"]
_, dstAll := dstTags["*"] _, dstAll := dstTags["*"]
for nodeTag := range targetNodeTags {
aclRule := models.AclRule{ aclRule := models.AclRule{
ID: acl.ID, ID: acl.ID,
AllowedProtocol: acl.Proto, AllowedProtocol: acl.Proto,
@@ -1697,38 +1903,36 @@ func GetEgressRulesForNode(targetnode models.Node) (rules map[string]models.AclR
Direction: acl.AllowedDirection, Direction: acl.AllowedDirection,
Allowed: true, Allowed: true,
} }
for nodeTag := range targetNodeTags {
if nodeTag != "*" { if nodeTag != "*" {
ip, cidr, err := net.ParseCIDR(nodeTag.String()) ip, cidr, err := net.ParseCIDR(nodeTag.String())
if err != nil { if err == nil {
continue
}
if ip.To4() != nil { if ip.To4() != nil {
aclRule.Dst = append(aclRule.Dst, *cidr) aclRule.Dst = append(aclRule.Dst, *cidr)
} else { } else {
aclRule.Dst6 = append(aclRule.Dst6, *cidr) aclRule.Dst6 = append(aclRule.Dst6, *cidr)
} }
}
} else {
aclRule.Dst = append(aclRule.Dst, net.IPNet{
IP: net.IPv4zero, // 0.0.0.0
Mask: net.CIDRMask(0, 32), // /0 means match all IPv4
})
aclRule.Dst6 = append(aclRule.Dst6, net.IPNet{
IP: net.IPv6zero, // ::
Mask: net.CIDRMask(0, 128), // /0 means match all IPv6
})
} }
if acl.AllowedDirection == models.TrafficDirectionBi { if acl.AllowedDirection == models.TrafficDirectionBi {
var existsInSrcTag bool var existsInSrcTag bool
var existsInDstTag bool var existsInDstTag bool
if _, ok := srcTags[nodeTag.String()]; ok || srcAll { if _, ok := srcTags[nodeTag.String()]; ok || srcAll {
existsInSrcTag = true existsInSrcTag = true
} }
if _, ok := dstTags[nodeTag.String()]; ok || dstAll { if _, ok := dstTags[nodeTag.String()]; ok || dstAll {
existsInDstTag = true existsInDstTag = true
} }
if srcAll || dstAll {
if targetnode.NetworkRange.IP != nil {
aclRule.IPList = append(aclRule.IPList, targetnode.NetworkRange)
}
if targetnode.NetworkRange6.IP != nil {
aclRule.IP6List = append(aclRule.IP6List, targetnode.NetworkRange6)
}
break
}
if existsInSrcTag && !existsInDstTag { if existsInSrcTag && !existsInDstTag {
// get all dst tags // get all dst tags
for dst := range dstTags { for dst := range dstTags {
@@ -1835,8 +2039,16 @@ func GetEgressRulesForNode(targetnode models.Node) (rules map[string]models.AclR
} }
} }
} else { } else {
_, all := dstTags["*"] if dstAll {
if _, ok := dstTags[nodeTag.String()]; ok || all { if targetnode.NetworkRange.IP != nil {
aclRule.IPList = append(aclRule.IPList, targetnode.NetworkRange)
}
if targetnode.NetworkRange6.IP != nil {
aclRule.IP6List = append(aclRule.IP6List, targetnode.NetworkRange6)
}
break
}
if _, ok := dstTags[nodeTag.String()]; ok || dstAll {
// get all src tags // get all src tags
for src := range srcTags { for src := range srcTags {
if src == nodeTag.String() { if src == nodeTag.String() {
@@ -1864,6 +2076,8 @@ func GetEgressRulesForNode(targetnode models.Node) (rules map[string]models.AclR
} }
} }
} }
}
if len(aclRule.IPList) > 0 || len(aclRule.IP6List) > 0 { if len(aclRule.IPList) > 0 || len(aclRule.IP6List) > 0 {
aclRule.IPList = UniqueIPNetList(aclRule.IPList) aclRule.IPList = UniqueIPNetList(aclRule.IPList)
aclRule.IP6List = UniqueIPNetList(aclRule.IP6List) aclRule.IP6List = UniqueIPNetList(aclRule.IP6List)
@@ -1871,8 +2085,6 @@ func GetEgressRulesForNode(targetnode models.Node) (rules map[string]models.AclR
} }
} }
}
return return
} }

View File

@@ -240,7 +240,7 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) {
} }
// update last login time // update last login time
result.LastLoginTime = time.Now() result.LastLoginTime = time.Now().UTC()
err = UpsertUser(result) err = UpsertUser(result)
if err != nil { if err != nil {
slog.Error("error upserting user", "error", err) slog.Error("error upserting user", "error", err)

366
logic/egress.go Normal file
View File

@@ -0,0 +1,366 @@
package logic
import (
"context"
"encoding/json"
"errors"
"maps"
"net"
"github.com/gravitl/netmaker/db"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/schema"
)
func ValidateEgressReq(e *schema.Egress) error {
if e.Network == "" {
return errors.New("network id is empty")
}
_, err := GetNetwork(e.Network)
if err != nil {
return errors.New("failed to get network " + err.Error())
}
if !e.IsInetGw {
if e.Range == "" {
return errors.New("egress range is empty")
}
_, _, err = net.ParseCIDR(e.Range)
if err != nil {
return errors.New("invalid egress range " + err.Error())
}
err = ValidateEgressRange(e.Network, []string{e.Range})
if err != nil {
return errors.New("invalid egress range " + err.Error())
}
} else {
if len(e.Nodes) > 1 {
return errors.New("can only set one internet routing node")
}
req := models.InetNodeReq{}
for k := range e.Nodes {
inetNode, err := GetNodeByID(k)
if err != nil {
return errors.New("invalid routing node " + err.Error())
}
// check if node is acting as egress gw already
GetNodeEgressInfo(&inetNode)
if err := ValidateInetGwReq(inetNode, req, false); err != nil {
return err
}
}
}
if len(e.Nodes) != 0 {
for k := range e.Nodes {
_, err := GetNodeByID(k)
if err != nil {
return errors.New("invalid routing node " + err.Error())
}
}
}
return nil
}
func GetInetClientsFromAclPolicies(eID string) (inetClientIDs []string) {
e := schema.Egress{ID: eID}
err := e.Get(db.WithContext(context.TODO()))
if err != nil || !e.Status {
return
}
acls, _ := ListAclsByNetwork(models.NetworkID(e.Network))
for _, acl := range acls {
for _, dstI := range acl.Dst {
if dstI.ID == models.EgressID {
if dstI.Value != eID {
continue
}
for _, srcI := range acl.Src {
if srcI.Value == "*" {
continue
}
if srcI.ID == models.NodeID {
inetClientIDs = append(inetClientIDs, srcI.Value)
}
if srcI.ID == models.NodeTagID {
inetClientIDs = append(inetClientIDs, GetNodeIDsWithTag(models.TagID(srcI.Value))...)
}
}
}
}
}
return
}
func isNodeUsingInternetGw(node *models.Node) {
host, err := GetHost(node.HostID.String())
if err != nil {
return
}
if host.IsDefault || node.IsFailOver {
return
}
nodeTags := maps.Clone(node.Tags)
nodeTags[models.TagID(node.ID.String())] = struct{}{}
acls, _ := ListAclsByNetwork(models.NetworkID(node.Network))
var isUsing bool
for _, acl := range acls {
if !acl.Enabled {
continue
}
srcVal := convAclTagToValueMap(acl.Src)
for _, dstI := range acl.Dst {
if dstI.ID == models.EgressID {
e := schema.Egress{ID: dstI.Value}
err := e.Get(db.WithContext(context.TODO()))
if err != nil || !e.Status {
continue
}
if e.IsInetGw {
if _, ok := srcVal[node.ID.String()]; ok {
for nodeID := range e.Nodes {
if nodeID == node.ID.String() {
continue
}
node.EgressDetails.InternetGwID = nodeID
isUsing = true
return
}
}
for tagID := range nodeTags {
if _, ok := srcVal[tagID.String()]; ok {
for nodeID := range e.Nodes {
if nodeID == node.ID.String() {
continue
}
node.EgressDetails.InternetGwID = nodeID
isUsing = true
return
}
}
}
}
}
}
}
if !isUsing {
node.EgressDetails.InternetGwID = ""
}
}
func DoesNodeHaveAccessToEgress(node *models.Node, e *schema.Egress) bool {
nodeTags := maps.Clone(node.Tags)
nodeTags[models.TagID(node.ID.String())] = struct{}{}
if !e.IsInetGw {
nodeTags[models.TagID("*")] = struct{}{}
}
acls, _ := ListAclsByNetwork(models.NetworkID(node.Network))
if !e.IsInetGw {
defaultDevicePolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy)
if defaultDevicePolicy.Enabled {
return true
}
}
for _, acl := range acls {
if !acl.Enabled {
continue
}
srcVal := convAclTagToValueMap(acl.Src)
if !e.IsInetGw && acl.AllowedDirection == models.TrafficDirectionBi {
if _, ok := srcVal["*"]; ok {
return true
}
}
for _, dstI := range acl.Dst {
if !e.IsInetGw && dstI.ID == models.NodeTagID && dstI.Value == "*" {
return true
}
if dstI.ID == models.EgressID && dstI.Value == e.ID {
e := schema.Egress{ID: dstI.Value}
err := e.Get(db.WithContext(context.TODO()))
if err != nil {
continue
}
if node.IsStatic {
if _, ok := srcVal[node.StaticNode.ClientID]; ok {
return true
}
} else {
if _, ok := srcVal[node.ID.String()]; ok {
return true
}
}
for tagID := range nodeTags {
if _, ok := srcVal[tagID.String()]; ok {
return true
}
}
}
}
}
return false
}
func AddEgressInfoToPeerByAccess(node, targetNode *models.Node) {
eli, _ := (&schema.Egress{Network: targetNode.Network}).ListByNetwork(db.WithContext(context.TODO()))
req := models.EgressGatewayRequest{
NodeID: targetNode.ID.String(),
NetID: targetNode.Network,
}
defer func() {
if targetNode.Mutex != nil {
targetNode.Mutex.Lock()
}
isNodeUsingInternetGw(targetNode)
if targetNode.Mutex != nil {
targetNode.Mutex.Unlock()
}
}()
for _, e := range eli {
if !e.Status || e.Network != targetNode.Network {
continue
}
if !DoesNodeHaveAccessToEgress(node, &e) {
if node.IsRelayed && node.RelayedBy == targetNode.ID.String() {
if !DoesNodeHaveAccessToEgress(targetNode, &e) {
continue
}
} else {
continue
}
}
if metric, ok := e.Nodes[targetNode.ID.String()]; ok {
if e.IsInetGw {
targetNode.EgressDetails.IsInternetGateway = true
targetNode.EgressDetails.InetNodeReq = models.InetNodeReq{
InetNodeClientIDs: GetInetClientsFromAclPolicies(e.ID),
}
req.Ranges = append(req.Ranges, "0.0.0.0/0")
req.RangesWithMetric = append(req.RangesWithMetric, models.EgressRangeMetric{
Network: "0.0.0.0/0",
Nat: true,
RouteMetric: 256,
})
req.Ranges = append(req.Ranges, "::/0")
req.RangesWithMetric = append(req.RangesWithMetric, models.EgressRangeMetric{
Network: "::/0",
Nat: true,
RouteMetric: 256,
})
} else {
m64, err := metric.(json.Number).Int64()
if err != nil {
m64 = 256
}
m := uint32(m64)
req.Ranges = append(req.Ranges, e.Range)
req.RangesWithMetric = append(req.RangesWithMetric, models.EgressRangeMetric{
Network: e.Range,
Nat: e.Nat,
RouteMetric: m,
})
}
}
}
if targetNode.Mutex != nil {
targetNode.Mutex.Lock()
}
if len(req.Ranges) > 0 {
targetNode.EgressDetails.IsEgressGateway = true
targetNode.EgressDetails.EgressGatewayRanges = req.Ranges
targetNode.EgressDetails.EgressGatewayRequest = req
} else {
targetNode.EgressDetails = models.EgressDetails{}
}
if targetNode.Mutex != nil {
targetNode.Mutex.Unlock()
}
}
func GetNodeEgressInfo(targetNode *models.Node) {
eli, _ := (&schema.Egress{Network: targetNode.Network}).ListByNetwork(db.WithContext(context.TODO()))
req := models.EgressGatewayRequest{
NodeID: targetNode.ID.String(),
NetID: targetNode.Network,
}
defer func() {
if targetNode.Mutex != nil {
targetNode.Mutex.Lock()
}
isNodeUsingInternetGw(targetNode)
if targetNode.Mutex != nil {
targetNode.Mutex.Unlock()
}
}()
for _, e := range eli {
if !e.Status || e.Network != targetNode.Network {
continue
}
if metric, ok := e.Nodes[targetNode.ID.String()]; ok {
if e.IsInetGw {
targetNode.EgressDetails.IsInternetGateway = true
targetNode.EgressDetails.InetNodeReq = models.InetNodeReq{
InetNodeClientIDs: GetInetClientsFromAclPolicies(e.ID),
}
req.Ranges = append(req.Ranges, "0.0.0.0/0")
req.RangesWithMetric = append(req.RangesWithMetric, models.EgressRangeMetric{
Network: "0.0.0.0/0",
Nat: true,
RouteMetric: 256,
})
req.Ranges = append(req.Ranges, "::/0")
req.RangesWithMetric = append(req.RangesWithMetric, models.EgressRangeMetric{
Network: "::/0",
Nat: true,
RouteMetric: 256,
})
} else {
m64, err := metric.(json.Number).Int64()
if err != nil {
m64 = 256
}
m := uint32(m64)
req.Ranges = append(req.Ranges, e.Range)
req.RangesWithMetric = append(req.RangesWithMetric, models.EgressRangeMetric{
Network: e.Range,
Nat: e.Nat,
RouteMetric: m,
})
}
}
}
if targetNode.Mutex != nil {
targetNode.Mutex.Lock()
}
if len(req.Ranges) > 0 {
targetNode.EgressDetails.IsEgressGateway = true
targetNode.EgressDetails.EgressGatewayRanges = req.Ranges
targetNode.EgressDetails.EgressGatewayRequest = req
} else {
targetNode.EgressDetails = models.EgressDetails{}
}
if targetNode.Mutex != nil {
targetNode.Mutex.Unlock()
}
}
func RemoveNodeFromEgress(node models.Node) {
egs, _ := (&schema.Egress{}).ListByNetwork(db.WithContext(context.TODO()))
for _, egI := range egs {
if _, ok := egI.Nodes[node.ID.String()]; ok {
delete(egI.Nodes, node.ID.String())
egI.Update(db.WithContext(context.TODO()))
}
}
}

View File

@@ -1,6 +1,7 @@
package logic package logic
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@@ -13,9 +14,11 @@ import (
"github.com/goombaio/namegenerator" "github.com/goombaio/namegenerator"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/db"
"github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic/acls" "github.com/gravitl/netmaker/logic/acls"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/schema"
"github.com/gravitl/netmaker/servercfg" "github.com/gravitl/netmaker/servercfg"
"golang.org/x/exp/slog" "golang.org/x/exp/slog"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
@@ -71,13 +74,19 @@ func GetEgressRangesOnNetwork(client *models.ExtClient) ([]string, error) {
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
// clientNode := client.ConvertToStaticNode()
for _, currentNode := range networkNodes { for _, currentNode := range networkNodes {
if currentNode.Network != client.Network { if currentNode.Network != client.Network {
continue continue
} }
if currentNode.IsEgressGateway { // add the egress gateway range(s) to the result GetNodeEgressInfo(&currentNode)
if len(currentNode.EgressGatewayRanges) > 0 { if currentNode.EgressDetails.IsInternetGateway && client.IngressGatewayID != currentNode.ID.String() {
result = append(result, currentNode.EgressGatewayRanges...) continue
}
if currentNode.EgressDetails.IsEgressGateway { // add the egress gateway range(s) to the result
fmt.Println("EGRESSS EXTCLEINT: ", currentNode.EgressDetails)
if len(currentNode.EgressDetails.EgressGatewayRanges) > 0 {
result = append(result, currentNode.EgressDetails.EgressGatewayRanges...)
} }
} }
} }
@@ -627,7 +636,15 @@ func getFwRulesForNodeAndPeerOnGw(node, peer models.Node, allowedPolicies []mode
// add egress range rules // add egress range rules
for _, dstI := range policy.Dst { for _, dstI := range policy.Dst {
if dstI.ID == models.EgressRange { if dstI.ID == models.EgressID {
e := schema.Egress{ID: dstI.Value}
err := e.Get(db.WithContext(context.TODO()))
if err != nil {
continue
}
dstI.Value = e.Range
ip, cidr, err := net.ParseCIDR(dstI.Value) ip, cidr, err := net.ParseCIDR(dstI.Value)
if err == nil { if err == nil {
if ip.To4() != nil { if ip.To4() != nil {
@@ -708,7 +725,15 @@ func getFwRulesForUserNodesOnGw(node models.Node, nodes []models.Node) (rules []
// add egress ranges // add egress ranges
for _, dstI := range policy.Dst { for _, dstI := range policy.Dst {
if dstI.ID == models.EgressRange { if dstI.ID == models.EgressID {
e := schema.Egress{ID: dstI.Value}
err := e.Get(db.WithContext(context.TODO()))
if err != nil {
continue
}
dstI.Value = e.Range
ip, cidr, err := net.ParseCIDR(dstI.Value) ip, cidr, err := net.ParseCIDR(dstI.Value)
if err == nil { if err == nil {
if ip.To4() != nil && userNodeI.StaticNode.Address != "" { if ip.To4() != nil && userNodeI.StaticNode.Address != "" {

View File

@@ -1,6 +1,7 @@
package logic package logic
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"slices" "slices"
@@ -8,14 +9,27 @@ import (
"time" "time"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/db"
"github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/schema"
"github.com/gravitl/netmaker/servercfg" "github.com/gravitl/netmaker/servercfg"
) )
// IsInternetGw - checks if node is acting as internet gw // IsInternetGw - checks if node is acting as internet gw
func IsInternetGw(node models.Node) bool { func IsInternetGw(node models.Node) bool {
return node.IsInternetGateway e := schema.Egress{
Network: node.Network,
}
egList, _ := e.ListByNetwork(db.WithContext(context.TODO()))
for _, egI := range egList {
if egI.IsInetGw {
if _, ok := egI.Nodes[node.ID.String()]; ok {
return true
}
}
}
return false
} }
// GetInternetGateways - gets all the nodes that are internet gateways // GetInternetGateways - gets all the nodes that are internet gateways
@@ -26,7 +40,7 @@ func GetInternetGateways() ([]models.Node, error) {
} }
igs := make([]models.Node, 0) igs := make([]models.Node, 0)
for _, node := range nodes { for _, node := range nodes {
if node.IsInternetGateway { if node.EgressDetails.IsInternetGateway {
igs = append(igs, node) igs = append(igs, node)
} }
} }
@@ -56,7 +70,7 @@ func GetAllEgresses() ([]models.Node, error) {
} }
egresses := make([]models.Node, 0) egresses := make([]models.Node, 0)
for _, node := range nodes { for _, node := range nodes {
if node.IsEgressGateway { if node.EgressDetails.IsEgressGateway {
egresses = append(egresses, node) egresses = append(egresses, node)
} }
} }
@@ -133,11 +147,11 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
if gateway.Ranges == nil { if gateway.Ranges == nil {
gateway.Ranges = make([]string, 0) gateway.Ranges = make([]string, 0)
} }
node.IsEgressGateway = true node.EgressDetails.IsEgressGateway = true
node.EgressGatewayRanges = gateway.Ranges node.EgressDetails.EgressGatewayRanges = gateway.Ranges
node.EgressGatewayNatEnabled = models.ParseBool(gateway.NatEnabled) node.EgressDetails.EgressGatewayNatEnabled = models.ParseBool(gateway.NatEnabled)
node.EgressGatewayRequest = gateway // store entire request for use when preserving the egress gateway node.EgressDetails.EgressGatewayRequest = gateway // store entire request for use when preserving the egress gateway
node.SetLastModified() node.SetLastModified()
if err = UpsertNode(&node); err != nil { if err = UpsertNode(&node); err != nil {
return models.Node{}, err return models.Node{}, err
@@ -156,9 +170,9 @@ func DeleteEgressGateway(network, nodeid string) (models.Node, error) {
if err != nil { if err != nil {
return models.Node{}, err return models.Node{}, err
} }
node.IsEgressGateway = false node.EgressDetails.IsEgressGateway = false
node.EgressGatewayRanges = []string{} node.EgressDetails.EgressGatewayRanges = []string{}
node.EgressGatewayRequest = models.EgressGatewayRequest{} // remove preserved request as the egress gateway is gone node.EgressDetails.EgressGatewayRequest = models.EgressGatewayRequest{} // remove preserved request as the egress gateway is gone
node.SetLastModified() node.SetLastModified()
if err = UpsertNode(&node); err != nil { if err = UpsertNode(&node); err != nil {
return models.Node{}, err return models.Node{}, err
@@ -191,12 +205,12 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq
node.IsIngressGateway = true node.IsIngressGateway = true
node.IsGw = true node.IsGw = true
if !servercfg.IsPro { if !servercfg.IsPro {
node.IsInternetGateway = ingress.IsInternetGateway node.EgressDetails.IsInternetGateway = ingress.IsInternetGateway
} }
node.IngressGatewayRange = network.AddressRange node.IngressGatewayRange = network.AddressRange
node.IngressGatewayRange6 = network.AddressRange6 node.IngressGatewayRange6 = network.AddressRange6
node.IngressDNS = ingress.ExtclientDNS node.IngressDNS = ingress.ExtclientDNS
if node.IsInternetGateway && node.IngressDNS == "" { if node.EgressDetails.IsInternetGateway && node.IngressDNS == "" {
node.IngressDNS = "1.1.1.1" node.IngressDNS = "1.1.1.1"
} }
node.IngressPersistentKeepalive = 20 node.IngressPersistentKeepalive = 20
@@ -267,10 +281,10 @@ func DeleteIngressGateway(nodeid string) (models.Node, []models.ExtClient, error
return models.Node{}, removedClients, err return models.Node{}, removedClients, err
} }
logger.Log(3, "deleting ingress gateway") logger.Log(3, "deleting ingress gateway")
node.LastModified = time.Now() node.LastModified = time.Now().UTC()
node.IsIngressGateway = false node.IsIngressGateway = false
if !servercfg.IsPro { if !servercfg.IsPro {
node.IsInternetGateway = false node.EgressDetails.IsInternetGateway = false
} }
delete(node.Tags, models.TagID(fmt.Sprintf("%s.%s", node.Network, models.GwTagName))) delete(node.Tags, models.TagID(fmt.Sprintf("%s.%s", node.Network, models.GwTagName)))
node.IngressGatewayRange = "" node.IngressGatewayRange = ""

View File

@@ -548,17 +548,29 @@ func GetRelatedHosts(hostID string) []models.Host {
// CheckHostPort checks host endpoints to ensures that hosts on the same server // CheckHostPort checks host endpoints to ensures that hosts on the same server
// with the same endpoint have different listen ports // with the same endpoint have different listen ports
// in the case of 64535 hosts or more with same endpoint, ports will not be changed // in the case of 64535 hosts or more with same endpoint, ports will not be changed
func CheckHostPorts(h *models.Host) { func CheckHostPorts(h *models.Host) (changed bool) {
portsInUse := make(map[int]bool, 0) portsInUse := make(map[int]bool, 0)
hosts, err := GetAllHosts() hosts, err := GetAllHosts()
if err != nil { if err != nil {
return return
} }
originalPort := h.ListenPort
defer func() {
if originalPort != h.ListenPort {
changed = true
}
}()
if h.EndpointIP == nil {
return
}
for _, host := range hosts { for _, host := range hosts {
if host.ID.String() == h.ID.String() { if host.ID.String() == h.ID.String() {
// skip self // skip self
continue continue
} }
if host.EndpointIP == nil {
continue
}
if !host.EndpointIP.Equal(h.EndpointIP) { if !host.EndpointIP.Equal(h.EndpointIP) {
continue continue
} }
@@ -566,11 +578,16 @@ func CheckHostPorts(h *models.Host) {
} }
// iterate until port is not found or max iteration is reached // iterate until port is not found or max iteration is reached
for i := 0; portsInUse[h.ListenPort] && i < maxPort-minPort+1; i++ { for i := 0; portsInUse[h.ListenPort] && i < maxPort-minPort+1; i++ {
if h.ListenPort == 443 {
h.ListenPort = 51821
} else {
h.ListenPort++ h.ListenPort++
}
if h.ListenPort > maxPort { if h.ListenPort > maxPort {
h.ListenPort = minPort h.ListenPort = minPort
} }
} }
return
} }
// HostExists - checks if given host already exists // HostExists - checks if given host already exists

View File

@@ -135,7 +135,7 @@ func GetUserNameFromToken(authtoken string) (username string, err error) {
err = errors.New("token revoked") err = errors.New("token revoked")
return "", err return "", err
} }
a.LastUsed = time.Now() a.LastUsed = time.Now().UTC()
a.Update(db.WithContext(context.TODO())) a.Update(db.WithContext(context.TODO()))
} }
} }
@@ -179,7 +179,7 @@ func VerifyUserToken(tokenString string) (username string, issuperadmin, isadmin
err = errors.New("token revoked") err = errors.New("token revoked")
return "", false, false, err return "", false, false, err
} }
a.LastUsed = time.Now() a.LastUsed = time.Now().UTC()
a.Update(db.WithContext(context.TODO())) a.Update(db.WithContext(context.TODO()))
} }
} }

View File

@@ -522,7 +522,6 @@ func UniqueAddress6DB(networkName string, reverse bool) (net.IP, error) {
var network models.Network var network models.Network
network, err := GetParentNetwork(networkName) network, err := GetParentNetwork(networkName)
if err != nil { if err != nil {
fmt.Println("Network Not Found")
return add, err return add, err
} }
if network.IsIPv6 == "no" { if network.IsIPv6 == "no" {
@@ -567,7 +566,6 @@ func UniqueAddress6Cache(networkName string, reverse bool) (net.IP, error) {
var network models.Network var network models.Network
network, err := GetParentNetwork(networkName) network, err := GetParentNetwork(networkName)
if err != nil { if err != nil {
fmt.Println("Network Not Found")
return add, err return add, err
} }
if network.IsIPv6 == "no" { if network.IsIPv6 == "no" {

View File

@@ -164,7 +164,7 @@ func UpdateNodeCheckin(node *models.Node) error {
if err != nil { if err != nil {
return err return err
} }
node.EgressDetails = models.EgressDetails{}
err = database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME) err = database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
if err != nil { if err != nil {
return err return err
@@ -183,6 +183,7 @@ func UpsertNode(newNode *models.Node) error {
if err != nil { if err != nil {
return err return err
} }
newNode.EgressDetails = models.EgressDetails{}
err = database.Insert(newNode.ID.String(), string(data), database.NODES_TABLE_NAME) err = database.Insert(newNode.ID.String(), string(data), database.NODES_TABLE_NAME)
if err != nil { if err != nil {
return err return err
@@ -218,7 +219,7 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
return err return err
} }
} }
newNode.EgressDetails = models.EgressDetails{}
newNode.SetLastModified() newNode.SetLastModified()
if data, err := json.Marshal(newNode); err != nil { if data, err := json.Marshal(newNode); err != nil {
return err return err
@@ -280,21 +281,21 @@ func DeleteNode(node *models.Node, purge bool) error {
// unset all the relayed nodes // unset all the relayed nodes
SetRelayedNodes(false, node.ID.String(), node.RelayedNodes) SetRelayedNodes(false, node.ID.String(), node.RelayedNodes)
} }
if node.InternetGwID != "" { if node.EgressDetails.InternetGwID != "" {
inetNode, err := GetNodeByID(node.InternetGwID) inetNode, err := GetNodeByID(node.EgressDetails.InternetGwID)
if err == nil { if err == nil {
clientNodeIDs := []string{} clientNodeIDs := []string{}
for _, inetNodeClientID := range inetNode.InetNodeReq.InetNodeClientIDs { for _, inetNodeClientID := range inetNode.EgressDetails.InetNodeReq.InetNodeClientIDs {
if inetNodeClientID == node.ID.String() { if inetNodeClientID == node.ID.String() {
continue continue
} }
clientNodeIDs = append(clientNodeIDs, inetNodeClientID) clientNodeIDs = append(clientNodeIDs, inetNodeClientID)
} }
inetNode.InetNodeReq.InetNodeClientIDs = clientNodeIDs inetNode.EgressDetails.InetNodeReq.InetNodeClientIDs = clientNodeIDs
UpsertNode(&inetNode) UpsertNode(&inetNode)
} }
} }
if node.IsInternetGateway { if node.EgressDetails.IsInternetGateway {
UnsetInternetGw(node) UnsetInternetGw(node)
} }
if !purge && !alreadyDeleted { if !purge && !alreadyDeleted {
@@ -320,8 +321,9 @@ func DeleteNode(node *models.Node, purge bool) error {
if err := DissasociateNodeFromHost(node, host); err != nil { if err := DissasociateNodeFromHost(node, host); err != nil {
return err return err
} }
go RemoveNodeFromAclPolicy(*node)
go RemoveNodeFromAclPolicy(*node)
go RemoveNodeFromEgress(*node)
return nil return nil
} }
@@ -783,16 +785,16 @@ func ValidateNodeIp(currentNode *models.Node, newNode *models.ApiNode) error {
return nil return nil
} }
func ValidateEgressRange(gateway models.EgressGatewayRequest) error { func ValidateEgressRange(netID string, ranges []string) error {
network, err := GetNetworkSettings(gateway.NetID) network, err := GetNetworkSettings(netID)
if err != nil { if err != nil {
slog.Error("error getting network with netid", "error", gateway.NetID, err.Error) slog.Error("error getting network with netid", "error", netID, err.Error)
return errors.New("error getting network with netid: " + gateway.NetID + " " + err.Error()) return errors.New("error getting network with netid: " + netID + " " + err.Error())
} }
ipv4Net := network.AddressRange ipv4Net := network.AddressRange
ipv6Net := network.AddressRange6 ipv6Net := network.AddressRange6
for _, v := range gateway.Ranges { for _, v := range ranges {
if ipv4Net != "" { if ipv4Net != "" {
if ContainsCIDR(ipv4Net, v) { if ContainsCIDR(ipv4Net, v) {
slog.Error("egress range should not be the same as or contained in the netmaker network address", "error", v, ipv4Net) slog.Error("egress range should not be the same as or contained in the netmaker network address", "error", v, ipv4Net)
@@ -949,6 +951,30 @@ func AddTagMapWithStaticNodesWithUsers(netID models.NetworkID,
return tagNodesMap return tagNodesMap
} }
func GetNodeIDsWithTag(tagID models.TagID) (ids []string) {
tag, err := GetTag(tagID)
if err != nil {
return
}
nodes, _ := GetNetworkNodes(tag.Network.String())
for _, nodeI := range nodes {
if nodeI.Tags == nil {
continue
}
if nodeI.Mutex != nil {
nodeI.Mutex.Lock()
}
if _, ok := nodeI.Tags[tagID]; ok {
ids = append(ids, nodeI.ID.String())
}
if nodeI.Mutex != nil {
nodeI.Mutex.Unlock()
}
}
return
}
func GetNodesWithTag(tagID models.TagID) map[string]models.Node { func GetNodesWithTag(tagID models.TagID) map[string]models.Node {
nMap := make(map[string]models.Node) nMap := make(map[string]models.Node)
tag, err := GetTag(tagID) tag, err := GetTag(tagID)

View File

@@ -6,6 +6,7 @@ import (
"net" "net"
"net/netip" "net/netip"
"github.com/google/uuid"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic/acls/nodeacls" "github.com/gravitl/netmaker/logic/acls/nodeacls"
@@ -47,16 +48,19 @@ var (
} }
// UnsetInternetGw // UnsetInternetGw
UnsetInternetGw = func(node *models.Node) { UnsetInternetGw = func(node *models.Node) {
node.IsInternetGateway = false node.EgressDetails.IsInternetGateway = false
} }
// SetInternetGw // SetInternetGw
SetInternetGw = func(node *models.Node, req models.InetNodeReq) { SetInternetGw = func(node *models.Node, req models.InetNodeReq) {
node.IsInternetGateway = true node.EgressDetails.IsInternetGateway = true
} }
// GetAllowedIpForInetNodeClient // GetAllowedIpForInetNodeClient
GetAllowedIpForInetNodeClient = func(node, peer *models.Node) []net.IPNet { GetAllowedIpForInetNodeClient = func(node, peer *models.Node) []net.IPNet {
return []net.IPNet{} return []net.IPNet{}
} }
ValidateInetGwReq = func(inetNode models.Node, req models.InetNodeReq, update bool) error {
return nil
}
) )
// GetHostPeerInfo - fetches required peer info per network // GetHostPeerInfo - fetches required peer info per network
@@ -161,26 +165,16 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
} }
defer func() { defer func() {
if !hostPeerUpdate.FwUpdate.AllowAll { if !hostPeerUpdate.FwUpdate.AllowAll {
aclRule := models.AclRule{
ID: "allowed-network-rules",
AllowedProtocol: models.ALL,
Direction: models.TrafficDirectionBi,
Allowed: true,
}
for _, allowedNet := range hostPeerUpdate.FwUpdate.AllowedNetworks {
if allowedNet.IP.To4() != nil {
aclRule.IPList = append(aclRule.IPList, allowedNet)
} else {
aclRule.IP6List = append(aclRule.IP6List, allowedNet)
}
}
hostPeerUpdate.FwUpdate.AclRules["allowed-network-rules"] = aclRule
hostPeerUpdate.FwUpdate.EgressInfo["allowed-network-rules"] = models.EgressInfo{ hostPeerUpdate.FwUpdate.EgressInfo["allowed-network-rules"] = models.EgressInfo{
EgressID: "allowed-network-rules", EgressID: "allowed-network-rules",
EgressFwRules: map[string]models.AclRule{ EgressFwRules: make(map[string]models.AclRule),
"allowed-network-rules": aclRule,
},
} }
for _, aclRule := range hostPeerUpdate.FwUpdate.AllowedNetworks {
hostPeerUpdate.FwUpdate.AclRules[aclRule.ID] = aclRule
hostPeerUpdate.FwUpdate.EgressInfo["allowed-network-rules"].EgressFwRules[aclRule.ID] = aclRule
}
} }
}() }()
@@ -189,14 +183,17 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
for _, nodeID := range host.Nodes { for _, nodeID := range host.Nodes {
networkAllowAll := true networkAllowAll := true
nodeID := nodeID nodeID := nodeID
if nodeID == uuid.Nil.String() {
continue
}
node, err := GetNodeByID(nodeID) node, err := GetNodeByID(nodeID)
if err != nil { if err != nil {
continue continue
} }
if !node.Connected || node.PendingDelete || node.Action == models.NODE_DELETE { if !node.Connected || node.PendingDelete || node.Action == models.NODE_DELETE {
continue continue
} }
GetNodeEgressInfo(&node)
hostPeerUpdate = SetDefaultGw(node, hostPeerUpdate) hostPeerUpdate = SetDefaultGw(node, hostPeerUpdate)
if !hostPeerUpdate.IsInternetGw { if !hostPeerUpdate.IsInternetGw {
hostPeerUpdate.IsInternetGw = IsInternetGw(node) hostPeerUpdate.IsInternetGw = IsInternetGw(node)
@@ -204,13 +201,22 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
defaultUserPolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.UserPolicy) defaultUserPolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.UserPolicy)
defaultDevicePolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy) defaultDevicePolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy)
if (defaultDevicePolicy.Enabled && defaultUserPolicy.Enabled) || (!checkIfAnyPolicyisUniDirectional(node) && !checkIfAnyActiveEgressPolicy(node)) { if (defaultDevicePolicy.Enabled && defaultUserPolicy.Enabled) ||
if node.NetworkRange.IP != nil { (!checkIfAnyPolicyisUniDirectional(node) && !checkIfAnyActiveEgressPolicy(node)) ||
hostPeerUpdate.FwUpdate.AllowedNetworks = append(hostPeerUpdate.FwUpdate.AllowedNetworks, node.NetworkRange) checkIfNodeHasAccessToAllResources(&node) {
aclRule := models.AclRule{
ID: fmt.Sprintf("%s-allowed-network-rules", node.ID.String()),
AllowedProtocol: models.ALL,
Direction: models.TrafficDirectionBi,
Allowed: true,
IPList: []net.IPNet{node.NetworkRange},
IP6List: []net.IPNet{node.NetworkRange6},
} }
if node.NetworkRange6.IP != nil { if !(defaultDevicePolicy.Enabled && defaultUserPolicy.Enabled) {
hostPeerUpdate.FwUpdate.AllowedNetworks = append(hostPeerUpdate.FwUpdate.AllowedNetworks, node.NetworkRange6) aclRule.Dst = []net.IPNet{node.NetworkRange}
aclRule.Dst6 = []net.IPNet{node.NetworkRange6}
} }
hostPeerUpdate.FwUpdate.AllowedNetworks = append(hostPeerUpdate.FwUpdate.AllowedNetworks, aclRule)
} else { } else {
networkAllowAll = false networkAllowAll = false
hostPeerUpdate.FwUpdate.AllowAll = false hostPeerUpdate.FwUpdate.AllowAll = false
@@ -247,8 +253,9 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
PersistentKeepaliveInterval: &peerHost.PersistentKeepalive, PersistentKeepaliveInterval: &peerHost.PersistentKeepalive,
ReplaceAllowedIPs: true, ReplaceAllowedIPs: true,
} }
AddEgressInfoToPeerByAccess(&node, &peer)
_, isFailOverPeer := node.FailOverPeers[peer.ID.String()] _, isFailOverPeer := node.FailOverPeers[peer.ID.String()]
if peer.IsEgressGateway { if peer.EgressDetails.IsEgressGateway {
peerKey := peerHost.PublicKey.String() peerKey := peerHost.PublicKey.String()
if isFailOverPeer && peer.FailedOverBy.String() != node.ID.String() { if isFailOverPeer && peer.FailedOverBy.String() != node.ID.String() {
// get relay host // get relay host
@@ -435,7 +442,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
logger.Log(1, "error retrieving external clients:", err.Error()) logger.Log(1, "error retrieving external clients:", err.Error())
} }
} }
if node.IsEgressGateway && node.EgressGatewayRequest.NatEnabled == "yes" && len(node.EgressGatewayRequest.Ranges) > 0 { if node.EgressDetails.IsEgressGateway && len(node.EgressDetails.EgressGatewayRequest.Ranges) > 0 {
hostPeerUpdate.FwUpdate.IsEgressGw = true hostPeerUpdate.FwUpdate.IsEgressGw = true
hostPeerUpdate.FwUpdate.EgressInfo[node.ID.String()] = models.EgressInfo{ hostPeerUpdate.FwUpdate.EgressInfo[node.ID.String()] = models.EgressInfo{
EgressID: node.ID.String(), EgressID: node.ID.String(),
@@ -449,12 +456,12 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
IP: node.Address6.IP, IP: node.Address6.IP,
Mask: getCIDRMaskFromAddr(node.Address6.IP.String()), Mask: getCIDRMaskFromAddr(node.Address6.IP.String()),
}, },
EgressGWCfg: node.EgressGatewayRequest, EgressGWCfg: node.EgressDetails.EgressGatewayRequest,
EgressFwRules: make(map[string]models.AclRule), EgressFwRules: make(map[string]models.AclRule),
} }
} }
if node.IsEgressGateway { if node.EgressDetails.IsEgressGateway {
if !networkAllowAll { if !networkAllowAll {
egressInfo := hostPeerUpdate.FwUpdate.EgressInfo[node.ID.String()] egressInfo := hostPeerUpdate.FwUpdate.EgressInfo[node.ID.String()]
if egressInfo.EgressFwRules == nil { if egressInfo.EgressFwRules == nil {
@@ -492,7 +499,6 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
}, },
} }
} }
} }
// == post peer calculations == // == post peer calculations ==
// indicate removal if no allowed IPs were calculated // indicate removal if no allowed IPs were calculated
@@ -549,11 +555,11 @@ func GetPeerListenPort(host *models.Host) int {
} }
func filterConflictingEgressRoutes(node, peer models.Node) []string { func filterConflictingEgressRoutes(node, peer models.Node) []string {
egressIPs := slices.Clone(peer.EgressGatewayRanges) egressIPs := slices.Clone(peer.EgressDetails.EgressGatewayRanges)
if node.IsEgressGateway { if node.EgressDetails.IsEgressGateway {
// filter conflicting addrs // filter conflicting addrs
nodeEgressMap := make(map[string]struct{}) nodeEgressMap := make(map[string]struct{})
for _, rangeI := range node.EgressGatewayRanges { for _, rangeI := range node.EgressDetails.EgressGatewayRanges {
nodeEgressMap[rangeI] = struct{}{} nodeEgressMap[rangeI] = struct{}{}
} }
for i := len(egressIPs) - 1; i >= 0; i-- { for i := len(egressIPs) - 1; i >= 0; i-- {
@@ -567,11 +573,11 @@ func filterConflictingEgressRoutes(node, peer models.Node) []string {
} }
func filterConflictingEgressRoutesWithMetric(node, peer models.Node) []models.EgressRangeMetric { func filterConflictingEgressRoutesWithMetric(node, peer models.Node) []models.EgressRangeMetric {
egressIPs := slices.Clone(peer.EgressGatewayRequest.RangesWithMetric) egressIPs := slices.Clone(peer.EgressDetails.EgressGatewayRequest.RangesWithMetric)
if node.IsEgressGateway { if node.EgressDetails.IsEgressGateway {
// filter conflicting addrs // filter conflicting addrs
nodeEgressMap := make(map[string]struct{}) nodeEgressMap := make(map[string]struct{})
for _, rangeI := range node.EgressGatewayRanges { for _, rangeI := range node.EgressDetails.EgressGatewayRanges {
nodeEgressMap[rangeI] = struct{}{} nodeEgressMap[rangeI] = struct{}{}
} }
for i := len(egressIPs) - 1; i >= 0; i-- { for i := len(egressIPs) - 1; i >= 0; i-- {
@@ -588,13 +594,13 @@ func filterConflictingEgressRoutesWithMetric(node, peer models.Node) []models.Eg
func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet { func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet {
var allowedips []net.IPNet var allowedips []net.IPNet
allowedips = getNodeAllowedIPs(peer, node) allowedips = getNodeAllowedIPs(peer, node)
if peer.IsInternetGateway && node.InternetGwID == peer.ID.String() { if peer.EgressDetails.IsInternetGateway && node.EgressDetails.InternetGwID == peer.ID.String() {
allowedips = append(allowedips, GetAllowedIpForInetNodeClient(node, peer)...) allowedips = append(allowedips, GetAllowedIpForInetNodeClient(node, peer)...)
return allowedips return allowedips
} }
if node.IsRelayed && node.RelayedBy == peer.ID.String() { if node.IsRelayed && node.RelayedBy == peer.ID.String() {
allowedips = append(allowedips, GetAllowedIpsForRelayed(node, peer)...) allowedips = append(allowedips, GetAllowedIpsForRelayed(node, peer)...)
if peer.InternetGwID != "" { if peer.EgressDetails.InternetGwID != "" {
return allowedips return allowedips
} }
} }
@@ -623,11 +629,11 @@ func GetEgressIPs(peer *models.Node) []net.IPNet {
// check for internet gateway // check for internet gateway
internetGateway := false internetGateway := false
if slices.Contains(peer.EgressGatewayRanges, "0.0.0.0/0") || slices.Contains(peer.EgressGatewayRanges, "::/0") { if slices.Contains(peer.EgressDetails.EgressGatewayRanges, "0.0.0.0/0") || slices.Contains(peer.EgressDetails.EgressGatewayRanges, "::/0") {
internetGateway = true internetGateway = true
} }
allowedips := []net.IPNet{} allowedips := []net.IPNet{}
for _, iprange := range peer.EgressGatewayRanges { // go through each cidr for egress gateway for _, iprange := range peer.EgressDetails.EgressGatewayRanges { // go through each cidr for egress gateway
_, ipnet, err := net.ParseCIDR(iprange) // confirming it's valid cidr _, ipnet, err := net.ParseCIDR(iprange) // confirming it's valid cidr
if err != nil { if err != nil {
logger.Log(1, "could not parse gateway IP range. Not adding ", iprange) logger.Log(1, "could not parse gateway IP range. Not adding ", iprange)
@@ -669,13 +675,13 @@ func getNodeAllowedIPs(peer, node *models.Node) []net.IPNet {
allowedips = append(allowedips, allowed) allowedips = append(allowedips, allowed)
} }
// handle egress gateway peers // handle egress gateway peers
if peer.IsEgressGateway { if peer.EgressDetails.IsEgressGateway {
// hasGateway = true // hasGateway = true
egressIPs := GetEgressIPs(peer) egressIPs := GetEgressIPs(peer)
if node.IsEgressGateway { if node.EgressDetails.IsEgressGateway {
// filter conflicting addrs // filter conflicting addrs
nodeEgressMap := make(map[string]struct{}) nodeEgressMap := make(map[string]struct{})
for _, rangeI := range node.EgressGatewayRanges { for _, rangeI := range node.EgressDetails.EgressGatewayRanges {
nodeEgressMap[rangeI] = struct{}{} nodeEgressMap[rangeI] = struct{}{}
} }
for i := len(egressIPs) - 1; i >= 0; i-- { for i := len(egressIPs) - 1; i >= 0; i-- {

View File

@@ -114,13 +114,14 @@ func ValidateRelay(relay models.RelayRequest, update bool) error {
if err != nil { if err != nil {
return err return err
} }
GetNodeEgressInfo(&relayedNode)
if relayedNode.IsIngressGateway { if relayedNode.IsIngressGateway {
return errors.New("cannot relay an ingress gateway (" + relayedNodeID + ")") return errors.New("cannot relay an ingress gateway (" + relayedNodeID + ")")
} }
if relayedNode.IsInternetGateway { if relayedNode.EgressDetails.IsInternetGateway {
return errors.New("cannot relay an internet gateway (" + relayedNodeID + ")") return errors.New("cannot relay an internet gateway (" + relayedNodeID + ")")
} }
if relayedNode.InternetGwID != "" && relayedNode.InternetGwID != relay.NodeID { if relayedNode.EgressDetails.InternetGwID != "" && relayedNode.EgressDetails.InternetGwID != relay.NodeID {
return errors.New("cannot relay an internet client (" + relayedNodeID + ")") return errors.New("cannot relay an internet client (" + relayedNodeID + ")")
} }
if relayedNode.IsFailOver { if relayedNode.IsFailOver {
@@ -193,8 +194,9 @@ func RelayedAllowedIPs(peer, node *models.Node) []net.IPNet {
if err != nil { if err != nil {
continue continue
} }
GetNodeEgressInfo(&relayedNode)
allowed := getRelayedAddresses(relayedNodeID) allowed := getRelayedAddresses(relayedNodeID)
if relayedNode.IsEgressGateway { if relayedNode.EgressDetails.IsEgressGateway {
allowed = append(allowed, GetEgressIPs(&relayedNode)...) allowed = append(allowed, GetEgressIPs(&relayedNode)...)
} }
allowedIPs = append(allowedIPs, allowed...) allowedIPs = append(allowedIPs, allowed...)
@@ -208,7 +210,7 @@ func GetAllowedIpsForRelayed(relayed, relay *models.Node) (allowedIPs []net.IPNe
logger.Log(0, "RelayedByRelay called with invalid parameters") logger.Log(0, "RelayedByRelay called with invalid parameters")
return return
} }
if relay.InternetGwID != "" { if relay.EgressDetails.InternetGwID != "" {
return GetAllowedIpForInetNodeClient(relayed, relay) return GetAllowedIpForInetNodeClient(relayed, relay)
} }
peers, err := GetNetworkNodes(relay.Network) peers, err := GetNetworkNodes(relay.Network)

View File

@@ -290,7 +290,7 @@ func CreateDefaultTags(netID models.NetworkID) {
TagName: models.GwTagName, TagName: models.GwTagName,
Network: netID, Network: netID,
CreatedBy: "auto", CreatedBy: "auto",
CreatedAt: time.Now(), CreatedAt: time.Now().UTC(),
} }
_, err := GetTag(tag.ID) _, err := GetTag(tag.ID)
if err == nil { if err == nil {

View File

@@ -9,24 +9,10 @@ func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool {
// single comparison statements // single comparison statements
if newNode.Address.String() != currentNode.Address.String() || if newNode.Address.String() != currentNode.Address.String() ||
newNode.Address6.String() != currentNode.Address6.String() || newNode.Address6.String() != currentNode.Address6.String() ||
newNode.IsEgressGateway != currentNode.IsEgressGateway ||
newNode.IsIngressGateway != currentNode.IsIngressGateway ||
newNode.IsRelay != currentNode.IsRelay || newNode.IsRelay != currentNode.IsRelay ||
newNode.DNSOn != currentNode.DNSOn ||
newNode.Connected != currentNode.Connected { newNode.Connected != currentNode.Connected {
return true return true
} }
// multi-comparison statements
if newNode.IsEgressGateway {
if len(currentNode.EgressGatewayRanges) != len(newNode.EgressGatewayRanges) {
return true
}
for _, address := range newNode.EgressGatewayRanges {
if !StringSliceContains(currentNode.EgressGatewayRanges, address) {
return true
}
}
}
if newNode.IsRelay { if newNode.IsRelay {
if len(currentNode.RelayedNodes) != len(newNode.RelayedNodes) { if len(currentNode.RelayedNodes) != len(newNode.RelayedNodes) {
return true return true

View File

@@ -1,20 +1,24 @@
package migrate package migrate
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log" "log"
"time" "time"
"golang.org/x/exp/slog" "golang.org/x/exp/slog"
"gorm.io/datatypes"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/db"
"github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/logic/acls" "github.com/gravitl/netmaker/logic/acls"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq" "github.com/gravitl/netmaker/mq"
"github.com/gravitl/netmaker/schema"
"github.com/gravitl/netmaker/servercfg" "github.com/gravitl/netmaker/servercfg"
) )
@@ -30,6 +34,7 @@ func Run() {
updateNodes() updateNodes()
updateAcls() updateAcls()
migrateToGws() migrateToGws()
migrateToEgressV1()
} }
func assignSuperAdmin() { func assignSuperAdmin() {
@@ -500,6 +505,218 @@ func migrateToGws() {
} }
} }
func migrateToEgressV1() {
nodes, _ := logic.GetAllNodes()
user, err := logic.GetSuperAdmin()
if err != nil {
return
}
for _, node := range nodes {
if node.IsEgressGateway {
egressHost, err := logic.GetHost(node.HostID.String())
if err != nil {
continue
}
for _, rangeI := range node.EgressGatewayRequest.Ranges {
e := schema.Egress{
ID: uuid.New().String(),
Name: fmt.Sprintf("%s egress", egressHost.Name),
Description: "",
Network: node.Network,
Nodes: datatypes.JSONMap{
node.ID.String(): 256,
},
Tags: make(datatypes.JSONMap),
Range: rangeI,
Nat: node.EgressGatewayRequest.NatEnabled == "yes",
Status: true,
CreatedBy: user.UserName,
CreatedAt: time.Now().UTC(),
}
err = e.Create(db.WithContext(context.TODO()))
if err == nil {
node.IsEgressGateway = false
node.EgressGatewayRequest = models.EgressGatewayRequest{}
node.EgressGatewayNatEnabled = false
node.EgressGatewayRanges = []string{}
logic.UpsertNode(&node)
acl := models.Acl{
ID: uuid.New().String(),
Name: "egress node policy",
MetaData: "",
Default: false,
ServiceType: models.Any,
NetworkID: models.NetworkID(node.Network),
Proto: models.ALL,
RuleType: models.DevicePolicy,
Src: []models.AclPolicyTag{
{
ID: models.NodeTagID,
Value: "*",
},
},
Dst: []models.AclPolicyTag{
{
ID: models.EgressID,
Value: e.ID,
},
},
AllowedDirection: models.TrafficDirectionUni,
Enabled: true,
CreatedBy: "auto",
CreatedAt: time.Now().UTC(),
}
logic.InsertAcl(acl)
acl = models.Acl{
ID: uuid.New().String(),
Name: "egress node policy",
MetaData: "",
Default: false,
ServiceType: models.Any,
NetworkID: models.NetworkID(node.Network),
Proto: models.ALL,
RuleType: models.UserPolicy,
Src: []models.AclPolicyTag{
{
ID: models.UserGroupAclID,
Value: "*",
},
},
Dst: []models.AclPolicyTag{
{
ID: models.EgressID,
Value: e.ID,
},
},
AllowedDirection: models.TrafficDirectionUni,
Enabled: true,
CreatedBy: "auto",
CreatedAt: time.Now().UTC(),
}
logic.InsertAcl(acl)
}
}
}
if node.IsInternetGateway {
inetHost, err := logic.GetHost(node.HostID.String())
if err != nil {
continue
}
e := schema.Egress{
ID: uuid.New().String(),
Name: fmt.Sprintf("%s inet gw", inetHost.Name),
Description: "add description",
Network: node.Network,
Nodes: datatypes.JSONMap{
node.ID.String(): 256,
},
Tags: make(datatypes.JSONMap),
Range: "",
IsInetGw: true,
Nat: node.EgressGatewayRequest.NatEnabled == "yes",
Status: true,
CreatedBy: user.UserName,
CreatedAt: time.Now().UTC(),
}
err = e.Create(db.WithContext(context.TODO()))
if err == nil {
node.IsEgressGateway = false
node.EgressGatewayRequest = models.EgressGatewayRequest{}
node.EgressGatewayNatEnabled = false
node.EgressGatewayRanges = []string{}
node.IsInternetGateway = false
src := []models.AclPolicyTag{}
for _, inetClientID := range node.InetNodeReq.InetNodeClientIDs {
_, err := logic.GetNodeByID(inetClientID)
if err == nil {
src = append(src, models.AclPolicyTag{
ID: models.NodeID,
Value: inetClientID,
})
}
}
acl := models.Acl{
ID: uuid.New().String(),
Name: "exit node policy",
MetaData: "all traffic on source nodes will pass through the destination node in the policy",
Default: false,
ServiceType: models.Any,
NetworkID: models.NetworkID(node.Network),
Proto: models.ALL,
RuleType: models.DevicePolicy,
Src: src,
Dst: []models.AclPolicyTag{
{
ID: models.EgressID,
Value: e.ID,
},
},
AllowedDirection: models.TrafficDirectionBi,
Enabled: true,
CreatedBy: "auto",
CreatedAt: time.Now().UTC(),
}
logic.InsertAcl(acl)
acl = models.Acl{
ID: uuid.New().String(),
Name: "exit node policy",
MetaData: "all traffic on source nodes will pass through the destination node in the policy",
Default: false,
ServiceType: models.Any,
NetworkID: models.NetworkID(node.Network),
Proto: models.ALL,
RuleType: models.UserPolicy,
Src: []models.AclPolicyTag{
{
ID: models.UserGroupAclID,
Value: fmt.Sprintf("%s-%s-grp", node.Network, models.NetworkAdmin),
},
{
ID: models.UserGroupAclID,
Value: fmt.Sprintf("global-%s-grp", models.NetworkAdmin),
},
{
ID: models.UserGroupAclID,
Value: fmt.Sprintf("%s-%s-grp", node.Network, models.NetworkUser),
},
{
ID: models.UserGroupAclID,
Value: fmt.Sprintf("global-%s-grp", models.NetworkUser),
},
},
Dst: []models.AclPolicyTag{
{
ID: models.EgressID,
Value: e.ID,
},
},
AllowedDirection: models.TrafficDirectionBi,
Enabled: true,
CreatedBy: "auto",
CreatedAt: time.Now().UTC(),
}
logic.InsertAcl(acl)
node.InetNodeReq = models.InetNodeReq{}
logic.UpsertNode(&node)
}
}
if node.InternetGwID != "" {
node.InternetGwID = ""
logic.UpsertNode(&node)
}
}
}
func settings() { func settings() {
_, err := database.FetchRecords(database.SERVER_SETTINGS) _, err := database.FetchRecords(database.SERVER_SETTINGS)
if database.IsEmptyRecord(err) { if database.IsEmptyRecord(err) {

60
models/accessToken.go Normal file
View File

@@ -0,0 +1,60 @@
package models
import (
"context"
"time"
"github.com/gravitl/netmaker/db"
)
// accessTokenTableName - access tokens table
const accessTokenTableName = "user_access_tokens"
// UserAccessToken - token used to access netmaker
type UserAccessToken struct {
ID string `gorm:"id,primary_key" json:"id"`
Name string `gorm:"name" json:"name"`
UserName string `gorm:"user_name" json:"user_name"`
ExpiresAt time.Time `gorm:"expires_at" json:"expires_at"`
LastUsed time.Time `gorm:"last_used" json:"last_used"`
CreatedBy string `gorm:"created_by" json:"created_by"`
CreatedAt time.Time `gorm:"created_at" json:"created_at"`
}
func (a *UserAccessToken) Table() string {
return accessTokenTableName
}
func (a *UserAccessToken) Get() error {
return db.FromContext(context.TODO()).Table(a.Table()).First(&a).Where("id = ?", a.ID).Error
}
func (a *UserAccessToken) Update() error {
return db.FromContext(context.TODO()).Table(a.Table()).Where("id = ?", a.ID).Updates(&a).Error
}
func (a *UserAccessToken) Create() error {
return db.FromContext(context.TODO()).Table(a.Table()).Create(&a).Error
}
func (a *UserAccessToken) List() (ats []UserAccessToken, err error) {
err = db.FromContext(context.TODO()).Table(a.Table()).Find(&ats).Error
return
}
func (a *UserAccessToken) ListByUser() (ats []UserAccessToken) {
db.FromContext(context.TODO()).Table(a.Table()).Where("user_name = ?", a.UserName).Find(&ats)
if ats == nil {
ats = []UserAccessToken{}
}
return
}
func (a *UserAccessToken) Delete() error {
return db.FromContext(context.TODO()).Table(a.Table()).Where("id = ?", a.ID).Delete(&a).Error
}
func (a *UserAccessToken) DeleteAllUserTokens() error {
return db.FromContext(context.TODO()).Table(a.Table()).Where("user_name = ? OR created_by = ?", a.UserName, a.UserName).Delete(&a).Error
}

View File

@@ -60,6 +60,7 @@ const (
NodeTagID AclGroupType = "tag" NodeTagID AclGroupType = "tag"
NodeID AclGroupType = "device" NodeID AclGroupType = "device"
EgressRange AclGroupType = "egress-range" EgressRange AclGroupType = "egress-range"
EgressID AclGroupType = "egress-id"
NetmakerIPAclID AclGroupType = "ip" NetmakerIPAclID AclGroupType = "ip"
NetmakerSubNetRangeAClID AclGroupType = "ipset" NetmakerSubNetRangeAClID AclGroupType = "ipset"
) )

View File

@@ -79,21 +79,16 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node {
convertedNode.PendingDelete = a.PendingDelete convertedNode.PendingDelete = a.PendingDelete
convertedNode.FailedOverBy = currentNode.FailedOverBy convertedNode.FailedOverBy = currentNode.FailedOverBy
convertedNode.FailOverPeers = currentNode.FailOverPeers convertedNode.FailOverPeers = currentNode.FailOverPeers
convertedNode.IsEgressGateway = a.IsEgressGateway
convertedNode.IsIngressGateway = a.IsIngressGateway convertedNode.IsIngressGateway = a.IsIngressGateway
// prevents user from changing ranges, must delete and recreate
convertedNode.EgressGatewayRanges = currentNode.EgressGatewayRanges
convertedNode.IngressGatewayRange = currentNode.IngressGatewayRange convertedNode.IngressGatewayRange = currentNode.IngressGatewayRange
convertedNode.IngressGatewayRange6 = currentNode.IngressGatewayRange6 convertedNode.IngressGatewayRange6 = currentNode.IngressGatewayRange6
convertedNode.DNSOn = a.DNSOn convertedNode.DNSOn = a.DNSOn
convertedNode.IngressDNS = a.IngressDns convertedNode.IngressDNS = a.IngressDns
convertedNode.IngressPersistentKeepalive = a.IngressPersistentKeepalive convertedNode.IngressPersistentKeepalive = a.IngressPersistentKeepalive
convertedNode.IngressMTU = a.IngressMTU convertedNode.IngressMTU = a.IngressMTU
convertedNode.IsInternetGateway = a.IsInternetGateway convertedNode.EgressDetails.IsInternetGateway = a.IsInternetGateway
convertedNode.EgressGatewayRequest = currentNode.EgressGatewayRequest convertedNode.EgressDetails.InternetGwID = currentNode.EgressDetails.InternetGwID
convertedNode.EgressGatewayNatEnabled = currentNode.EgressGatewayNatEnabled convertedNode.EgressDetails.InetNodeReq = currentNode.EgressDetails.InetNodeReq
convertedNode.InternetGwID = currentNode.InternetGwID
convertedNode.InetNodeReq = currentNode.InetNodeReq
convertedNode.RelayedNodes = a.RelayedNodes convertedNode.RelayedNodes = a.RelayedNodes
convertedNode.DefaultACL = a.DefaultACL convertedNode.DefaultACL = a.DefaultACL
convertedNode.OwnerID = currentNode.OwnerID convertedNode.OwnerID = currentNode.OwnerID
@@ -187,11 +182,7 @@ func (nm *Node) ConvertToAPINode() *ApiNode {
apiNode.IsRelay = nm.IsRelay apiNode.IsRelay = nm.IsRelay
apiNode.RelayedBy = nm.RelayedBy apiNode.RelayedBy = nm.RelayedBy
apiNode.RelayedNodes = nm.RelayedNodes apiNode.RelayedNodes = nm.RelayedNodes
apiNode.IsEgressGateway = nm.IsEgressGateway
apiNode.IsIngressGateway = nm.IsIngressGateway apiNode.IsIngressGateway = nm.IsIngressGateway
apiNode.EgressGatewayRanges = nm.EgressGatewayRanges
apiNode.EgressGatewayRangesWithMetric = nm.EgressGatewayRequest.RangesWithMetric
apiNode.EgressGatewayNatEnabled = nm.EgressGatewayNatEnabled
apiNode.DNSOn = nm.DNSOn apiNode.DNSOn = nm.DNSOn
apiNode.IngressDns = nm.IngressDNS apiNode.IngressDns = nm.IngressDNS
apiNode.IngressPersistentKeepalive = nm.IngressPersistentKeepalive apiNode.IngressPersistentKeepalive = nm.IngressPersistentKeepalive
@@ -200,9 +191,9 @@ func (nm *Node) ConvertToAPINode() *ApiNode {
apiNode.Connected = nm.Connected apiNode.Connected = nm.Connected
apiNode.PendingDelete = nm.PendingDelete apiNode.PendingDelete = nm.PendingDelete
apiNode.DefaultACL = nm.DefaultACL apiNode.DefaultACL = nm.DefaultACL
apiNode.IsInternetGateway = nm.IsInternetGateway apiNode.IsInternetGateway = nm.EgressDetails.IsInternetGateway
apiNode.InternetGwID = nm.InternetGwID apiNode.InternetGwID = nm.EgressDetails.InternetGwID
apiNode.InetNodeReq = nm.InetNodeReq apiNode.InetNodeReq = nm.EgressDetails.InetNodeReq
apiNode.IsFailOver = nm.IsFailOver apiNode.IsFailOver = nm.IsFailOver
apiNode.FailOverPeers = nm.FailOverPeers apiNode.FailOverPeers = nm.FailOverPeers
apiNode.FailedOverBy = nm.FailedOverBy apiNode.FailedOverBy = nm.FailedOverBy

14
models/egress.go Normal file
View File

@@ -0,0 +1,14 @@
package models
type EgressReq struct {
ID string `json:"id"`
Name string `json:"name"`
Network string `json:"network"`
Description string `json:"description"`
Nodes map[string]int `json:"nodes"`
Tags []string `json:"tags"`
Range string `json:"range"`
Nat bool `json:"nat"`
Status bool `json:"status"`
IsInetGw bool `json:"is_internet_gateway"`
}

View File

@@ -107,7 +107,7 @@ type KeyUpdate struct {
// FwUpdate - struct for firewall updates // FwUpdate - struct for firewall updates
type FwUpdate struct { type FwUpdate struct {
AllowAll bool `json:"allow_all"` AllowAll bool `json:"allow_all"`
AllowedNetworks []net.IPNet `json:"networks"` AllowedNetworks []AclRule `json:"networks"`
IsEgressGw bool `json:"is_egress_gw"` IsEgressGw bool `json:"is_egress_gw"`
IsIngressGw bool `json:"is_ingress_gw"` IsIngressGw bool `json:"is_ingress_gw"`
EgressInfo map[string]EgressInfo `json:"egress_info"` EgressInfo map[string]EgressInfo `json:"egress_info"`

View File

@@ -121,6 +121,16 @@ type Node struct {
StaticNode ExtClient `json:"static_node"` StaticNode ExtClient `json:"static_node"`
Status NodeStatus `json:"node_status"` Status NodeStatus `json:"node_status"`
Mutex *sync.Mutex `json:"-"` Mutex *sync.Mutex `json:"-"`
EgressDetails EgressDetails `json:"-"`
}
type EgressDetails struct {
EgressGatewayNatEnabled bool
EgressGatewayRequest EgressGatewayRequest
IsEgressGateway bool
EgressGatewayRanges []string
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 // LegacyNode - legacy struct for node model
@@ -377,17 +387,17 @@ func (node *LegacyNode) SetIsStaticDefault() {
// Node.SetLastModified - set last modified initial time // Node.SetLastModified - set last modified initial time
func (node *Node) SetLastModified() { func (node *Node) SetLastModified() {
node.LastModified = time.Now() node.LastModified = time.Now().UTC()
} }
// Node.SetLastCheckIn - set checkin time of node // Node.SetLastCheckIn - set checkin time of node
func (node *Node) SetLastCheckIn() { func (node *Node) SetLastCheckIn() {
node.LastCheckIn = time.Now() node.LastCheckIn = time.Now().UTC()
} }
// Node.SetLastPeerUpdate - sets last peer update time // Node.SetLastPeerUpdate - sets last peer update time
func (node *Node) SetLastPeerUpdate() { func (node *Node) SetLastPeerUpdate() {
node.LastPeerUpdate = time.Now() node.LastPeerUpdate = time.Now().UTC()
} }
// Node.SetExpirationDateTime - sets node expiry time // Node.SetExpirationDateTime - sets node expiry time
@@ -442,15 +452,9 @@ func (newNode *Node) Fill(
if newNode.Network == "" { if newNode.Network == "" {
newNode.Network = currentNode.Network newNode.Network = currentNode.Network
} }
if newNode.IsEgressGateway != currentNode.IsEgressGateway {
newNode.IsEgressGateway = currentNode.IsEgressGateway
}
if newNode.IsIngressGateway != currentNode.IsIngressGateway { if newNode.IsIngressGateway != currentNode.IsIngressGateway {
newNode.IsIngressGateway = currentNode.IsIngressGateway newNode.IsIngressGateway = currentNode.IsIngressGateway
} }
if newNode.EgressGatewayRanges == nil {
newNode.EgressGatewayRanges = currentNode.EgressGatewayRanges
}
if newNode.IngressGatewayRange == "" { if newNode.IngressGatewayRange == "" {
newNode.IngressGatewayRange = currentNode.IngressGatewayRange newNode.IngressGatewayRange = currentNode.IngressGatewayRange
} }
@@ -567,7 +571,6 @@ func (ln *LegacyNode) ConvertToNewNode() (*Host, *Node) {
} }
} }
node.Action = ln.Action node.Action = ln.Action
node.IsEgressGateway = parseBool(ln.IsEgressGateway)
node.IsIngressGateway = parseBool(ln.IsIngressGateway) node.IsIngressGateway = parseBool(ln.IsIngressGateway)
node.DNSOn = parseBool(ln.DNSOn) node.DNSOn = parseBool(ln.DNSOn)
@@ -601,7 +604,6 @@ func (n *Node) Legacy(h *Host, s *ServerConfig, net *Network) *LegacyNode {
//l.IsRelay = formatBool(n.IsRelay) //l.IsRelay = formatBool(n.IsRelay)
//l.IsDocker = formatBool(n.IsDocker) //l.IsDocker = formatBool(n.IsDocker)
//l.IsK8S = formatBool(n.IsK8S) //l.IsK8S = formatBool(n.IsK8S)
l.IsEgressGateway = formatBool(n.IsEgressGateway)
l.IsIngressGateway = formatBool(n.IsIngressGateway) l.IsIngressGateway = formatBool(n.IsIngressGateway)
//l.EgressGatewayRanges = n.EgressGatewayRanges //l.EgressGatewayRanges = n.EgressGatewayRanges
//l.EgressGatewayNatEnabled = n.EgressGatewayNatEnabled //l.EgressGatewayNatEnabled = n.EgressGatewayNatEnabled

View File

@@ -156,6 +156,7 @@ type ExtPeersResponse struct {
type EgressRangeMetric struct { type EgressRangeMetric struct {
Network string `json:"network"` Network string `json:"network"`
RouteMetric uint32 `json:"route_metric"` // preffered range 1-999 RouteMetric uint32 `json:"route_metric"` // preffered range 1-999
Nat bool `json:"nat"`
} }
// EgressGatewayRequest - egress gateway request // EgressGatewayRequest - egress gateway request

View File

@@ -205,6 +205,8 @@ func failOverME(w http.ResponseWriter, r *http.Request) {
) )
return return
} }
logic.GetNodeEgressInfo(&node)
logic.GetNodeEgressInfo(&peerNode)
if peerNode.IsFailOver { if peerNode.IsFailOver {
logic.ReturnErrorResponse( logic.ReturnErrorResponse(
w, w,
@@ -245,7 +247,7 @@ func failOverME(w http.ResponseWriter, r *http.Request) {
) )
return return
} }
if node.IsInternetGateway && peerNode.InternetGwID == node.ID.String() { if node.EgressDetails.IsInternetGateway && peerNode.EgressDetails.InternetGwID == node.ID.String() {
logic.ReturnErrorResponse( logic.ReturnErrorResponse(
w, w,
r, r,
@@ -256,7 +258,7 @@ func failOverME(w http.ResponseWriter, r *http.Request) {
) )
return return
} }
if node.InternetGwID != "" && node.InternetGwID == peerNode.ID.String() { if node.EgressDetails.InternetGwID != "" && node.EgressDetails.InternetGwID == peerNode.ID.String() {
logic.ReturnErrorResponse( logic.ReturnErrorResponse(
w, w,
r, r,
@@ -349,6 +351,8 @@ func checkfailOverCtx(w http.ResponseWriter, r *http.Request) {
) )
return return
} }
logic.GetNodeEgressInfo(&node)
logic.GetNodeEgressInfo(&peerNode)
if peerNode.IsFailOver { if peerNode.IsFailOver {
logic.ReturnErrorResponse( logic.ReturnErrorResponse(
w, w,
@@ -389,7 +393,18 @@ func checkfailOverCtx(w http.ResponseWriter, r *http.Request) {
) )
return return
} }
if node.IsInternetGateway && peerNode.InternetGwID == node.ID.String() { if node.EgressDetails.InternetGwID != "" || peerNode.EgressDetails.InternetGwID != "" {
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("node using a internet gw by the peer node"),
"badrequest",
),
)
return
}
if node.EgressDetails.IsInternetGateway && peerNode.EgressDetails.InternetGwID == node.ID.String() {
logic.ReturnErrorResponse( logic.ReturnErrorResponse(
w, w,
r, r,
@@ -400,7 +415,7 @@ func checkfailOverCtx(w http.ResponseWriter, r *http.Request) {
) )
return return
} }
if node.InternetGwID != "" && node.InternetGwID == peerNode.ID.String() { if node.EgressDetails.InternetGwID != "" && node.EgressDetails.InternetGwID == peerNode.ID.String() {
logic.ReturnErrorResponse( logic.ReturnErrorResponse(
w, w,
r, r,
@@ -411,6 +426,17 @@ func checkfailOverCtx(w http.ResponseWriter, r *http.Request) {
) )
return return
} }
if ok := logic.IsPeerAllowed(node, peerNode, true); !ok {
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("peers are not allowed to communicate"),
"badrequest",
),
)
return
}
err = proLogic.CheckFailOverCtx(failOverNode, node, peerNode) err = proLogic.CheckFailOverCtx(failOverNode, node, peerNode)
if err != nil { if err != nil {

View File

@@ -44,7 +44,7 @@ func createInternetGw(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return return
} }
if node.IsInternetGateway { if node.EgressDetails.IsInternetGateway {
logic.ReturnSuccessResponse(w, r, "node is already acting as internet gateway") logic.ReturnSuccessResponse(w, r, "node is already acting as internet gateway")
return return
} }
@@ -132,7 +132,7 @@ func updateInternetGw(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return return
} }
if !node.IsInternetGateway { if !node.EgressDetails.IsInternetGateway {
logic.ReturnErrorResponse( logic.ReturnErrorResponse(
w, w,
r, r,

View File

@@ -942,7 +942,7 @@ func getUserRemoteAccessNetworkGateways(w http.ResponseWriter, r *http.Request)
GwID: node.ID.String(), GwID: node.ID.String(),
GWName: host.Name, GWName: host.Name,
Network: node.Network, Network: node.Network,
IsInternetGateway: node.IsInternetGateway, IsInternetGateway: node.EgressDetails.IsInternetGateway,
Metadata: node.Metadata, Metadata: node.Metadata,
}) })
@@ -1069,7 +1069,7 @@ func getRemoteAccessGatewayConf(w http.ResponseWriter, r *http.Request) {
Network: node.Network, Network: node.Network,
GwClient: userConf, GwClient: userConf,
Connected: true, Connected: true,
IsInternetGateway: node.IsInternetGateway, IsInternetGateway: node.EgressDetails.IsInternetGateway,
GwPeerPublicKey: host.PublicKey.String(), GwPeerPublicKey: host.PublicKey.String(),
GwListenPort: logic.GetPeerListenPort(host), GwListenPort: logic.GetPeerListenPort(host),
Metadata: node.Metadata, Metadata: node.Metadata,
@@ -1161,7 +1161,7 @@ func getUserRemoteAccessGwsV1(w http.ResponseWriter, r *http.Request) {
Network: node.Network, Network: node.Network,
GwClient: extClient, GwClient: extClient,
Connected: true, Connected: true,
IsInternetGateway: node.IsInternetGateway, IsInternetGateway: node.EgressDetails.IsInternetGateway,
GwPeerPublicKey: host.PublicKey.String(), GwPeerPublicKey: host.PublicKey.String(),
GwListenPort: logic.GetPeerListenPort(host), GwListenPort: logic.GetPeerListenPort(host),
Metadata: node.Metadata, Metadata: node.Metadata,
@@ -1205,7 +1205,7 @@ func getUserRemoteAccessGwsV1(w http.ResponseWriter, r *http.Request) {
GwID: node.ID.String(), GwID: node.ID.String(),
GWName: host.Name, GWName: host.Name,
Network: node.Network, Network: node.Network,
IsInternetGateway: node.IsInternetGateway, IsInternetGateway: node.EgressDetails.IsInternetGateway,
GwPeerPublicKey: host.PublicKey.String(), GwPeerPublicKey: host.PublicKey.String(),
GwListenPort: logic.GetPeerListenPort(host), GwListenPort: logic.GetPeerListenPort(host),
Metadata: node.Metadata, Metadata: node.Metadata,

View File

@@ -110,6 +110,7 @@ func InitPro() {
logic.DeleteMetrics = proLogic.DeleteMetrics logic.DeleteMetrics = proLogic.DeleteMetrics
logic.GetTrialEndDate = getTrialEndDate logic.GetTrialEndDate = getTrialEndDate
logic.SetDefaultGw = proLogic.SetDefaultGw logic.SetDefaultGw = proLogic.SetDefaultGw
logic.ValidateInetGwReq = proLogic.ValidateInetGwReq
logic.SetDefaultGwForRelayedUpdate = proLogic.SetDefaultGwForRelayedUpdate logic.SetDefaultGwForRelayedUpdate = proLogic.SetDefaultGwForRelayedUpdate
logic.UnsetInternetGw = proLogic.UnsetInternetGw logic.UnsetInternetGw = proLogic.UnsetInternetGw
logic.SetInternetGw = proLogic.SetInternetGw logic.SetInternetGw = proLogic.SetInternetGw

View File

@@ -165,6 +165,7 @@ func GetFailOverPeerIps(peer, node *models.Node) []net.IPNet {
for failOverpeerID := range node.FailOverPeers { for failOverpeerID := range node.FailOverPeers {
failOverpeer, err := logic.GetNodeByID(failOverpeerID) failOverpeer, err := logic.GetNodeByID(failOverpeerID)
if err == nil && failOverpeer.FailedOverBy == peer.ID { if err == nil && failOverpeer.FailedOverBy == peer.ID {
logic.GetNodeEgressInfo(&failOverpeer)
if failOverpeer.Address.IP != nil { if failOverpeer.Address.IP != nil {
allowed := net.IPNet{ allowed := net.IPNet{
IP: failOverpeer.Address.IP, IP: failOverpeer.Address.IP,
@@ -179,7 +180,7 @@ func GetFailOverPeerIps(peer, node *models.Node) []net.IPNet {
} }
allowedips = append(allowedips, allowed) allowedips = append(allowedips, allowed)
} }
if failOverpeer.IsEgressGateway { if failOverpeer.EgressDetails.IsEgressGateway {
allowedips = append(allowedips, logic.GetEgressIPs(&failOverpeer)...) allowedips = append(allowedips, logic.GetEgressIPs(&failOverpeer)...)
} }
if failOverpeer.IsRelay { if failOverpeer.IsRelay {
@@ -199,7 +200,7 @@ func GetFailOverPeerIps(peer, node *models.Node) []net.IPNet {
} }
allowedips = append(allowedips, allowed) allowedips = append(allowedips, allowed)
} }
if rNode.IsEgressGateway { if rNode.EgressDetails.IsEgressGateway {
allowedips = append(allowedips, logic.GetEgressIPs(&rNode)...) allowedips = append(allowedips, logic.GetEgressIPs(&rNode)...)
} }
} }

View File

@@ -24,7 +24,7 @@ func ValidateInetGwReq(inetNode models.Node, req models.InetNodeReq, update bool
if inetHost.FirewallInUse == models.FIREWALL_NONE { if inetHost.FirewallInUse == models.FIREWALL_NONE {
return errors.New("iptables or nftables needs to be installed") return errors.New("iptables or nftables needs to be installed")
} }
if inetNode.InternetGwID != "" { if inetNode.EgressDetails.InternetGwID != "" {
return fmt.Errorf("node %s is using a internet gateway already", inetHost.Name) return fmt.Errorf("node %s is using a internet gateway already", inetHost.Name)
} }
if inetNode.IsRelayed { if inetNode.IsRelayed {
@@ -36,22 +36,28 @@ func ValidateInetGwReq(inetNode models.Node, req models.InetNodeReq, update bool
if err != nil { if err != nil {
return err return err
} }
if clientNode.IsFailOver {
return errors.New("failover node cannot be set to use internet gateway")
}
clientHost, err := logic.GetHost(clientNode.HostID.String()) clientHost, err := logic.GetHost(clientNode.HostID.String())
if err != nil { if err != nil {
return err 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 { 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") return errors.New("can only attach linux or windows machine to a internet gateway")
} }
if clientNode.IsInternetGateway { if clientNode.EgressDetails.IsInternetGateway {
return fmt.Errorf("node %s acting as internet gateway cannot use another internet gateway", clientHost.Name) return fmt.Errorf("node %s acting as internet gateway cannot use another internet gateway", clientHost.Name)
} }
if update { if update {
if clientNode.InternetGwID != "" && clientNode.InternetGwID != inetNode.ID.String() { if clientNode.EgressDetails.InternetGwID != "" && clientNode.EgressDetails.InternetGwID != inetNode.ID.String() {
return fmt.Errorf("node %s is already using a internet gateway", clientHost.Name) return fmt.Errorf("node %s is already using a internet gateway", clientHost.Name)
} }
} else { } else {
if clientNode.InternetGwID != "" { if clientNode.EgressDetails.InternetGwID != "" {
return fmt.Errorf("node %s is already using a internet gateway", clientHost.Name) return fmt.Errorf("node %s is already using a internet gateway", clientHost.Name)
} }
} }
@@ -68,7 +74,7 @@ func ValidateInetGwReq(inetNode models.Node, req models.InetNodeReq, update bool
if err != nil { if err != nil {
continue continue
} }
if node.InternetGwID != "" && node.InternetGwID != inetNode.ID.String() { if node.EgressDetails.InternetGwID != "" && node.EgressDetails.InternetGwID != inetNode.ID.String() {
return errors.New("nodes on same host cannot use different internet gateway") return errors.New("nodes on same host cannot use different internet gateway")
} }
@@ -79,14 +85,14 @@ func ValidateInetGwReq(inetNode models.Node, req models.InetNodeReq, update bool
// SetInternetGw - sets the node as internet gw based on flag bool // SetInternetGw - sets the node as internet gw based on flag bool
func SetInternetGw(node *models.Node, req models.InetNodeReq) { func SetInternetGw(node *models.Node, req models.InetNodeReq) {
node.IsInternetGateway = true node.EgressDetails.IsInternetGateway = true
node.InetNodeReq = req node.EgressDetails.InetNodeReq = req
for _, clientNodeID := range req.InetNodeClientIDs { for _, clientNodeID := range req.InetNodeClientIDs {
clientNode, err := logic.GetNodeByID(clientNodeID) clientNode, err := logic.GetNodeByID(clientNodeID)
if err != nil { if err != nil {
continue continue
} }
clientNode.InternetGwID = node.ID.String() clientNode.EgressDetails.InternetGwID = node.ID.String()
logic.UpsertNode(&clientNode) logic.UpsertNode(&clientNode)
} }
@@ -99,19 +105,19 @@ func UnsetInternetGw(node *models.Node) {
return return
} }
for _, clientNode := range nodes { for _, clientNode := range nodes {
if node.ID.String() == clientNode.InternetGwID { if node.ID.String() == clientNode.EgressDetails.InternetGwID {
clientNode.InternetGwID = "" clientNode.EgressDetails.InternetGwID = ""
logic.UpsertNode(&clientNode) logic.UpsertNode(&clientNode)
} }
} }
node.IsInternetGateway = false node.EgressDetails.IsInternetGateway = false
node.InetNodeReq = models.InetNodeReq{} node.EgressDetails.InetNodeReq = models.InetNodeReq{}
} }
func SetDefaultGwForRelayedUpdate(relayed, relay models.Node, peerUpdate models.HostPeerUpdate) models.HostPeerUpdate { func SetDefaultGwForRelayedUpdate(relayed, relay models.Node, peerUpdate models.HostPeerUpdate) models.HostPeerUpdate {
if relay.InternetGwID != "" { if relay.EgressDetails.InternetGwID != "" {
relayedHost, err := logic.GetHost(relayed.HostID.String()) relayedHost, err := logic.GetHost(relayed.HostID.String())
if err != nil { if err != nil {
return peerUpdate return peerUpdate
@@ -127,9 +133,9 @@ func SetDefaultGwForRelayedUpdate(relayed, relay models.Node, peerUpdate models.
} }
func SetDefaultGw(node models.Node, peerUpdate models.HostPeerUpdate) models.HostPeerUpdate { func SetDefaultGw(node models.Node, peerUpdate models.HostPeerUpdate) models.HostPeerUpdate {
if node.InternetGwID != "" { if node.EgressDetails.InternetGwID != "" {
inetNode, err := logic.GetNodeByID(node.InternetGwID) inetNode, err := logic.GetNodeByID(node.EgressDetails.InternetGwID)
if err != nil { if err != nil {
return peerUpdate return peerUpdate
} }

View File

@@ -7,7 +7,6 @@ import (
"time" "time"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq" "github.com/gravitl/netmaker/mq"
@@ -659,30 +658,13 @@ func GetUserRAGNodesV1(user models.User) (gws map[string]models.Node) {
func GetUserRAGNodes(user models.User) (gws map[string]models.Node) { func GetUserRAGNodes(user models.User) (gws map[string]models.Node) {
gws = make(map[string]models.Node) gws = make(map[string]models.Node)
userGwAccessScope := GetUserNetworkRolesWithRemoteVPNAccess(user)
logger.Log(3, fmt.Sprintf("User Gw Access Scope: %+v", userGwAccessScope))
_, allNetAccess := userGwAccessScope["*"]
nodes, err := logic.GetAllNodes() nodes, err := logic.GetAllNodes()
if err != nil { if err != nil {
return return
} }
for _, node := range nodes { for _, node := range nodes {
if node.IsIngressGateway && !node.PendingDelete { if ok, _ := logic.IsUserAllowedToCommunicate(user.UserName, node); ok {
if allNetAccess {
gws[node.ID.String()] = node gws[node.ID.String()] = node
} else {
gwRsrcMap := userGwAccessScope[models.NetworkID(node.Network)]
scope, ok := gwRsrcMap[models.AllRemoteAccessGwRsrcID]
if !ok {
if scope, ok = gwRsrcMap[models.RsrcID(node.ID.String())]; !ok {
continue
}
}
if scope.VPNaccess {
gws[node.ID.String()] = node
}
}
} }
} }
return return

4
schema/activity.go Normal file
View File

@@ -0,0 +1,4 @@
package schema
type Activity struct {
}

70
schema/egress.go Normal file
View File

@@ -0,0 +1,70 @@
package schema
import (
"context"
"time"
"github.com/gravitl/netmaker/db"
"gorm.io/datatypes"
)
const egressTable = "egresses"
type Egress struct {
ID string `gorm:"primaryKey" json:"id"`
Name string `gorm:"name" json:"name"`
Network string `gorm:"network" json:"network"`
Description string `gorm:"description" json:"description"`
Nodes datatypes.JSONMap `gorm:"nodes" json:"nodes"`
Tags datatypes.JSONMap `gorm:"tags" json:"tags"`
Range string `gorm:"range" json:"range"`
Nat bool `gorm:"nat" json:"nat"`
IsInetGw bool `gorm:"is_inet_gw" json:"is_internet_gateway"`
Status bool `gorm:"status" json:"status"`
CreatedBy string `gorm:"created_by" json:"created_by"`
CreatedAt time.Time `gorm:"created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"updated_at" json:"updated_at"`
}
func (e *Egress) Table() string {
return egressTable
}
func (e *Egress) Get(ctx context.Context) error {
return db.FromContext(ctx).Table(e.Table()).First(&e).Where("id = ?", e.ID).Error
}
func (e *Egress) Update(ctx context.Context) error {
return db.FromContext(ctx).Table(e.Table()).Where("id = ?", e.ID).Updates(&e).Error
}
func (e *Egress) UpdateNatStatus(ctx context.Context) error {
return db.FromContext(ctx).Table(e.Table()).Where("id = ?", e.ID).Updates(map[string]any{
"nat": e.Nat,
}).Error
}
func (e *Egress) UpdateINetGwStatus(ctx context.Context) error {
return db.FromContext(ctx).Table(e.Table()).Where("id = ?", e.ID).Updates(map[string]any{
"is_inet_gw": e.IsInetGw,
}).Error
}
func (e *Egress) UpdateEgressStatus(ctx context.Context) error {
return db.FromContext(ctx).Table(e.Table()).Where("id = ?", e.ID).Updates(map[string]any{
"status": e.Status,
}).Error
}
func (e *Egress) Create(ctx context.Context) error {
return db.FromContext(ctx).Table(e.Table()).Create(&e).Error
}
func (e *Egress) ListByNetwork(ctx context.Context) (egs []Egress, err error) {
err = db.FromContext(ctx).Table(e.Table()).Where("network = ?", e.Network).Find(&egs).Error
return
}
func (e *Egress) Delete(ctx context.Context) error {
return db.FromContext(ctx).Table(e.Table()).Where("id = ?", e.ID).Delete(&e).Error
}

View File

@@ -4,6 +4,7 @@ package schema
func ListModels() []interface{} { func ListModels() []interface{} {
return []interface{}{ return []interface{}{
&Job{}, &Job{},
&Egress{},
&UserAccessToken{}, &UserAccessToken{},
} }
} }