mirror of
https://github.com/gravitl/netmaker.git
synced 2025-09-07 21:54:54 +08:00
* 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>
366 lines
9 KiB
Go
366 lines
9 KiB
Go
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()))
|
|
}
|
|
}
|
|
|
|
}
|