mirror of
https://github.com/gravitl/netmaker.git
synced 2025-09-26 21:01:32 +08:00
NM-79: Domain Based Egress Routing (#3607)
* add support for egress domain routing * add domain info to egress range * fix egress domain update * send peer update domain resolution update * add egress domain update in the peer update * use range field for domain check * add egress domain to host pull * add egress domain model to egress host update * add egress domain model to egress host update * update egress domain model on acls * add check of range if domain is set * sync egress domains to dns system * add egress domain to match domain list, fix egress nat rule for domains * fix all rsrcs comms * fix static checks * fix egress acls on CE * check for all resources access on a node * simplify egress acl rules * merged ce and pro acl rule func * fix uni direction acl rule for static nodes * allow relayed nodes traffic * resolve merge conflicts * remove anywhere dst rule on user node acls * fix: broadcast user groups update for acl changes * add egress domain ans routes to nodes * add egress ranges to DST * add all egress ranges for all resources * fix DNS routing acls rules
This commit is contained in:
@@ -45,14 +45,27 @@ func createEgress(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
var egressRange string
|
||||
var cidrErr error
|
||||
if !req.IsInetGw {
|
||||
egressRange, err = logic.NormalizeCIDR(req.Range)
|
||||
if err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
if req.Range != "" {
|
||||
egressRange, cidrErr = logic.NormalizeCIDR(req.Range)
|
||||
}
|
||||
isDomain := logic.IsFQDN(req.Range)
|
||||
if cidrErr != nil && !isDomain {
|
||||
if cidrErr != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(cidrErr, "badrequest"))
|
||||
} else {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("bad domain name"), "badrequest"))
|
||||
}
|
||||
return
|
||||
}
|
||||
if isDomain {
|
||||
req.Domain = req.Range
|
||||
egressRange = ""
|
||||
}
|
||||
} else {
|
||||
egressRange = "*"
|
||||
req.Domain = ""
|
||||
}
|
||||
|
||||
e := schema.Egress{
|
||||
@@ -61,6 +74,8 @@ func createEgress(w http.ResponseWriter, r *http.Request) {
|
||||
Network: req.Network,
|
||||
Description: req.Description,
|
||||
Range: egressRange,
|
||||
Domain: req.Domain,
|
||||
DomainAns: []string{},
|
||||
Nat: req.Nat,
|
||||
Nodes: make(datatypes.JSONMap),
|
||||
Tags: make(datatypes.JSONMap),
|
||||
@@ -108,7 +123,35 @@ func createEgress(w http.ResponseWriter, r *http.Request) {
|
||||
// }
|
||||
|
||||
// }
|
||||
go mq.PublishPeerUpdate(false)
|
||||
if req.Domain != "" {
|
||||
if req.Nodes != nil {
|
||||
for nodeID := range req.Nodes {
|
||||
node, err := logic.GetNodeByID(nodeID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
host, _ := logic.GetHost(node.HostID.String())
|
||||
if host == nil {
|
||||
continue
|
||||
}
|
||||
mq.HostUpdate(&models.HostUpdate{
|
||||
Action: models.EgressUpdate,
|
||||
Host: *host,
|
||||
EgressDomain: models.EgressDomain{
|
||||
ID: e.ID,
|
||||
Host: *host,
|
||||
Node: node,
|
||||
Domain: e.Domain,
|
||||
},
|
||||
Node: node,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
go mq.PublishPeerUpdate(false)
|
||||
}
|
||||
|
||||
logic.ReturnSuccessResponseWithJson(w, r, e, "created egress resource")
|
||||
}
|
||||
|
||||
@@ -161,14 +204,25 @@ func updateEgress(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
var egressRange string
|
||||
var cidrErr error
|
||||
if !req.IsInetGw {
|
||||
egressRange, err = logic.NormalizeCIDR(req.Range)
|
||||
if err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
egressRange, cidrErr = logic.NormalizeCIDR(req.Range)
|
||||
isDomain := logic.IsFQDN(req.Range)
|
||||
if cidrErr != nil && !isDomain {
|
||||
if cidrErr != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(cidrErr, "badrequest"))
|
||||
} else {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("bad domain name"), "badrequest"))
|
||||
}
|
||||
return
|
||||
}
|
||||
if isDomain {
|
||||
req.Domain = req.Range
|
||||
egressRange = ""
|
||||
}
|
||||
} else {
|
||||
egressRange = "*"
|
||||
req.Domain = ""
|
||||
}
|
||||
|
||||
e := schema.Egress{ID: req.ID}
|
||||
@@ -209,10 +263,14 @@ func updateEgress(w http.ResponseWriter, r *http.Request) {
|
||||
for nodeID, metric := range req.Nodes {
|
||||
e.Nodes[nodeID] = metric
|
||||
}
|
||||
if e.Domain != req.Domain {
|
||||
e.DomainAns = datatypes.JSONSlice[string]{}
|
||||
}
|
||||
e.Range = egressRange
|
||||
e.Description = req.Description
|
||||
e.Name = req.Name
|
||||
e.Nat = req.Nat
|
||||
e.Domain = req.Domain
|
||||
e.Status = req.Status
|
||||
e.UpdatedAt = time.Now().UTC()
|
||||
if err := logic.ValidateEgressReq(&e); err != nil {
|
||||
@@ -238,6 +296,34 @@ func updateEgress(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
event.Diff.New = e
|
||||
logic.LogEvent(event)
|
||||
if req.Domain != "" {
|
||||
if req.Nodes != nil {
|
||||
for nodeID := range req.Nodes {
|
||||
node, err := logic.GetNodeByID(nodeID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
host, _ := logic.GetHost(node.HostID.String())
|
||||
if host == nil {
|
||||
continue
|
||||
}
|
||||
mq.HostUpdate(&models.HostUpdate{
|
||||
Action: models.EgressUpdate,
|
||||
Host: *host,
|
||||
EgressDomain: models.EgressDomain{
|
||||
ID: e.ID,
|
||||
Host: *host,
|
||||
Node: node,
|
||||
Domain: e.Domain,
|
||||
},
|
||||
Node: node,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
go mq.PublishPeerUpdate(false)
|
||||
}
|
||||
go mq.PublishPeerUpdate(false)
|
||||
logic.ReturnSuccessResponseWithJson(w, r, e, "updated egress resource")
|
||||
}
|
||||
|
@@ -253,11 +253,13 @@ func pull(w http.ResponseWriter, r *http.Request) {
|
||||
ChangeDefaultGw: hPU.ChangeDefaultGw,
|
||||
DefaultGwIp: hPU.DefaultGwIp,
|
||||
IsInternetGw: hPU.IsInternetGw,
|
||||
NameServers: hPU.NameServers,
|
||||
EgressWithDomains: hPU.EgressWithDomains,
|
||||
EndpointDetection: logic.IsEndpointDetectionEnabled(),
|
||||
DnsNameservers: hPU.DnsNameservers,
|
||||
}
|
||||
|
||||
logger.Log(1, hostID, "completed a pull")
|
||||
logger.Log(1, hostID, host.Name, "completed a pull")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(&response)
|
||||
}
|
||||
@@ -374,7 +376,6 @@ func hostUpdateFallback(w http.ResponseWriter, r *http.Request) {
|
||||
switch hostUpdate.Action {
|
||||
case models.CheckIn:
|
||||
sendPeerUpdate = mq.HandleHostCheckin(&hostUpdate.Host, currentHost)
|
||||
|
||||
case models.UpdateHost:
|
||||
if hostUpdate.Host.PublicKey != currentHost.PublicKey {
|
||||
//remove old peer entry
|
||||
@@ -384,12 +385,24 @@ func hostUpdateFallback(w http.ResponseWriter, r *http.Request) {
|
||||
err := logic.UpsertHost(currentHost)
|
||||
if err != nil {
|
||||
slog.Error("failed to update host", "id", currentHost.ID, "error", err)
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.Internal))
|
||||
return
|
||||
}
|
||||
|
||||
case models.UpdateMetrics:
|
||||
mq.UpdateMetricsFallBack(hostUpdate.Node.ID.String(), hostUpdate.NewMetrics)
|
||||
case models.EgressUpdate:
|
||||
e := schema.Egress{ID: hostUpdate.EgressDomain.ID}
|
||||
err = e.Get(db.WithContext(r.Context()))
|
||||
if err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.BadReq))
|
||||
return
|
||||
}
|
||||
if len(hostUpdate.Node.EgressGatewayRanges) > 0 {
|
||||
e.DomainAns = hostUpdate.Node.EgressGatewayRanges
|
||||
e.Update(db.WithContext(r.Context()))
|
||||
}
|
||||
sendPeerUpdate = true
|
||||
}
|
||||
|
||||
if sendPeerUpdate {
|
||||
|
116
logic/acls.go
116
logic/acls.go
@@ -54,6 +54,9 @@ func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) {
|
||||
if !nodeI.IsStatic || nodeI.IsUserNode {
|
||||
continue
|
||||
}
|
||||
if !node.StaticNode.Enabled {
|
||||
continue
|
||||
}
|
||||
// if nodeI.StaticNode.IngressGatewayID != node.ID.String() {
|
||||
// continue
|
||||
// }
|
||||
@@ -292,35 +295,70 @@ func getFwRulesForNodeAndPeerOnGw(node, peer models.Node, allowedPolicies []mode
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
dstI.Value = e.Range
|
||||
if len(e.DomainAns) > 0 {
|
||||
for _, domainAnsI := range e.DomainAns {
|
||||
dstI.Value = domainAnsI
|
||||
|
||||
ip, cidr, err := net.ParseCIDR(dstI.Value)
|
||||
if err == nil {
|
||||
if ip.To4() != nil {
|
||||
if node.Address.IP != nil {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: net.IPNet{
|
||||
IP: node.Address.IP,
|
||||
Mask: net.CIDRMask(32, 32),
|
||||
},
|
||||
DstIP: *cidr,
|
||||
Allow: true,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if node.Address6.IP != nil {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: net.IPNet{
|
||||
IP: node.Address6.IP,
|
||||
Mask: net.CIDRMask(128, 128),
|
||||
},
|
||||
DstIP: *cidr,
|
||||
Allow: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ip, cidr, err := net.ParseCIDR(dstI.Value)
|
||||
if err == nil {
|
||||
if ip.To4() != nil {
|
||||
if node.Address.IP != nil {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: net.IPNet{
|
||||
IP: node.Address.IP,
|
||||
Mask: net.CIDRMask(32, 32),
|
||||
},
|
||||
DstIP: *cidr,
|
||||
Allow: true,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if node.Address6.IP != nil {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: net.IPNet{
|
||||
IP: node.Address6.IP,
|
||||
Mask: net.CIDRMask(128, 128),
|
||||
},
|
||||
DstIP: *cidr,
|
||||
Allow: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dstI.Value = e.Range
|
||||
|
||||
ip, cidr, err := net.ParseCIDR(dstI.Value)
|
||||
if err == nil {
|
||||
if ip.To4() != nil {
|
||||
if node.Address.IP != nil {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: net.IPNet{
|
||||
IP: node.Address.IP,
|
||||
Mask: net.CIDRMask(32, 32),
|
||||
},
|
||||
DstIP: *cidr,
|
||||
Allow: true,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if node.Address6.IP != nil {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: net.IPNet{
|
||||
IP: node.Address6.IP,
|
||||
Mask: net.CIDRMask(128, 128),
|
||||
},
|
||||
DstIP: *cidr,
|
||||
Allow: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -364,6 +402,9 @@ func GetStaticNodeIps(node models.Node) (ips []net.IP) {
|
||||
if !extclient.IsUserNode && defaultDevicePolicy.Enabled {
|
||||
continue
|
||||
}
|
||||
if !extclient.StaticNode.Enabled {
|
||||
continue
|
||||
}
|
||||
if extclient.StaticNode.Address != "" {
|
||||
ips = append(ips, extclient.StaticNode.AddressIPNet4().IP)
|
||||
}
|
||||
@@ -673,7 +714,6 @@ func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRu
|
||||
}
|
||||
|
||||
func GetEgressRulesForNode(targetnode models.Node) (rules map[string]models.AclRule) {
|
||||
fmt.Println("==========> Getting Egress FW rules ", targetnode.ID)
|
||||
rules = make(map[string]models.AclRule)
|
||||
defer func() {
|
||||
rules = GetEgressUserRulesForNode(&targetnode, rules)
|
||||
@@ -720,14 +760,28 @@ func GetEgressRulesForNode(targetnode models.Node) (rules map[string]models.AclR
|
||||
}
|
||||
for egressID, egI := range egressIDMap {
|
||||
if _, ok := dstTags[egressID]; ok || dstAll {
|
||||
ip, cidr, err := net.ParseCIDR(egI.Range)
|
||||
if err == nil {
|
||||
if ip.To4() != nil {
|
||||
aclRule.Dst = append(aclRule.Dst, *cidr)
|
||||
} else {
|
||||
aclRule.Dst6 = append(aclRule.Dst6, *cidr)
|
||||
if servercfg.IsPro && egI.Domain != "" && len(egI.DomainAns) > 0 {
|
||||
for _, domainAnsI := range egI.DomainAns {
|
||||
ip, cidr, err := net.ParseCIDR(domainAnsI)
|
||||
if err == nil {
|
||||
if ip.To4() != nil {
|
||||
aclRule.Dst = append(aclRule.Dst, *cidr)
|
||||
} else {
|
||||
aclRule.Dst6 = append(aclRule.Dst6, *cidr)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ip, cidr, err := net.ParseCIDR(egI.Range)
|
||||
if err == nil {
|
||||
if ip.To4() != nil {
|
||||
aclRule.Dst = append(aclRule.Dst, *cidr)
|
||||
} else {
|
||||
aclRule.Dst6 = append(aclRule.Dst6, *cidr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, srcAll := srcTags["*"]
|
||||
if srcAll {
|
||||
if targetnode.NetworkRange.IP != nil {
|
||||
|
26
logic/dns.go
26
logic/dns.go
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
@@ -118,6 +119,31 @@ func GetDNS(network string) ([]models.DNSEntry, error) {
|
||||
return dns, nil
|
||||
}
|
||||
|
||||
func EgressDNs(network string) (entries []models.DNSEntry) {
|
||||
egs, _ := (&schema.Egress{
|
||||
Network: network,
|
||||
}).ListByNetwork(db.WithContext(context.TODO()))
|
||||
for _, egI := range egs {
|
||||
if egI.Domain != "" && len(egI.DomainAns) > 0 {
|
||||
entry := models.DNSEntry{
|
||||
Name: egI.Domain,
|
||||
}
|
||||
for _, domainAns := range egI.DomainAns {
|
||||
ip, _, err := net.ParseCIDR(domainAns)
|
||||
if err == nil {
|
||||
if ip.To4() != nil {
|
||||
entry.Address = ip.String()
|
||||
} else {
|
||||
entry.Address6 = ip.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetExtclientDNS - gets all extclients dns entries
|
||||
func GetExtclientDNS() []models.DNSEntry {
|
||||
extclients, err := GetAllExtClients()
|
||||
|
131
logic/egress.go
131
logic/egress.go
@@ -36,6 +36,35 @@ func ValidateEgressReq(e *schema.Egress) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func DoesUserHaveAccessToEgress(user *models.User, e *schema.Egress, acls []models.Acl) bool {
|
||||
|
||||
if !e.Status {
|
||||
return false
|
||||
}
|
||||
for _, acl := range acls {
|
||||
if !acl.Enabled {
|
||||
continue
|
||||
}
|
||||
dstTags := ConvAclTagToValueMap(acl.Dst)
|
||||
_, all := dstTags["*"]
|
||||
|
||||
if _, ok := dstTags[e.ID]; ok || all {
|
||||
// get all src tags
|
||||
for _, srcAcl := range acl.Src {
|
||||
if srcAcl.ID == models.UserAclID && srcAcl.Value == user.UserName {
|
||||
return true
|
||||
} else if srcAcl.ID == models.UserGroupAclID {
|
||||
// fetch all users in the group
|
||||
if _, ok := user.UserGroups[models.UserGroupID(srcAcl.Value)]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func DoesNodeHaveAccessToEgress(node *models.Node, e *schema.Egress, acls []models.Acl) bool {
|
||||
nodeTags := maps.Clone(node.Tags)
|
||||
nodeTags[models.TagID(node.ID.String())] = struct{}{}
|
||||
@@ -107,12 +136,31 @@ func AddEgressInfoToPeerByAccess(node, targetNode *models.Node, eli []schema.Egr
|
||||
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 e.Range != "" {
|
||||
req.Ranges = append(req.Ranges, e.Range)
|
||||
} else {
|
||||
req.Ranges = append(req.Ranges, e.DomainAns...)
|
||||
}
|
||||
|
||||
if e.Range != "" {
|
||||
req.Ranges = append(req.Ranges, e.Range)
|
||||
req.RangesWithMetric = append(req.RangesWithMetric, models.EgressRangeMetric{
|
||||
Network: e.Range,
|
||||
Nat: e.Nat,
|
||||
RouteMetric: m,
|
||||
})
|
||||
}
|
||||
if e.Domain != "" && len(e.DomainAns) > 0 {
|
||||
req.Ranges = append(req.Ranges, e.DomainAns...)
|
||||
for _, domainAnsI := range e.DomainAns {
|
||||
req.RangesWithMetric = append(req.RangesWithMetric, models.EgressRangeMetric{
|
||||
Network: domainAnsI,
|
||||
Nat: e.Nat,
|
||||
RouteMetric: m,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if targetNode.Mutex != nil {
|
||||
@@ -132,6 +180,27 @@ func AddEgressInfoToPeerByAccess(node, targetNode *models.Node, eli []schema.Egr
|
||||
}
|
||||
}
|
||||
|
||||
func GetEgressDomainsByAccess(user *models.User, network models.NetworkID) (domains []string) {
|
||||
acls, _ := ListAclsByNetwork(network)
|
||||
eli, _ := (&schema.Egress{Network: network.String()}).ListByNetwork(db.WithContext(context.TODO()))
|
||||
defaultDevicePolicy, _ := GetDefaultPolicy(network, models.DevicePolicy)
|
||||
isDefaultPolicyActive := defaultDevicePolicy.Enabled
|
||||
for _, e := range eli {
|
||||
if !e.Status || e.Network != network.String() {
|
||||
continue
|
||||
}
|
||||
if !isDefaultPolicyActive {
|
||||
if !DoesUserHaveAccessToEgress(user, &e, acls) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if e.Domain != "" && len(e.DomainAns) > 0 {
|
||||
domains = append(domains, e.Domain)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetNodeEgressInfo(targetNode *models.Node, eli []schema.Egress, acls []models.Acl) {
|
||||
|
||||
req := models.EgressGatewayRequest{
|
||||
@@ -149,12 +218,25 @@ func GetNodeEgressInfo(targetNode *models.Node, eli []schema.Egress, acls []mode
|
||||
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 e.Range != "" {
|
||||
req.Ranges = append(req.Ranges, e.Range)
|
||||
req.RangesWithMetric = append(req.RangesWithMetric, models.EgressRangeMetric{
|
||||
Network: e.Range,
|
||||
Nat: e.Nat,
|
||||
RouteMetric: m,
|
||||
})
|
||||
}
|
||||
if e.Domain != "" && len(e.DomainAns) > 0 {
|
||||
req.Ranges = append(req.Ranges, e.DomainAns...)
|
||||
for _, domainAnsI := range e.DomainAns {
|
||||
req.RangesWithMetric = append(req.RangesWithMetric, models.EgressRangeMetric{
|
||||
Network: domainAnsI,
|
||||
Nat: e.Nat,
|
||||
RouteMetric: m,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -218,3 +300,28 @@ func GetEgressRanges(netID models.NetworkID) (map[string][]string, map[string]st
|
||||
}
|
||||
return nodeEgressMap, resultMap, nil
|
||||
}
|
||||
|
||||
func ListAllByRoutingNodeWithDomain(egs []schema.Egress, nodeID string) (egWithDomain []models.EgressDomain) {
|
||||
for _, egI := range egs {
|
||||
if !egI.Status || egI.Domain == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := egI.Nodes[nodeID]; ok {
|
||||
node, err := GetNodeByID(nodeID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
host, err := GetHost(node.HostID.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
egWithDomain = append(egWithDomain, models.EgressDomain{
|
||||
ID: egI.ID,
|
||||
Domain: egI.Domain,
|
||||
Node: node,
|
||||
Host: *host,
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@@ -722,21 +722,3 @@ func GetStaticNodesByNetwork(network models.NetworkID, onlyWg bool) (staticNode
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func GetStaticNodesByGw(gwNode models.Node) (staticNode []models.Node) {
|
||||
extClients, err := GetAllExtClients()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, extI := range extClients {
|
||||
if extI.IngressGatewayID == gwNode.ID.String() {
|
||||
n := models.Node{
|
||||
IsStatic: true,
|
||||
StaticNode: extI,
|
||||
IsUserNode: extI.RemoteAccessClientID != "",
|
||||
}
|
||||
staticNode = append(staticNode, n)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@@ -6,7 +6,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
@@ -18,6 +17,7 @@ import (
|
||||
"github.com/gravitl/netmaker/logger"
|
||||
"github.com/gravitl/netmaker/models"
|
||||
"github.com/gravitl/netmaker/servercfg"
|
||||
"github.com/gravitl/netmaker/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -310,17 +310,22 @@ func UpdateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool)
|
||||
sendPeerUpdate = true
|
||||
}
|
||||
isEndpointChanged := false
|
||||
if currHost.EndpointIP.String() != newHost.EndpointIP.String() {
|
||||
if !currHost.EndpointIP.Equal(newHost.EndpointIP) {
|
||||
currHost.EndpointIP = newHost.EndpointIP
|
||||
sendPeerUpdate = true
|
||||
isEndpointChanged = true
|
||||
}
|
||||
if currHost.EndpointIPv6.String() != newHost.EndpointIPv6.String() {
|
||||
if !currHost.EndpointIPv6.Equal(newHost.EndpointIPv6) {
|
||||
currHost.EndpointIPv6 = newHost.EndpointIPv6
|
||||
sendPeerUpdate = true
|
||||
isEndpointChanged = true
|
||||
}
|
||||
if !reflect.DeepEqual(currHost.Interfaces, newHost.Interfaces) {
|
||||
for i := range newHost.Interfaces {
|
||||
newHost.Interfaces[i].AddressString = newHost.Interfaces[i].Address.String()
|
||||
}
|
||||
utils.SortIfacesByName(currHost.Interfaces)
|
||||
utils.SortIfacesByName(newHost.Interfaces)
|
||||
if !utils.CompareIfaces(currHost.Interfaces, newHost.Interfaces) {
|
||||
currHost.Interfaces = newHost.Interfaces
|
||||
sendPeerUpdate = true
|
||||
}
|
||||
|
@@ -76,7 +76,7 @@ func GetHostPeerInfo(host *models.Host) (models.HostPeerInfo, error) {
|
||||
|
||||
peerHost, err := GetHost(peer.HostID.String())
|
||||
if err != nil {
|
||||
logger.Log(1, "no peer host", peer.HostID.String(), err.Error())
|
||||
logger.Log(4, "no peer host", peer.HostID.String(), err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -182,6 +182,10 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
|
||||
acls, _ := ListAclsByNetwork(models.NetworkID(node.Network))
|
||||
eli, _ := (&schema.Egress{Network: node.Network}).ListByNetwork(db.WithContext(context.TODO()))
|
||||
GetNodeEgressInfo(&node, eli, acls)
|
||||
if node.EgressDetails.IsEgressGateway {
|
||||
egsWithDomain := ListAllByRoutingNodeWithDomain(eli, node.ID.String())
|
||||
hostPeerUpdate.EgressWithDomains = append(hostPeerUpdate.EgressWithDomains, egsWithDomain...)
|
||||
}
|
||||
hostPeerUpdate = SetDefaultGw(node, hostPeerUpdate)
|
||||
if !hostPeerUpdate.IsInternetGw {
|
||||
hostPeerUpdate.IsInternetGw = IsInternetGw(node)
|
||||
@@ -231,7 +235,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
|
||||
|
||||
peerHost, err := GetHost(peer.HostID.String())
|
||||
if err != nil {
|
||||
logger.Log(1, "no peer host", peer.HostID.String(), err.Error())
|
||||
logger.Log(4, "no peer host", peer.HostID.String(), err.Error())
|
||||
continue
|
||||
}
|
||||
peerConfig := wgtypes.PeerConfig{
|
||||
|
@@ -12,6 +12,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
@@ -273,3 +274,17 @@ func compareIface(a, b models.Iface) bool {
|
||||
a.Address.Mask.String() == b.Address.Mask.String() &&
|
||||
a.AddressString == b.AddressString
|
||||
}
|
||||
|
||||
// IsFQDN checks if the given string is a valid Fully Qualified Domain Name (FQDN)
|
||||
func IsFQDN(domain string) bool {
|
||||
// Basic check to ensure the domain is not empty and has at least one dot (.)
|
||||
if domain == "" || !strings.Contains(domain, ".") {
|
||||
return false
|
||||
}
|
||||
|
||||
// Regular expression for validating FQDN (basic check for valid characters and structure)
|
||||
fqdnRegex := `^(?i)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$`
|
||||
re := regexp.MustCompile(fqdnRegex)
|
||||
|
||||
return re.MatchString(domain)
|
||||
}
|
||||
|
@@ -343,6 +343,10 @@ func updateHosts() {
|
||||
}
|
||||
logic.UpsertHost(&host)
|
||||
}
|
||||
if host.IsDefault && !host.AutoUpdate {
|
||||
host.AutoUpdate = true
|
||||
logic.UpsertHost(&host)
|
||||
}
|
||||
if servercfg.IsPro && host.Location == "" {
|
||||
if host.EndpointIP != nil {
|
||||
host.Location = logic.GetHostLocInfo(host.EndpointIP.String(), os.Getenv("IP_INFO_TOKEN"))
|
||||
|
@@ -8,6 +8,7 @@ type EgressReq struct {
|
||||
Nodes map[string]int `json:"nodes"`
|
||||
Tags []string `json:"tags"`
|
||||
Range string `json:"range"`
|
||||
Domain string `json:"domain"`
|
||||
Nat bool `json:"nat"`
|
||||
Status bool `json:"status"`
|
||||
IsInetGw bool `json:"is_internet_gateway"`
|
||||
|
@@ -124,6 +124,8 @@ const (
|
||||
SignalPull HostMqAction = "SIGNAL_PULL"
|
||||
// UpdateMetrics - updates metrics data
|
||||
UpdateMetrics HostMqAction = "UPDATE_METRICS"
|
||||
// EgressUpdate - const for egress update action
|
||||
EgressUpdate HostMqAction = "EGRESS_UPDATE"
|
||||
)
|
||||
|
||||
// SignalAction - turn peer signal action
|
||||
@@ -138,11 +140,12 @@ const (
|
||||
|
||||
// HostUpdate - struct for host update
|
||||
type HostUpdate struct {
|
||||
Action HostMqAction
|
||||
Host Host
|
||||
Node Node
|
||||
Signal Signal
|
||||
NewMetrics Metrics
|
||||
Action HostMqAction
|
||||
Host Host
|
||||
Node Node
|
||||
Signal Signal
|
||||
EgressDomain EgressDomain
|
||||
NewMetrics Metrics
|
||||
}
|
||||
|
||||
// HostTurnRegister - struct for host turn registration
|
||||
|
@@ -12,27 +12,34 @@ type HostPeerInfo struct {
|
||||
|
||||
// HostPeerUpdate - struct for host peer updates
|
||||
type HostPeerUpdate struct {
|
||||
Host Host `json:"host"`
|
||||
ChangeDefaultGw bool `json:"change_default_gw"`
|
||||
DefaultGwIp net.IP `json:"default_gw_ip"`
|
||||
IsInternetGw bool `json:"is_inet_gw"`
|
||||
NodeAddrs []net.IPNet `json:"nodes_addrs"`
|
||||
Server string `json:"server"`
|
||||
ServerVersion string `json:"serverversion"`
|
||||
ServerAddrs []ServerAddr `json:"serveraddrs"`
|
||||
NodePeers []wgtypes.PeerConfig `json:"node_peers"`
|
||||
Peers []wgtypes.PeerConfig `json:"host_peers"`
|
||||
PeerIDs PeerMap `json:"peerids"`
|
||||
HostNetworkInfo HostInfoMap `json:"host_network_info,omitempty"`
|
||||
EgressRoutes []EgressNetworkRoutes `json:"egress_network_routes"`
|
||||
FwUpdate FwUpdate `json:"fw_update"`
|
||||
ReplacePeers bool `json:"replace_peers"`
|
||||
NameServers []string `json:"name_servers"`
|
||||
DnsNameservers []Nameserver `json:"dns_nameservers"`
|
||||
Host Host `json:"host"`
|
||||
ChangeDefaultGw bool `json:"change_default_gw"`
|
||||
DefaultGwIp net.IP `json:"default_gw_ip"`
|
||||
IsInternetGw bool `json:"is_inet_gw"`
|
||||
NodeAddrs []net.IPNet `json:"nodes_addrs"`
|
||||
Server string `json:"server"`
|
||||
ServerVersion string `json:"serverversion"`
|
||||
ServerAddrs []ServerAddr `json:"serveraddrs"`
|
||||
NodePeers []wgtypes.PeerConfig `json:"node_peers"`
|
||||
Peers []wgtypes.PeerConfig `json:"host_peers"`
|
||||
PeerIDs PeerMap `json:"peerids"`
|
||||
HostNetworkInfo HostInfoMap `json:"host_network_info,omitempty"`
|
||||
EgressRoutes []EgressNetworkRoutes `json:"egress_network_routes"`
|
||||
FwUpdate FwUpdate `json:"fw_update"`
|
||||
ReplacePeers bool `json:"replace_peers"`
|
||||
NameServers []string `json:"name_servers"`
|
||||
DnsNameservers []Nameserver `json:"dns_nameservers"`
|
||||
EgressWithDomains []EgressDomain `json:"egress_with_domains"`
|
||||
ServerConfig
|
||||
OldPeerUpdateFields
|
||||
}
|
||||
|
||||
type EgressDomain struct {
|
||||
ID string `json:"id"`
|
||||
Node Node `json:"node"`
|
||||
Host Host `json:"host"`
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
type Nameserver struct {
|
||||
IPs []string `json:"ips"`
|
||||
MatchDomain string `json:"match_domain"`
|
||||
|
@@ -262,6 +262,8 @@ type HostPull struct {
|
||||
DefaultGwIp net.IP `json:"default_gw_ip"`
|
||||
IsInternetGw bool `json:"is_inet_gw"`
|
||||
EndpointDetection bool `json:"endpoint_detection"`
|
||||
NameServers []string `json:"name_servers"`
|
||||
EgressWithDomains []EgressDomain `json:"egress_with_domains"`
|
||||
DnsNameservers []Nameserver `json:"dns_nameservers"`
|
||||
}
|
||||
|
||||
|
@@ -253,6 +253,7 @@ func sendPeers() {
|
||||
func SendDNSSyncByNetwork(network string) error {
|
||||
|
||||
k, err := logic.GetDNS(network)
|
||||
k = append(k, logic.EgressDNs(network)...)
|
||||
if err == nil && len(k) > 0 {
|
||||
err = PushSyncDNS(k)
|
||||
if err != nil {
|
||||
@@ -269,6 +270,7 @@ func sendDNSSync() error {
|
||||
if err == nil && len(networks) > 0 {
|
||||
for _, v := range networks {
|
||||
k, err := logic.GetDNS(v.NetID)
|
||||
k = append(k, logic.EgressDNs(v.NetID)...)
|
||||
if err == nil && len(k) > 0 {
|
||||
err = PushSyncDNS(k)
|
||||
if err != nil {
|
||||
|
@@ -1580,6 +1580,7 @@ func getUserRemoteAccessGwsV1(w http.ResponseWriter, r *http.Request) {
|
||||
gw.MatchDomains = append(gw.MatchDomains, nsI.MatchDomain)
|
||||
}
|
||||
}
|
||||
gw.MatchDomains = append(gw.MatchDomains, logic.GetEgressDomainsByAccess(user, models.NetworkID(node.Network))...)
|
||||
gws = append(gws, gw)
|
||||
userGws[node.Network] = gws
|
||||
delete(userGwNodes, node.ID.String())
|
||||
@@ -1630,6 +1631,7 @@ func getUserRemoteAccessGwsV1(w http.ResponseWriter, r *http.Request) {
|
||||
gw.MatchDomains = append(gw.MatchDomains, nsI.MatchDomain)
|
||||
}
|
||||
}
|
||||
gw.MatchDomains = append(gw.MatchDomains, logic.GetEgressDomainsByAccess(user, models.NetworkID(node.Network))...)
|
||||
gws = append(gws, gw)
|
||||
userGws[node.Network] = gws
|
||||
}
|
||||
|
@@ -32,6 +32,9 @@ func GetFwRulesForUserNodesOnGw(node models.Node, nodes []models.Node) (rules []
|
||||
defaultUserPolicy, _ := logic.GetDefaultPolicy(models.NetworkID(node.Network), models.UserPolicy)
|
||||
userNodes := getStaticUserNodesByNetwork(models.NetworkID(node.Network))
|
||||
for _, userNodeI := range userNodes {
|
||||
if !userNodeI.StaticNode.Enabled {
|
||||
continue
|
||||
}
|
||||
if defaultUserPolicy.Enabled {
|
||||
if userNodeI.StaticNode.Address != "" {
|
||||
rules = append(rules, models.FwRule{
|
||||
@@ -107,28 +110,56 @@ func GetFwRulesForUserNodesOnGw(node models.Node, nodes []models.Node) (rules []
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
dstI.Value = e.Range
|
||||
if e.Range != "" {
|
||||
dstI.Value = e.Range
|
||||
|
||||
ip, cidr, err := net.ParseCIDR(dstI.Value)
|
||||
if err == nil {
|
||||
if ip.To4() != nil && userNodeI.StaticNode.Address != "" {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: userNodeI.StaticNode.AddressIPNet4(),
|
||||
DstIP: *cidr,
|
||||
AllowedProtocol: policy.Proto,
|
||||
AllowedPorts: policy.Port,
|
||||
Allow: true,
|
||||
})
|
||||
} else if ip.To16() != nil && userNodeI.StaticNode.Address6 != "" {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: userNodeI.StaticNode.AddressIPNet6(),
|
||||
DstIP: *cidr,
|
||||
AllowedProtocol: policy.Proto,
|
||||
AllowedPorts: policy.Port,
|
||||
Allow: true,
|
||||
})
|
||||
ip, cidr, err := net.ParseCIDR(dstI.Value)
|
||||
if err == nil {
|
||||
if ip.To4() != nil && userNodeI.StaticNode.Address != "" {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: userNodeI.StaticNode.AddressIPNet4(),
|
||||
DstIP: *cidr,
|
||||
AllowedProtocol: policy.Proto,
|
||||
AllowedPorts: policy.Port,
|
||||
Allow: true,
|
||||
})
|
||||
} else if ip.To16() != nil && userNodeI.StaticNode.Address6 != "" {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: userNodeI.StaticNode.AddressIPNet6(),
|
||||
DstIP: *cidr,
|
||||
AllowedProtocol: policy.Proto,
|
||||
AllowedPorts: policy.Port,
|
||||
Allow: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if len(e.DomainAns) > 0 {
|
||||
for _, domainAns := range e.DomainAns {
|
||||
dstI.Value = domainAns
|
||||
|
||||
ip, cidr, err := net.ParseCIDR(dstI.Value)
|
||||
if err == nil {
|
||||
if ip.To4() != nil && userNodeI.StaticNode.Address != "" {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: userNodeI.StaticNode.AddressIPNet4(),
|
||||
DstIP: *cidr,
|
||||
AllowedProtocol: policy.Proto,
|
||||
AllowedPorts: policy.Port,
|
||||
Allow: true,
|
||||
})
|
||||
} else if ip.To16() != nil && userNodeI.StaticNode.Address6 != "" {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: userNodeI.StaticNode.AddressIPNet6(),
|
||||
DstIP: *cidr,
|
||||
AllowedProtocol: policy.Proto,
|
||||
AllowedPorts: policy.Port,
|
||||
Allow: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,39 +307,78 @@ func GetFwRulesForNodeAndPeerOnGw(node, peer models.Node, allowedPolicies []mode
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
dstI.Value = e.Range
|
||||
if e.Range != "" {
|
||||
dstI.Value = e.Range
|
||||
|
||||
ip, cidr, err := net.ParseCIDR(dstI.Value)
|
||||
if err == nil {
|
||||
if ip.To4() != nil {
|
||||
if node.Address.IP != nil {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: net.IPNet{
|
||||
IP: node.Address.IP,
|
||||
Mask: net.CIDRMask(32, 32),
|
||||
},
|
||||
DstIP: *cidr,
|
||||
AllowedProtocol: policy.Proto,
|
||||
AllowedPorts: policy.Port,
|
||||
Allow: true,
|
||||
})
|
||||
ip, cidr, err := net.ParseCIDR(dstI.Value)
|
||||
if err == nil {
|
||||
if ip.To4() != nil {
|
||||
if node.Address.IP != nil {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: net.IPNet{
|
||||
IP: node.Address.IP,
|
||||
Mask: net.CIDRMask(32, 32),
|
||||
},
|
||||
DstIP: *cidr,
|
||||
AllowedProtocol: policy.Proto,
|
||||
AllowedPorts: policy.Port,
|
||||
Allow: true,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if node.Address6.IP != nil {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: net.IPNet{
|
||||
IP: node.Address6.IP,
|
||||
Mask: net.CIDRMask(128, 128),
|
||||
},
|
||||
DstIP: *cidr,
|
||||
AllowedProtocol: policy.Proto,
|
||||
AllowedPorts: policy.Port,
|
||||
Allow: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if node.Address6.IP != nil {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: net.IPNet{
|
||||
IP: node.Address6.IP,
|
||||
Mask: net.CIDRMask(128, 128),
|
||||
},
|
||||
DstIP: *cidr,
|
||||
AllowedProtocol: policy.Proto,
|
||||
AllowedPorts: policy.Port,
|
||||
Allow: true,
|
||||
})
|
||||
|
||||
}
|
||||
} else if len(e.DomainAns) > 0 {
|
||||
for _, domainAnsI := range e.DomainAns {
|
||||
dstI.Value = domainAnsI
|
||||
|
||||
ip, cidr, err := net.ParseCIDR(dstI.Value)
|
||||
if err == nil {
|
||||
if ip.To4() != nil {
|
||||
if node.Address.IP != nil {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: net.IPNet{
|
||||
IP: node.Address.IP,
|
||||
Mask: net.CIDRMask(32, 32),
|
||||
},
|
||||
DstIP: *cidr,
|
||||
AllowedProtocol: policy.Proto,
|
||||
AllowedPorts: policy.Port,
|
||||
Allow: true,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if node.Address6.IP != nil {
|
||||
rules = append(rules, models.FwRule{
|
||||
SrcIP: net.IPNet{
|
||||
IP: node.Address6.IP,
|
||||
Mask: net.CIDRMask(128, 128),
|
||||
},
|
||||
DstIP: *cidr,
|
||||
AllowedProtocol: policy.Proto,
|
||||
AllowedPorts: policy.Port,
|
||||
Allow: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -800,7 +870,14 @@ func GetEgressUserRulesForNode(targetnode *models.Node,
|
||||
continue
|
||||
}
|
||||
if _, ok := egI.Nodes[targetnode.ID.String()]; ok {
|
||||
targetNodeTags[models.TagID(egI.Range)] = struct{}{}
|
||||
if egI.Range != "" {
|
||||
targetNodeTags[models.TagID(egI.Range)] = struct{}{}
|
||||
} else if len(egI.DomainAns) > 0 {
|
||||
for _, domainAnsI := range egI.DomainAns {
|
||||
targetNodeTags[models.TagID(domainAnsI)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
targetNodeTags[models.TagID(egI.ID)] = struct{}{}
|
||||
}
|
||||
}
|
||||
@@ -818,7 +895,14 @@ func GetEgressUserRulesForNode(targetnode *models.Node,
|
||||
for nodeID := range e.Nodes {
|
||||
dstTags[nodeID] = struct{}{}
|
||||
}
|
||||
dstTags[e.Range] = struct{}{}
|
||||
if e.Range != "" {
|
||||
dstTags[e.Range] = struct{}{}
|
||||
} else if len(e.DomainAns) > 0 {
|
||||
for _, domainAnsI := range e.DomainAns {
|
||||
dstTags[domainAnsI] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -912,24 +996,57 @@ func GetEgressUserRulesForNode(targetnode *models.Node,
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ip, cidr, err := net.ParseCIDR(e.Range)
|
||||
if err == nil {
|
||||
if ip.To4() != nil {
|
||||
r.Dst = append(r.Dst, *cidr)
|
||||
} else {
|
||||
r.Dst6 = append(r.Dst6, *cidr)
|
||||
}
|
||||
if e.Range != "" {
|
||||
ip, cidr, err := net.ParseCIDR(e.Range)
|
||||
if err == nil {
|
||||
if ip.To4() != nil {
|
||||
r.Dst = append(r.Dst, *cidr)
|
||||
} else {
|
||||
r.Dst6 = append(r.Dst6, *cidr)
|
||||
}
|
||||
|
||||
}
|
||||
} else if len(e.DomainAns) > 0 {
|
||||
for _, domainAnsI := range e.DomainAns {
|
||||
ip, cidr, err := net.ParseCIDR(domainAnsI)
|
||||
if err == nil {
|
||||
if ip.To4() != nil {
|
||||
r.Dst = append(r.Dst, *cidr)
|
||||
} else {
|
||||
r.Dst6 = append(r.Dst6, *cidr)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if userNode.StaticNode.Address6 != "" {
|
||||
r.IP6List = append(r.IP6List, userNode.StaticNode.AddressIPNet6())
|
||||
}
|
||||
if aclRule, ok := rules[acl.ID]; ok {
|
||||
|
||||
aclRule.IPList = append(aclRule.IPList, r.IPList...)
|
||||
aclRule.IP6List = append(aclRule.IP6List, r.IP6List...)
|
||||
|
||||
aclRule.Dst = append(aclRule.Dst, r.Dst...)
|
||||
aclRule.Dst6 = append(aclRule.Dst6, r.Dst6...)
|
||||
|
||||
aclRule.IPList = logic.UniqueIPNetList(aclRule.IPList)
|
||||
aclRule.IP6List = logic.UniqueIPNetList(aclRule.IP6List)
|
||||
|
||||
aclRule.Dst = logic.UniqueIPNetList(aclRule.Dst)
|
||||
aclRule.Dst6 = logic.UniqueIPNetList(aclRule.Dst6)
|
||||
|
||||
rules[acl.ID] = aclRule
|
||||
} else {
|
||||
r.IPList = logic.UniqueIPNetList(r.IPList)
|
||||
r.IP6List = logic.UniqueIPNetList(r.IP6List)
|
||||
|
||||
r.Dst = logic.UniqueIPNetList(r.Dst)
|
||||
r.Dst6 = logic.UniqueIPNetList(r.Dst6)
|
||||
rules[acl.ID] = r
|
||||
}
|
||||
}
|
||||
@@ -1064,7 +1181,19 @@ func GetUserAclRulesForNode(targetnode *models.Node,
|
||||
egressRanges6 = append(egressRanges6, *cidr)
|
||||
}
|
||||
}
|
||||
} else if len(eI.DomainAns) > 0 {
|
||||
for _, domainAnsI := range eI.DomainAns {
|
||||
_, cidr, err := net.ParseCIDR(domainAnsI)
|
||||
if err == nil {
|
||||
if cidr.IP.To4() != nil {
|
||||
egressRanges4 = append(egressRanges4, *cidr)
|
||||
} else {
|
||||
egressRanges6 = append(egressRanges6, *cidr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
break
|
||||
@@ -1083,6 +1212,17 @@ func GetUserAclRulesForNode(targetnode *models.Node,
|
||||
egressRanges6 = append(egressRanges6, *cidr)
|
||||
}
|
||||
}
|
||||
} else if len(e.DomainAns) > 0 {
|
||||
for _, domainAnsI := range e.DomainAns {
|
||||
_, cidr, err := net.ParseCIDR(domainAnsI)
|
||||
if err == nil {
|
||||
if cidr.IP.To4() != nil {
|
||||
egressRanges4 = append(egressRanges4, *cidr)
|
||||
} else {
|
||||
egressRanges6 = append(egressRanges6, *cidr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -11,14 +11,16 @@ import (
|
||||
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"`
|
||||
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"`
|
||||
DomainAns datatypes.JSONSlice[string] `gorm:"domain_ans" json:"domain_ans"`
|
||||
Domain string `gorm:"domain" json:"domain"`
|
||||
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"`
|
||||
|
@@ -5,8 +5,11 @@ import (
|
||||
"log/slog"
|
||||
"net"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gravitl/netmaker/models"
|
||||
)
|
||||
|
||||
// RetryStrategy specifies a strategy to retry an operation after waiting a while,
|
||||
@@ -59,8 +62,8 @@ func TraceCaller() {
|
||||
funcName := runtime.FuncForPC(pc).Name()
|
||||
|
||||
// Print trace details
|
||||
slog.Debug("Called from function: %s\n", "func-name", funcName)
|
||||
slog.Debug("File: %s, Line: %d\n", "file", file, "line-no", line)
|
||||
slog.Debug("Called from function: %s\n", "func", funcName)
|
||||
slog.Debug("File: %s, Line: %d\n", "file", file, "line", line)
|
||||
}
|
||||
|
||||
// NoEmptyStringToCsv takes a bunch of strings, filters out empty ones and returns a csv version of the string
|
||||
@@ -86,3 +89,54 @@ func GetExtClientEndpoint(hostIpv4Endpoint, hostIpv6Endpoint net.IP, hostListenP
|
||||
return fmt.Sprintf("%s:%d", hostIpv4Endpoint.String(), hostListenPort)
|
||||
}
|
||||
}
|
||||
|
||||
// SortIfacesByName sorts a slice of Iface by name in ascending order
|
||||
func SortIfacesByName(ifaces []models.Iface) {
|
||||
sort.Slice(ifaces, func(i, j int) bool {
|
||||
return ifaces[i].Name < ifaces[j].Name
|
||||
})
|
||||
}
|
||||
|
||||
// CompareIfaces compares two slices of Iface and returns true if they are equal
|
||||
// Two slices are considered equal if they have the same length and all corresponding
|
||||
// elements have the same Name, AddressString, and IP address
|
||||
func CompareIfaces(ifaces1, ifaces2 []models.Iface) bool {
|
||||
// Check if lengths are different
|
||||
if len(ifaces1) != len(ifaces2) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Compare each element
|
||||
for i := range ifaces1 {
|
||||
if !CompareIface(ifaces1[i], ifaces2[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// CompareIface compares two individual Iface structs and returns true if they are equal
|
||||
func CompareIface(iface1, iface2 models.Iface) bool {
|
||||
// Compare Name
|
||||
if iface1.Name != iface2.Name {
|
||||
return false
|
||||
}
|
||||
|
||||
// Compare AddressString
|
||||
if iface1.AddressString != iface2.AddressString {
|
||||
return false
|
||||
}
|
||||
|
||||
// Compare IP addresses
|
||||
if !iface1.Address.IP.Equal(iface2.Address.IP) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Compare network masks
|
||||
if iface1.Address.Mask.String() != iface2.Address.Mask.String() {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
Reference in New Issue
Block a user