NM-9: User All resources Policy and relayed node acl Fix (#3592)

* user policies fix

* fix user acl rules for all resources tag

* handle relayed comms via gateway with active acl policies

* fix static node comms to all resources
This commit is contained in:
Abhishek K 2025-08-11 14:32:26 +05:30 committed by GitHub
parent b972e7a969
commit 32657dde82
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 375 additions and 187 deletions

View file

@ -50,6 +50,27 @@ func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) {
// if nodeI.StaticNode.IngressGatewayID != node.ID.String() {
// continue
// }
if IsNodeAllowedToCommunicateWithAllRsrcs(nodeI) {
if nodeI.Address.IP != nil {
rules = append(rules, models.FwRule{
SrcIP: net.IPNet{
IP: nodeI.Address.IP,
Mask: net.CIDRMask(32, 32),
},
Allow: true,
})
}
if nodeI.Address6.IP != nil {
rules = append(rules, models.FwRule{
SrcIP: net.IPNet{
IP: nodeI.Address6.IP,
Mask: net.CIDRMask(128, 128),
},
Allow: true,
})
}
continue
}
for _, peer := range nodes {
if peer.StaticNode.ClientID == nodeI.StaticNode.ClientID || peer.IsUserNode {
continue
@ -74,6 +95,37 @@ func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) {
}
}
}
if len(node.RelayedNodes) > 0 {
for _, relayedNodeID := range node.RelayedNodes {
relayedNode, err := GetNodeByID(relayedNodeID)
if err != nil {
continue
}
if relayedNode.Address.IP != nil {
relayedFwRule := models.FwRule{
AllowedProtocol: models.ALL,
AllowedPorts: []string{},
Allow: true,
}
relayedFwRule.DstIP = relayedNode.AddressIPNet4()
relayedFwRule.SrcIP = node.NetworkRange
rules = append(rules, relayedFwRule)
}
if relayedNode.Address6.IP != nil {
relayedFwRule := models.FwRule{
AllowedProtocol: models.ALL,
AllowedPorts: []string{},
Allow: true,
}
relayedFwRule.DstIP = relayedNode.AddressIPNet6()
relayedFwRule.SrcIP = node.NetworkRange6
rules = append(rules, relayedFwRule)
}
}
}
return
}
@ -851,6 +903,60 @@ func MigrateAclPolicies() {
}
func IsNodeAllowedToCommunicateWithAllRsrcs(node models.Node) bool {
// check default policy if all allowed return true
defaultPolicy, err := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy)
if err == nil {
if defaultPolicy.Enabled {
return true
}
}
var nodeId string
if node.IsStatic {
nodeId = node.StaticNode.ClientID
node = node.StaticNode.ConvertToStaticNode()
} else {
nodeId = node.ID.String()
}
nodeTags := make(map[models.TagID]struct{})
nodeTags[models.TagID(nodeId)] = struct{}{}
if node.IsGw {
nodeTags[models.TagID(fmt.Sprintf("%s.%s", node.Network, models.GwTagName))] = struct{}{}
}
// list device policies
policies := ListDevicePolicies(models.NetworkID(node.Network))
srcMap := make(map[string]struct{})
dstMap := make(map[string]struct{})
defer func() {
srcMap = nil
dstMap = nil
}()
for _, policy := range policies {
if !policy.Enabled {
continue
}
srcMap = ConvAclTagToValueMap(policy.Src)
dstMap = ConvAclTagToValueMap(policy.Dst)
_, srcAll := srcMap["*"]
_, dstAll := dstMap["*"]
for tagID := range nodeTags {
if srcAll {
if _, ok := dstMap[tagID.String()]; ok {
return true
}
}
if dstAll {
if _, ok := srcMap[tagID.String()]; ok {
return true
}
}
}
}
return false
}
// IsNodeAllowedToCommunicate - check node is allowed to communicate with the peer // ADD ALLOWED DIRECTION - 0 => node -> peer, 1 => peer-> node,
func isNodeAllowedToCommunicate(node, peer models.Node, checkDefaultPolicy bool) (bool, []models.Acl) {
var nodeId, peerId string

View file

@ -729,12 +729,7 @@ func GetStaticNodesByNetwork(network models.NetworkID, onlyWg bool) (staticNode
if onlyWg && extI.RemoteAccessClientID != "" {
continue
}
n := models.Node{
IsStatic: true,
StaticNode: extI,
IsUserNode: extI.RemoteAccessClientID != "",
}
staticNode = append(staticNode, n)
staticNode = append(staticNode, extI.ConvertToStaticNode())
}
}

View file

@ -17,6 +17,27 @@ func GetFwRulesForUserNodesOnGw(node models.Node, nodes []models.Node) (rules []
defaultUserPolicy, _ := logic.GetDefaultPolicy(models.NetworkID(node.Network), models.UserPolicy)
userNodes := logic.GetStaticUserNodesByNetwork(models.NetworkID(node.Network))
for _, userNodeI := range userNodes {
if defaultUserPolicy.Enabled {
if userNodeI.StaticNode.Address != "" {
rules = append(rules, models.FwRule{
SrcIP: userNodeI.StaticNode.AddressIPNet4(),
DstIP: net.IPNet{},
AllowedProtocol: models.ALL,
AllowedPorts: []string{},
Allow: true,
})
}
if userNodeI.StaticNode.Address6 != "" {
rules = append(rules, models.FwRule{
SrcIP: userNodeI.StaticNode.AddressIPNet6(),
DstIP: net.IPNet{},
AllowedProtocol: models.ALL,
AllowedPorts: []string{},
Allow: true,
})
}
continue
}
for _, peer := range nodes {
if peer.IsUserNode {
continue
@ -26,68 +47,76 @@ func GetFwRulesForUserNodesOnGw(node models.Node, nodes []models.Node) (rules []
if peer.IsStatic {
peer = peer.StaticNode.ConvertToStaticNode()
}
if !defaultUserPolicy.Enabled {
for _, policy := range allowedPolicies {
if userNodeI.StaticNode.Address != "" {
for _, policy := range allowedPolicies {
if userNodeI.StaticNode.Address != "" {
rules = append(rules, models.FwRule{
SrcIP: userNodeI.StaticNode.AddressIPNet4(),
DstIP: net.IPNet{
IP: peer.Address.IP,
Mask: net.CIDRMask(32, 32),
},
AllowedProtocol: policy.Proto,
AllowedPorts: policy.Port,
Allow: true,
})
}
if userNodeI.StaticNode.Address6 != "" {
rules = append(rules, models.FwRule{
SrcIP: userNodeI.StaticNode.AddressIPNet6(),
DstIP: net.IPNet{
IP: peer.Address6.IP,
Mask: net.CIDRMask(128, 128),
},
AllowedProtocol: policy.Proto,
AllowedPorts: policy.Port,
Allow: true,
})
}
// add egress ranges
for _, dstI := range policy.Dst {
if dstI.Value == "*" {
rules = append(rules, models.FwRule{
SrcIP: userNodeI.StaticNode.AddressIPNet4(),
DstIP: net.IPNet{
IP: peer.Address.IP,
Mask: net.CIDRMask(32, 32),
},
AllowedProtocol: policy.Proto,
AllowedPorts: policy.Port,
Allow: true,
})
}
if userNodeI.StaticNode.Address6 != "" {
rules = append(rules, models.FwRule{
SrcIP: userNodeI.StaticNode.AddressIPNet6(),
DstIP: net.IPNet{
IP: peer.Address6.IP,
Mask: net.CIDRMask(128, 128),
},
SrcIP: userNodeI.StaticNode.AddressIPNet4(),
DstIP: net.IPNet{},
AllowedProtocol: policy.Proto,
AllowedPorts: policy.Port,
Allow: true,
})
break
}
if dstI.ID == models.EgressID {
// add egress ranges
for _, dstI := range policy.Dst {
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
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)
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,
})
}
}
}
}
}
}
@ -922,6 +951,8 @@ func getEgressUserRulesForNode(targetnode *models.Node,
if len(egs) == 0 {
return rules
}
defaultPolicy, _ := logic.GetDefaultPolicy(models.NetworkID(targetnode.Network), models.UserPolicy)
for _, egI := range egs {
if !egI.Status {
continue
@ -931,74 +962,69 @@ func getEgressUserRulesForNode(targetnode *models.Node,
targetNodeTags[models.TagID(egI.ID)] = struct{}{}
}
}
for _, acl := range acls {
if !acl.Enabled {
continue
}
dstTags := logic.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{}{}
if !defaultPolicy.Enabled {
for _, acl := range acls {
if !acl.Enabled {
continue
}
dstTags := logic.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{}{}
}
dstTags[e.Range] = struct{}{}
}
}
}
_, all := dstTags["*"]
addUsers := false
if !all {
for nodeTag := range targetNodeTags {
if _, ok := dstTags[nodeTag.String()]; ok {
addUsers = true
break
_, all := dstTags["*"]
addUsers := false
if !all {
for nodeTag := range targetNodeTags {
if _, ok := dstTags[nodeTag.String()]; ok {
addUsers = true
break
}
}
} else {
addUsers = true
}
} else {
addUsers = true
}
if addUsers {
// get all src tags
for _, srcAcl := range acl.Src {
if srcAcl.ID == models.UserAclID {
allowedUsers[srcAcl.Value] = append(allowedUsers[srcAcl.Value], acl)
} else if srcAcl.ID == models.UserGroupAclID {
// fetch all users in the group
if usersMap, ok := userGrpMap[models.UserGroupID(srcAcl.Value)]; ok {
for userName := range usersMap {
allowedUsers[userName] = append(allowedUsers[userName], acl)
if addUsers {
// get all src tags
for _, srcAcl := range acl.Src {
if srcAcl.ID == models.UserAclID {
allowedUsers[srcAcl.Value] = append(allowedUsers[srcAcl.Value], acl)
} else if srcAcl.ID == models.UserGroupAclID {
// fetch all users in the group
if usersMap, ok := userGrpMap[models.UserGroupID(srcAcl.Value)]; ok {
for userName := range usersMap {
allowedUsers[userName] = append(allowedUsers[userName], acl)
}
}
}
}
}
}
}
}
for _, userNode := range userNodes {
if !userNode.StaticNode.Enabled {
continue
if defaultPolicy.Enabled {
r := models.AclRule{
ID: defaultPolicy.ID,
AllowedProtocol: defaultPolicy.Proto,
AllowedPorts: defaultPolicy.Port,
Direction: defaultPolicy.AllowedDirection,
Allowed: true,
}
acls, ok := allowedUsers[userNode.StaticNode.OwnerID]
if !ok {
continue
}
for _, acl := range acls {
if !acl.Enabled {
for _, userNode := range userNodes {
if !userNode.StaticNode.Enabled {
continue
}
r := models.AclRule{
ID: acl.ID,
AllowedProtocol: acl.Proto,
AllowedPorts: acl.Port,
Direction: acl.AllowedDirection,
Allowed: true,
}
// Get peers in the tags and add allowed rules
if userNode.StaticNode.Address != "" {
r.IPList = append(r.IPList, userNode.StaticNode.AddressIPNet4())
@ -1006,36 +1032,70 @@ func getEgressUserRulesForNode(targetnode *models.Node,
if userNode.StaticNode.Address6 != "" {
r.IP6List = append(r.IP6List, userNode.StaticNode.AddressIPNet6())
}
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 {
continue
}
}
rules[defaultPolicy.ID] = r
} else {
for _, userNode := range userNodes {
if !userNode.StaticNode.Enabled {
continue
}
acls, ok := allowedUsers[userNode.StaticNode.OwnerID]
if !ok {
continue
}
for _, acl := range acls {
if !acl.Enabled {
continue
}
r := models.AclRule{
ID: acl.ID,
AllowedProtocol: acl.Proto,
AllowedPorts: acl.Port,
Direction: acl.AllowedDirection,
Allowed: true,
}
// Get peers in the tags and add allowed rules
if userNode.StaticNode.Address != "" {
r.IPList = append(r.IPList, userNode.StaticNode.AddressIPNet4())
}
if userNode.StaticNode.Address6 != "" {
r.IP6List = append(r.IP6List, userNode.StaticNode.AddressIPNet6())
}
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 {
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)
}
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 aclRule, ok := rules[acl.ID]; ok {
aclRule.IPList = append(aclRule.IPList, r.IPList...)
aclRule.IP6List = append(aclRule.IP6List, r.IP6List...)
rules[acl.ID] = aclRule
} else {
rules[acl.ID] = r
}
}
}
if aclRule, ok := rules[acl.ID]; ok {
aclRule.IPList = append(aclRule.IPList, r.IPList...)
aclRule.IP6List = append(aclRule.IP6List, r.IP6List...)
rules[acl.ID] = aclRule
} else {
rules[acl.ID] = r
}
}
}
return rules
}
@ -1056,63 +1116,58 @@ func getUserAclRulesForNode(targetnode *models.Node,
if targetNodeTags == nil {
targetNodeTags = make(map[models.TagID]struct{})
}
defaultPolicy, _ := logic.GetDefaultPolicy(models.NetworkID(targetnode.Network), models.UserPolicy)
targetNodeTags[models.TagID(targetnode.ID.String())] = struct{}{}
for _, acl := range acls {
if !acl.Enabled {
continue
}
dstTags := logic.ConvAclTagToValueMap(acl.Dst)
_, all := dstTags["*"]
addUsers := false
if !all {
for nodeTag := range targetNodeTags {
if _, ok := dstTags[nodeTag.String()]; ok {
addUsers = true
break
}
if !defaultPolicy.Enabled {
for _, acl := range acls {
if !acl.Enabled {
continue
}
dstTags := logic.ConvAclTagToValueMap(acl.Dst)
_, all := dstTags["*"]
addUsers := false
if !all {
for nodeTag := range targetNodeTags {
if _, ok := dstTags[nodeTag.String()]; ok {
addUsers = true
break
}
}
} else {
addUsers = true
}
} else {
addUsers = true
}
if addUsers {
// get all src tags
for _, srcAcl := range acl.Src {
if srcAcl.ID == models.UserAclID {
allowedUsers[srcAcl.Value] = append(allowedUsers[srcAcl.Value], acl)
} else if srcAcl.ID == models.UserGroupAclID {
// fetch all users in the group
if usersMap, ok := userGrpMap[models.UserGroupID(srcAcl.Value)]; ok {
for userName := range usersMap {
allowedUsers[userName] = append(allowedUsers[userName], acl)
if addUsers {
// get all src tags
for _, srcAcl := range acl.Src {
if srcAcl.ID == models.UserAclID {
allowedUsers[srcAcl.Value] = append(allowedUsers[srcAcl.Value], acl)
} else if srcAcl.ID == models.UserGroupAclID {
// fetch all users in the group
if usersMap, ok := userGrpMap[models.UserGroupID(srcAcl.Value)]; ok {
for userName := range usersMap {
allowedUsers[userName] = append(allowedUsers[userName], acl)
}
}
}
}
}
}
}
}
for _, userNode := range userNodes {
if !userNode.StaticNode.Enabled {
continue
if defaultPolicy.Enabled {
r := models.AclRule{
ID: defaultPolicy.ID,
AllowedProtocol: defaultPolicy.Proto,
AllowedPorts: defaultPolicy.Port,
Direction: defaultPolicy.AllowedDirection,
Allowed: true,
}
acls, ok := allowedUsers[userNode.StaticNode.OwnerID]
if !ok {
continue
}
for _, acl := range acls {
if !acl.Enabled {
for _, userNode := range userNodes {
if !userNode.StaticNode.Enabled {
continue
}
r := models.AclRule{
ID: acl.ID,
AllowedProtocol: acl.Proto,
AllowedPorts: acl.Port,
Direction: acl.AllowedDirection,
Allowed: true,
}
// Get peers in the tags and add allowed rules
if userNode.StaticNode.Address != "" {
r.IPList = append(r.IPList, userNode.StaticNode.AddressIPNet4())
@ -1120,16 +1175,47 @@ func getUserAclRulesForNode(targetnode *models.Node,
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.IPList = logic.UniqueIPNetList(aclRule.IPList)
aclRule.IP6List = logic.UniqueIPNetList(aclRule.IP6List)
rules[acl.ID] = aclRule
} else {
r.IPList = logic.UniqueIPNetList(r.IPList)
r.IP6List = logic.UniqueIPNetList(r.IP6List)
rules[acl.ID] = r
}
rules[defaultPolicy.ID] = r
} else {
for _, userNode := range userNodes {
if !userNode.StaticNode.Enabled {
continue
}
acls, ok := allowedUsers[userNode.StaticNode.OwnerID]
if !ok {
continue
}
for _, acl := range acls {
if !acl.Enabled {
continue
}
r := models.AclRule{
ID: acl.ID,
AllowedProtocol: acl.Proto,
AllowedPorts: acl.Port,
Direction: acl.AllowedDirection,
Allowed: true,
}
// Get peers in the tags and add allowed rules
if userNode.StaticNode.Address != "" {
r.IPList = append(r.IPList, userNode.StaticNode.AddressIPNet4())
}
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.IPList = logic.UniqueIPNetList(aclRule.IPList)
aclRule.IP6List = logic.UniqueIPNetList(aclRule.IP6List)
rules[acl.ID] = aclRule
} else {
r.IPList = logic.UniqueIPNetList(r.IPList)
r.IP6List = logic.UniqueIPNetList(r.IP6List)
rules[acl.ID] = r
}
}
}
}
@ -1228,9 +1314,9 @@ func CheckIfAnyPolicyisUniDirectional(targetNode models.Node, acls []models.Acl)
func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRule) {
targetnode := *targetnodeI
defer func() {
if !targetnode.IsIngressGateway {
rules = getUserAclRulesForNode(&targetnode, rules)
}
//if !targetnode.IsIngressGateway {
rules = getUserAclRulesForNode(&targetnode, rules)
//}
}()
rules = make(map[string]models.AclRule)
var taggedNodes map[models.TagID][]models.Node

View file

@ -5,6 +5,7 @@ package pro
import (
"encoding/base64"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/logic"