diff --git a/controllers/acls.go b/controllers/acls.go index 1bc2c61c..6873f2a0 100644 --- a/controllers/acls.go +++ b/controllers/acls.go @@ -52,6 +52,73 @@ func aclPolicyTypes(w http.ResponseWriter, r *http.Request) { // models.NetmakerIPAclID, // models.NetmakerSubNetRangeAClID, }, + ProtocolTypes: []models.ProtocolType{ + { + Name: models.Http, + AllowedProtocols: []models.Protocol{ + models.TCP, + }, + PortRange: "80", + }, + { + Name: models.Https, + AllowedProtocols: []models.Protocol{ + models.TCP, + }, + PortRange: "443", + }, + // { + // Name: "MySQL", + // AllowedProtocols: []models.Protocol{ + // models.TCP, + // }, + // PortRange: "3306", + // }, + // { + // Name: "DNS TCP", + // AllowedProtocols: []models.Protocol{ + // models.TCP, + // }, + // PortRange: "53", + // }, + // { + // Name: "DNS UDP", + // AllowedProtocols: []models.Protocol{ + // models.UDP, + // }, + // PortRange: "53", + // }, + { + Name: models.AllTCP, + AllowedProtocols: []models.Protocol{ + models.TCP, + }, + PortRange: "All ports", + }, + { + Name: models.AllUDP, + AllowedProtocols: []models.Protocol{ + models.UDP, + }, + PortRange: "All ports", + }, + { + Name: models.ICMPService, + AllowedProtocols: []models.Protocol{ + models.ICMP, + }, + PortRange: "", + }, + { + Name: models.Custom, + AllowedProtocols: []models.Protocol{ + models.UDP, + models.TCP, + }, + PortRange: "All ports", + AllowPortSetting: true, + }, + }, } logic.ReturnSuccessResponseWithJson(w, r, resp, "fetched acls types") } @@ -69,7 +136,7 @@ func aclDebug(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } - allowed := logic.IsNodeAllowedToCommunicate(node, peer, true) + allowed, _ := logic.IsNodeAllowedToCommunicate(node, peer, true) logic.ReturnSuccessResponseWithJson(w, r, allowed, "fetched all acls in the network ") } @@ -132,11 +199,6 @@ func createAcl(w http.ResponseWriter, r *http.Request) { acl.CreatedBy = user.UserName acl.CreatedAt = time.Now().UTC() acl.Default = false - if acl.RuleType == models.DevicePolicy { - acl.AllowedDirection = models.TrafficDirectionBi - } else { - acl.AllowedDirection = models.TrafficDirectionUni - } // validate create acl policy if !logic.IsAclPolicyValid(acl) { logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid policy"), "badrequest")) @@ -152,7 +214,7 @@ func createAcl(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } - go mq.PublishPeerUpdate(false) + go mq.PublishPeerUpdate(true) logic.ReturnSuccessResponseWithJson(w, r, acl, "created acl successfully") } @@ -194,7 +256,7 @@ func updateAcl(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } - go mq.PublishPeerUpdate(false) + go mq.PublishPeerUpdate(true) logic.ReturnSuccessResponse(w, r, "updated acl "+acl.Name) } @@ -225,6 +287,6 @@ func deleteAcl(w http.ResponseWriter, r *http.Request) { logic.FormatError(errors.New("cannot delete default policy"), "internal")) return } - go mq.PublishPeerUpdate(false) + go mq.PublishPeerUpdate(true) logic.ReturnSuccessResponse(w, r, "deleted acl "+acl.Name) } diff --git a/logic/acls.go b/logic/acls.go index 97b0f741..ce9b230f 100644 --- a/logic/acls.go +++ b/logic/acls.go @@ -18,6 +18,39 @@ var ( aclCacheMap = make(map[string]models.Acl) ) +func MigrateDefaulAclPolicies(netID models.NetworkID) { + if netID.String() == "" { + return + } + acl, err := GetAcl(fmt.Sprintf("%s.%s", netID, "all-nodes")) + if err == nil { + //if acl.Proto.String() == "" { + acl.Proto = models.ALL + acl.ServiceType = models.Custom + acl.Port = []string{} + UpsertAcl(acl) + //} + } + acl, err = GetAcl(fmt.Sprintf("%s.%s", netID, "all-users")) + if err == nil { + //if acl.Proto.String() == "" { + acl.Proto = models.ALL + acl.ServiceType = models.Custom + acl.Port = []string{} + UpsertAcl(acl) + //} + } + acl, err = GetAcl(fmt.Sprintf("%s.%s", netID, "all-remote-access-gws")) + if err == nil { + //if acl.Proto.String() == "" { + acl.Proto = models.ALL + acl.ServiceType = models.Custom + acl.Port = []string{} + UpsertAcl(acl) + //} + } +} + // CreateDefaultAclNetworkPolicies - create default acl network policies func CreateDefaultAclNetworkPolicies(netID models.NetworkID) { if netID.String() == "" { @@ -31,6 +64,8 @@ func CreateDefaultAclNetworkPolicies(netID models.NetworkID) { MetaData: "This Policy allows all nodes in the network to communicate with each other", Default: true, NetworkID: netID, + Proto: models.ALL, + Port: []string{}, RuleType: models.DevicePolicy, Src: []models.AclPolicyTag{ { @@ -56,6 +91,8 @@ func CreateDefaultAclNetworkPolicies(netID models.NetworkID) { Name: "All Users", MetaData: "This policy gives access to everything in the network for an user", NetworkID: netID, + Proto: models.ALL, + Port: []string{}, RuleType: models.UserPolicy, Src: []models.AclPolicyTag{ { @@ -81,6 +118,8 @@ func CreateDefaultAclNetworkPolicies(netID models.NetworkID) { Default: true, Name: "All Remote Access Gateways", NetworkID: netID, + Proto: models.ALL, + Port: []string{}, RuleType: models.DevicePolicy, Src: []models.AclPolicyTag{ { @@ -202,7 +241,10 @@ func IsAclExists(aclID string) bool { // IsAclPolicyValid - validates if acl policy is valid func IsAclPolicyValid(acl models.Acl) bool { //check if src and dst are valid - + if acl.AllowedDirection != models.TrafficDirectionBi && + acl.AllowedDirection != models.TrafficDirectionUni { + return false + } switch acl.RuleType { case models.UserPolicy: // src list should only contain users @@ -298,6 +340,10 @@ func UpdateAcl(newAcl, acl models.Acl) error { acl.Name = newAcl.Name acl.Src = newAcl.Src acl.Dst = newAcl.Dst + acl.AllowedDirection = newAcl.AllowedDirection + acl.Port = newAcl.Port + acl.Proto = newAcl.Proto + acl.ServiceType = newAcl.ServiceType } acl.Enabled = newAcl.Enabled d, err := json.Marshal(acl) @@ -382,7 +428,6 @@ func ListAcls() (acls []models.Acl) { if err != nil && !database.IsEmptyRecord(err) { return []models.Acl{} } - for _, dataI := range data { acl := models.Acl{} err := json.Unmarshal([]byte(dataI), &acl) @@ -463,6 +508,18 @@ func listDevicePolicies(netID models.NetworkID) []models.Acl { return deviceAcls } +// listUserPolicies - lists all user policies in a network +func listUserPolicies(netID models.NetworkID) []models.Acl { + allAcls := ListAcls() + deviceAcls := []models.Acl{} + for _, acl := range allAcls { + if acl.NetworkID == netID && acl.RuleType == models.UserPolicy { + deviceAcls = append(deviceAcls, acl) + } + } + return deviceAcls +} + // ListAcls - lists all acl policies func ListAclsByNetwork(netID models.NetworkID) ([]models.Acl, error) { @@ -485,19 +542,19 @@ func convAclTagToValueMap(acltags []models.AclPolicyTag) map[string]struct{} { } // IsUserAllowedToCommunicate - check if user is allowed to communicate with peer -func IsUserAllowedToCommunicate(userName string, peer models.Node) bool { +func IsUserAllowedToCommunicate(userName string, peer models.Node) (bool, []models.Acl) { if peer.IsStatic { peer = peer.StaticNode.ConvertToStaticNode() } acl, _ := GetDefaultPolicy(models.NetworkID(peer.Network), models.UserPolicy) if acl.Enabled { - return true + return true, []models.Acl{acl} } user, err := GetUser(userName) if err != nil { - return false + return false, []models.Acl{} } - + allowedPolicies := []models.Acl{} policies := listPoliciesOfUser(*user, models.NetworkID(peer.Network)) for _, policy := range policies { if !policy.Enabled { @@ -505,20 +562,25 @@ func IsUserAllowedToCommunicate(userName string, peer models.Node) bool { } dstMap := convAclTagToValueMap(policy.Dst) if _, ok := dstMap["*"]; ok { - return true + allowedPolicies = append(allowedPolicies, policy) + continue } for tagID := range peer.Tags { if _, ok := dstMap[tagID.String()]; ok { - return true + allowedPolicies = append(allowedPolicies, policy) + break } } } - return false + if len(allowedPolicies) > 0 { + return true, allowedPolicies + } + return false, []models.Acl{} } // IsNodeAllowedToCommunicate - check node is allowed to communicate with the peer -func IsNodeAllowedToCommunicate(node, peer models.Node, checkDefaultPolicy bool) bool { +func IsNodeAllowedToCommunicate(node, peer models.Node, checkDefaultPolicy bool) (bool, []models.Acl) { if node.IsStatic { node = node.StaticNode.ConvertToStaticNode() } @@ -530,11 +592,11 @@ func IsNodeAllowedToCommunicate(node, peer models.Node, checkDefaultPolicy bool) defaultPolicy, err := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy) if err == nil { if defaultPolicy.Enabled { - return true + return true, []models.Acl{defaultPolicy} } } } - + allowedPolicies := []models.Acl{} // list device policies policies := listDevicePolicies(models.NetworkID(peer.Network)) srcMap := make(map[string]struct{}) @@ -549,57 +611,88 @@ func IsNodeAllowedToCommunicate(node, peer models.Node, checkDefaultPolicy bool) } srcMap = convAclTagToValueMap(policy.Src) dstMap = convAclTagToValueMap(policy.Dst) - // fmt.Printf("\n======> SRCMAP: %+v\n", srcMap) - // fmt.Printf("\n======> DSTMAP: %+v\n", dstMap) - // fmt.Printf("\n======> node Tags: %+v\n", node.Tags) - // fmt.Printf("\n======> peer Tags: %+v\n", peer.Tags) for tagID := range node.Tags { - if _, ok := dstMap[tagID.String()]; ok { + allowed := false + if _, ok := dstMap[tagID.String()]; policy.AllowedDirection == models.TrafficDirectionBi && ok { if _, ok := srcMap["*"]; ok { - return true + allowed = true + allowedPolicies = append(allowedPolicies, policy) + break } for tagID := range peer.Tags { if _, ok := srcMap[tagID.String()]; ok { - return true + allowed = true + break } } } + if allowed { + allowedPolicies = append(allowedPolicies, policy) + break + } if _, ok := srcMap[tagID.String()]; ok { if _, ok := dstMap["*"]; ok { - return true + allowed = true + allowedPolicies = append(allowedPolicies, policy) + break } for tagID := range peer.Tags { if _, ok := dstMap[tagID.String()]; ok { - return true + allowed = true + break } } } + if allowed { + allowedPolicies = append(allowedPolicies, policy) + break + } } for tagID := range peer.Tags { + allowed := false if _, ok := dstMap[tagID.String()]; ok { if _, ok := srcMap["*"]; ok { - return true + allowed = true + allowedPolicies = append(allowedPolicies, policy) + break } for tagID := range node.Tags { if _, ok := srcMap[tagID.String()]; ok { - return true + allowed = true + break } } } - if _, ok := srcMap[tagID.String()]; ok { + if allowed { + allowedPolicies = append(allowedPolicies, policy) + break + } + + if _, ok := srcMap[tagID.String()]; policy.AllowedDirection == models.TrafficDirectionBi && ok { if _, ok := dstMap["*"]; ok { - return true + allowed = true + allowedPolicies = append(allowedPolicies, policy) + break } for tagID := range node.Tags { if _, ok := dstMap[tagID.String()]; ok { - return true + allowed = true + break } } } + if allowed { + allowedPolicies = append(allowedPolicies, policy) + break + } } } - return false + + if len(allowedPolicies) > 0 { + return true, allowedPolicies + } + return false, allowedPolicies } // SortTagEntrys - Sorts slice of Tag entries by their id @@ -648,7 +741,9 @@ func CheckIfTagAsActivePolicy(tagID models.TagID, netID models.NetworkID) bool { } for _, dstTagI := range acl.Dst { if dstTagI.ID == models.DeviceAclID { - return true + if tagID.String() == dstTagI.Value { + return true + } } } } @@ -682,3 +777,225 @@ func RemoveDeviceTagFromAclPolicies(tagID models.TagID, netID models.NetworkID) } return nil } + +func getUserAclRulesForNode(targetnode *models.Node, + rules map[string]models.AclRule) map[string]models.AclRule { + userNodes := GetStaticUserNodesByNetwork(models.NetworkID(targetnode.Network)) + userGrpMap := GetUserGrpMap() + allowedUsers := make(map[string][]models.Acl) + acls := listUserPolicies(models.NetworkID(targetnode.Network)) + for nodeTag := range targetnode.Tags { + for _, acl := range acls { + if !acl.Enabled { + continue + } + dstTags := convAclTagToValueMap(acl.Dst) + if _, ok := dstTags[nodeTag.String()]; ok { + // 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 + } + 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...) + rules[acl.ID] = aclRule + } else { + rules[acl.ID] = r + } + } + } + return rules +} + +func GetAclRulesForNode(targetnode *models.Node) (rules map[string]models.AclRule) { + 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)) + for nodeTag := range targetnode.Tags { + for _, acl := range acls { + if !acl.Enabled { + continue + } + srcTags := convAclTagToValueMap(acl.Src) + dstTags := convAclTagToValueMap(acl.Dst) + aclRule := models.AclRule{ + ID: acl.ID, + AllowedProtocol: acl.Proto, + AllowedPorts: acl.Port, + Direction: acl.AllowedDirection, + Allowed: true, + } + if acl.AllowedDirection == models.TrafficDirectionBi { + var existsInSrcTag bool + var existsInDstTag bool + + if _, ok := srcTags[nodeTag.String()]; ok { + existsInSrcTag = true + } + if _, ok := dstTags[nodeTag.String()]; ok { + existsInDstTag = true + } + + if existsInSrcTag && !existsInDstTag { + // get all dst tags + for dst := range dstTags { + if dst == nodeTag.String() { + continue + } + // Get peers in the tags and add allowed rules + nodes := taggedNodes[models.TagID(dst)] + for _, node := range nodes { + if node.ID == targetnode.ID { + continue + } + if node.Address.IP != nil { + aclRule.IPList = append(aclRule.IPList, node.AddressIPNet4()) + } + if node.Address6.IP != nil { + aclRule.IP6List = append(aclRule.IP6List, node.AddressIPNet6()) + } + if node.IsStatic && node.StaticNode.Address != "" { + aclRule.IPList = append(aclRule.IPList, node.StaticNode.AddressIPNet4()) + } + if node.IsStatic && node.StaticNode.Address6 != "" { + aclRule.IP6List = append(aclRule.IP6List, node.StaticNode.AddressIPNet6()) + } + } + } + } + if existsInDstTag && !existsInSrcTag { + // get all src tags + for src := range srcTags { + if src == nodeTag.String() { + continue + } + // Get peers in the tags and add allowed rules + nodes := taggedNodes[models.TagID(src)] + for _, node := range nodes { + if node.ID == targetnode.ID { + continue + } + if node.Address.IP != nil { + aclRule.IPList = append(aclRule.IPList, node.AddressIPNet4()) + } + if node.Address6.IP != nil { + aclRule.IP6List = append(aclRule.IP6List, node.AddressIPNet6()) + } + if node.IsStatic && node.StaticNode.Address != "" { + aclRule.IPList = append(aclRule.IPList, node.StaticNode.AddressIPNet4()) + } + if node.IsStatic && node.StaticNode.Address6 != "" { + aclRule.IP6List = append(aclRule.IP6List, node.StaticNode.AddressIPNet6()) + } + } + } + } + if existsInDstTag && existsInSrcTag { + nodes := taggedNodes[nodeTag] + for _, node := range nodes { + if node.ID == targetnode.ID { + continue + } + if node.Address.IP != nil { + aclRule.IPList = append(aclRule.IPList, node.AddressIPNet4()) + } + if node.Address6.IP != nil { + aclRule.IP6List = append(aclRule.IP6List, node.AddressIPNet6()) + } + if node.IsStatic && node.StaticNode.Address != "" { + aclRule.IPList = append(aclRule.IPList, node.StaticNode.AddressIPNet4()) + } + if node.IsStatic && node.StaticNode.Address6 != "" { + aclRule.IP6List = append(aclRule.IP6List, node.StaticNode.AddressIPNet6()) + } + } + } + } else { + if _, ok := dstTags[nodeTag.String()]; ok { + // get all src tags + for src := range srcTags { + if src == nodeTag.String() { + continue + } + // Get peers in the tags and add allowed rules + nodes := taggedNodes[models.TagID(src)] + for _, node := range nodes { + if node.ID == targetnode.ID { + continue + } + if node.Address.IP != nil { + aclRule.IPList = append(aclRule.IPList, node.AddressIPNet4()) + } + if node.Address6.IP != nil { + aclRule.IP6List = append(aclRule.IP6List, node.AddressIPNet6()) + } + if node.IsStatic && node.StaticNode.Address != "" { + aclRule.IPList = append(aclRule.IPList, node.StaticNode.AddressIPNet4()) + } + if node.IsStatic && node.StaticNode.Address6 != "" { + aclRule.IP6List = append(aclRule.IP6List, node.StaticNode.AddressIPNet6()) + } + } + } + } + } + if len(aclRule.IPList) > 0 || len(aclRule.IP6List) > 0 { + rules[acl.ID] = aclRule + } + } + } + return rules +} diff --git a/logic/extpeers.go b/logic/extpeers.go index 6cb35223..1f84c7e3 100644 --- a/logic/extpeers.go +++ b/logic/extpeers.go @@ -456,6 +456,10 @@ func GetStaticNodeIps(node models.Node) (ips []net.IP) { func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) { // fetch user access to static clients via policies + defer func() { + logger.Log(0, fmt.Sprintf("node.ID: %s, Rules: %+v\n", node.ID, rules)) + }() + defaultUserPolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.UserPolicy) defaultDevicePolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy) nodes, _ := GetNetworkNodes(node.Network) @@ -468,36 +472,50 @@ func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) { if peer.IsUserNode { continue } - if IsUserAllowedToCommunicate(userNodeI.StaticNode.OwnerID, peer) { + if ok, allowedPolicies := IsUserAllowedToCommunicate(userNodeI.StaticNode.OwnerID, peer); ok { if peer.IsStatic { if userNodeI.StaticNode.Address != "" { if !defaultUserPolicy.Enabled { - rules = append(rules, models.FwRule{ - SrcIP: userNodeI.StaticNode.AddressIPNet4(), - DstIP: peer.StaticNode.AddressIPNet4(), - Allow: true, - }) + for _, policy := range allowedPolicies { + rules = append(rules, models.FwRule{ + SrcIP: userNodeI.StaticNode.AddressIPNet4(), + DstIP: peer.StaticNode.AddressIPNet4(), + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + Allow: true, + }) + rules = append(rules, models.FwRule{ + SrcIP: peer.StaticNode.AddressIPNet4(), + DstIP: userNodeI.StaticNode.AddressIPNet4(), + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + Allow: true, + }) + } } - rules = append(rules, models.FwRule{ - SrcIP: peer.StaticNode.AddressIPNet4(), - DstIP: userNodeI.StaticNode.AddressIPNet4(), - Allow: true, - }) + } if userNodeI.StaticNode.Address6 != "" { if !defaultUserPolicy.Enabled { - rules = append(rules, models.FwRule{ - SrcIP: userNodeI.StaticNode.AddressIPNet6(), - DstIP: peer.StaticNode.AddressIPNet6(), - Allow: true, - }) + for _, policy := range allowedPolicies { + rules = append(rules, models.FwRule{ + SrcIP: userNodeI.StaticNode.AddressIPNet6(), + DstIP: peer.StaticNode.AddressIPNet6(), + Allow: true, + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + }) + rules = append(rules, models.FwRule{ + SrcIP: peer.StaticNode.AddressIPNet6(), + DstIP: userNodeI.StaticNode.AddressIPNet6(), + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + Allow: true, + }) + + } } - rules = append(rules, models.FwRule{ - SrcIP: peer.StaticNode.AddressIPNet6(), - DstIP: userNodeI.StaticNode.AddressIPNet6(), - Allow: true, - }) } if len(peer.StaticNode.ExtraAllowedIPs) > 0 { for _, additionalAllowedIPNet := range peer.StaticNode.ExtraAllowedIPs { @@ -526,29 +544,39 @@ func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) { if userNodeI.StaticNode.Address != "" { if !defaultUserPolicy.Enabled { - rules = append(rules, models.FwRule{ - SrcIP: userNodeI.StaticNode.AddressIPNet4(), - DstIP: net.IPNet{ - IP: peer.Address.IP, - Mask: net.CIDRMask(32, 32), - }, - Allow: true, - }) + for _, policy := range allowedPolicies { + 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), - }, - Allow: true, - }) + if !defaultUserPolicy.Enabled { + for _, policy := range allowedPolicies { + 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, + }) + } + } } } - } } } @@ -564,21 +592,48 @@ func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) { if peer.StaticNode.ClientID == nodeI.StaticNode.ClientID || peer.IsUserNode { continue } - if IsNodeAllowedToCommunicate(nodeI, peer, true) { + if ok, allowedPolicies := IsNodeAllowedToCommunicate(nodeI, peer, true); ok { if peer.IsStatic { if nodeI.StaticNode.Address != "" { - rules = append(rules, models.FwRule{ - SrcIP: nodeI.StaticNode.AddressIPNet4(), - DstIP: peer.StaticNode.AddressIPNet4(), - Allow: true, - }) + for _, policy := range allowedPolicies { + rules = append(rules, models.FwRule{ + SrcIP: nodeI.StaticNode.AddressIPNet4(), + DstIP: peer.StaticNode.AddressIPNet4(), + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + Allow: true, + }) + if policy.AllowedDirection == models.TrafficDirectionBi { + rules = append(rules, models.FwRule{ + SrcIP: peer.StaticNode.AddressIPNet4(), + DstIP: nodeI.StaticNode.AddressIPNet4(), + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + Allow: true, + }) + } + } + } if nodeI.StaticNode.Address6 != "" { - rules = append(rules, models.FwRule{ - SrcIP: nodeI.StaticNode.AddressIPNet6(), - DstIP: peer.StaticNode.AddressIPNet6(), - Allow: true, - }) + for _, policy := range allowedPolicies { + rules = append(rules, models.FwRule{ + SrcIP: nodeI.StaticNode.AddressIPNet6(), + DstIP: peer.StaticNode.AddressIPNet6(), + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + Allow: true, + }) + if policy.AllowedDirection == models.TrafficDirectionBi { + rules = append(rules, models.FwRule{ + SrcIP: peer.StaticNode.AddressIPNet6(), + DstIP: nodeI.StaticNode.AddressIPNet6(), + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + Allow: true, + }) + } + } } if len(peer.StaticNode.ExtraAllowedIPs) > 0 { for _, additionalAllowedIPNet := range peer.StaticNode.ExtraAllowedIPs { @@ -605,24 +660,56 @@ func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) { } } else { if nodeI.StaticNode.Address != "" { - rules = append(rules, models.FwRule{ - SrcIP: nodeI.StaticNode.AddressIPNet4(), - DstIP: net.IPNet{ - IP: peer.Address.IP, - Mask: net.CIDRMask(32, 32), - }, - Allow: true, - }) + for _, policy := range allowedPolicies { + rules = append(rules, models.FwRule{ + SrcIP: nodeI.StaticNode.AddressIPNet4(), + DstIP: net.IPNet{ + IP: peer.Address.IP, + Mask: net.CIDRMask(32, 32), + }, + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + Allow: true, + }) + if policy.AllowedDirection == models.TrafficDirectionBi { + rules = append(rules, models.FwRule{ + SrcIP: net.IPNet{ + IP: peer.Address.IP, + Mask: net.CIDRMask(32, 32), + }, + DstIP: nodeI.StaticNode.AddressIPNet4(), + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + Allow: true, + }) + } + } } if nodeI.StaticNode.Address6 != "" { - rules = append(rules, models.FwRule{ - SrcIP: nodeI.StaticNode.AddressIPNet6(), - DstIP: net.IPNet{ - IP: peer.Address6.IP, - Mask: net.CIDRMask(128, 128), - }, - Allow: true, - }) + for _, policy := range allowedPolicies { + rules = append(rules, models.FwRule{ + SrcIP: nodeI.StaticNode.AddressIPNet6(), + DstIP: net.IPNet{ + IP: peer.Address6.IP, + Mask: net.CIDRMask(128, 128), + }, + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + Allow: true, + }) + if policy.AllowedDirection == models.TrafficDirectionBi { + rules = append(rules, models.FwRule{ + SrcIP: net.IPNet{ + IP: peer.Address6.IP, + Mask: net.CIDRMask(128, 128), + }, + DstIP: nodeI.StaticNode.AddressIPNet6(), + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + Allow: true, + }) + } + } } } @@ -650,11 +737,11 @@ func GetExtPeers(node, peer *models.Node) ([]wgtypes.PeerConfig, []models.IDandA continue } if extPeer.RemoteAccessClientID == "" { - if !IsNodeAllowedToCommunicate(extPeer.ConvertToStaticNode(), *peer, true) { + if ok, _ := IsNodeAllowedToCommunicate(extPeer.ConvertToStaticNode(), *peer, true); !ok { continue } } else { - if !IsUserAllowedToCommunicate(extPeer.OwnerID, *peer) { + if ok, _ := IsUserAllowedToCommunicate(extPeer.OwnerID, *peer); !ok { continue } } @@ -739,7 +826,7 @@ func getExtpeerEgressRanges(node models.Node) (ranges, ranges6 []net.IPNet) { if len(extPeer.ExtraAllowedIPs) == 0 { continue } - if !IsNodeAllowedToCommunicate(extPeer.ConvertToStaticNode(), node, true) { + if ok, _ := IsNodeAllowedToCommunicate(extPeer.ConvertToStaticNode(), node, true); !ok { continue } for _, allowedRange := range extPeer.ExtraAllowedIPs { @@ -766,7 +853,7 @@ func getExtpeersExtraRoutes(node models.Node) (egressRoutes []models.EgressNetwo if len(extPeer.ExtraAllowedIPs) == 0 { continue } - if !IsNodeAllowedToCommunicate(extPeer.ConvertToStaticNode(), node, true) { + if ok, _ := IsNodeAllowedToCommunicate(extPeer.ConvertToStaticNode(), node, true); !ok { continue } egressRoutes = append(egressRoutes, getExtPeerEgressRoute(node, extPeer)...) diff --git a/logic/nodes.go b/logic/nodes.go index 32ca3ed7..9fe96a64 100644 --- a/logic/nodes.go +++ b/logic/nodes.go @@ -818,7 +818,7 @@ func GetTagMapWithNodes() (tagNodesMap map[models.TagID][]models.Node) { return } -func GetTagMapWithNodesByNetwork(netID models.NetworkID) (tagNodesMap map[models.TagID][]models.Node) { +func GetTagMapWithNodesByNetwork(netID models.NetworkID, withStaticNodes bool) (tagNodesMap map[models.TagID][]models.Node) { tagNodesMap = make(map[models.TagID][]models.Node) nodes, _ := GetNetworkNodes(netID.String()) for _, nodeI := range nodes { @@ -829,6 +829,9 @@ func GetTagMapWithNodesByNetwork(netID models.NetworkID) (tagNodesMap map[models tagNodesMap[nodeTagID] = append(tagNodesMap[nodeTagID], nodeI) } } + if !withStaticNodes { + return + } return AddTagMapWithStaticNodes(netID, tagNodesMap) } @@ -853,6 +856,27 @@ func AddTagMapWithStaticNodes(netID models.NetworkID, return tagNodesMap } +func AddTagMapWithStaticNodesWithUsers(netID models.NetworkID, + tagNodesMap map[models.TagID][]models.Node) map[models.TagID][]models.Node { + extclients, err := GetNetworkExtClients(netID.String()) + if err != nil { + return tagNodesMap + } + for _, extclient := range extclients { + if extclient.Tags == nil { + continue + } + for tagID := range extclient.Tags { + tagNodesMap[tagID] = append(tagNodesMap[tagID], models.Node{ + IsStatic: true, + StaticNode: extclient, + }) + } + + } + return tagNodesMap +} + func GetNodesWithTag(tagID models.TagID) map[string]models.Node { nMap := make(map[string]models.Node) tag, err := GetTag(tagID) diff --git a/logic/peers.go b/logic/peers.go index e88f3f4d..8a4fc854 100644 --- a/logic/peers.go +++ b/logic/peers.go @@ -74,8 +74,10 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N ServerVersion: servercfg.GetVersion(), ServerAddrs: []models.ServerAddr{}, FwUpdate: models.FwUpdate{ + AllowAll: true, EgressInfo: make(map[string]models.EgressInfo), IngressInfo: make(map[string]models.IngressInfo), + AclRules: make(map[string]models.AclRule), }, PeerIDs: make(models.PeerMap, 0), Peers: []wgtypes.PeerConfig{}, @@ -96,8 +98,6 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N if !node.Connected || node.PendingDelete || node.Action == models.NODE_DELETE { continue } - // check default policy if all allowed return true - defaultPolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy) if host.OS == models.OS_Types.IoT { hostPeerUpdate.NodeAddrs = append(hostPeerUpdate.NodeAddrs, node.PrimaryAddressIPNet()) if node.IsRelayed { @@ -156,6 +156,19 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N if !hostPeerUpdate.IsInternetGw { hostPeerUpdate.IsInternetGw = IsInternetGw(node) } + defaultUserPolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.UserPolicy) + defaultDevicePolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy) + if node.NetworkRange.IP != nil { + hostPeerUpdate.FwUpdate.Networks = append(hostPeerUpdate.FwUpdate.Networks, node.NetworkRange) + } + if node.NetworkRange6.IP != nil { + hostPeerUpdate.FwUpdate.Networks = append(hostPeerUpdate.FwUpdate.Networks, node.NetworkRange6) + } + + if !defaultDevicePolicy.Enabled || !defaultUserPolicy.Enabled { + hostPeerUpdate.FwUpdate.AllowAll = false + } + hostPeerUpdate.FwUpdate.AclRules = GetAclRulesForNode(&node) currentPeers := GetNetworkNodesMemory(allNodes, node.Network) for _, peer := range currentPeers { peer := peer @@ -257,11 +270,12 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N peerConfig.Endpoint.Port = peerHost.ListenPort } allowedips := GetAllowedIPs(&node, &peer, nil) + allowedToComm, _ := IsNodeAllowedToCommunicate(node, peer, false) if peer.Action != models.NODE_DELETE && !peer.PendingDelete && peer.Connected && nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(peer.ID.String())) && - (defaultPolicy.Enabled || IsNodeAllowedToCommunicate(node, peer, false)) && + (defaultDevicePolicy.Enabled || allowedToComm) && (deletedNode == nil || (deletedNode != nil && peer.ID.String() != deletedNode.ID.String())) { peerConfig.AllowedIPs = allowedips // only append allowed IPs if valid connection } @@ -311,8 +325,6 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N hostPeerUpdate.FwUpdate.IsIngressGw = true extPeers, extPeerIDAndAddrs, egressRoutes, err = GetExtPeers(&node, &node) if err == nil { - defaultUserPolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.UserPolicy) - defaultDevicePolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy) if !defaultDevicePolicy.Enabled || !defaultUserPolicy.Enabled { ingFwUpdate := models.IngressInfo{ IngressID: node.ID.String(), diff --git a/logic/tags.go b/logic/tags.go index 7cdf0f32..0c2aa095 100644 --- a/logic/tags.go +++ b/logic/tags.go @@ -85,7 +85,7 @@ func ListTagsWithNodes(netID models.NetworkID) ([]models.TagListResp, error) { if err != nil { return []models.TagListResp{}, err } - tagsNodeMap := GetTagMapWithNodesByNetwork(netID) + tagsNodeMap := GetTagMapWithNodesByNetwork(netID, true) resp := []models.TagListResp{} for _, tagI := range tags { tagRespI := models.TagListResp{ diff --git a/logic/user_mgmt.go b/logic/user_mgmt.go index 56395c78..f6eccac2 100644 --- a/logic/user_mgmt.go +++ b/logic/user_mgmt.go @@ -98,6 +98,25 @@ func ListPlatformRoles() ([]models.UserRolePermissionTemplate, error) { return userRoles, nil } +func GetUserGrpMap() map[models.UserGroupID]map[string]struct{} { + grpUsersMap := make(map[models.UserGroupID]map[string]struct{}) + users, _ := GetUsersDB() + for _, user := range users { + for gID := range user.UserGroups { + if grpUsers, ok := grpUsersMap[gID]; ok { + grpUsers[user.UserName] = struct{}{} + grpUsersMap[gID] = grpUsers + } else { + grpUsersMap[gID] = make(map[string]struct{}) + grpUsersMap[gID][user.UserName] = struct{}{} + } + } + + } + + return grpUsersMap +} + func userRolesInit() { d, _ := json.Marshal(SuperAdminPermissionTemplate) database.Insert(SuperAdminPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME) diff --git a/migrate/migrate.go b/migrate/migrate.go index 19e9232a..6cce4096 100644 --- a/migrate/migrate.go +++ b/migrate/migrate.go @@ -437,5 +437,6 @@ func createDefaultTagsAndPolicies() { for _, network := range networks { logic.CreateDefaultTags(models.NetworkID(network.NetID)) logic.CreateDefaultAclNetworkPolicies(models.NetworkID(network.NetID)) + logic.MigrateDefaulAclPolicies(models.NetworkID(network.NetID)) } } diff --git a/models/acl.go b/models/acl.go index d8c302ca..4778ad6d 100644 --- a/models/acl.go +++ b/models/acl.go @@ -1,6 +1,7 @@ package models import ( + "net" "time" ) @@ -14,6 +15,31 @@ const ( TrafficDirectionBi ) +// Protocol - allowed protocol +type Protocol string + +const ( + ALL Protocol = "all" + UDP Protocol = "udp" + TCP Protocol = "tcp" + ICMP Protocol = "icmp" +) + +type ServiceType string + +const ( + Http = "HTTP" + Https = "HTTPS" + AllTCP = "All TCP" + AllUDP = "All UDP" + ICMPService = "ICMP" + Custom = "Custom" +) + +func (p Protocol) String() string { + return string(p) +} + type AclPolicyType string const ( @@ -59,6 +85,9 @@ type Acl struct { RuleType AclPolicyType `json:"policy_type"` Src []AclPolicyTag `json:"src_type"` Dst []AclPolicyTag `json:"dst_type"` + Proto Protocol `json:"protocol"` // tcp, udp, etc. + ServiceType string `json:"type"` + Port []string `json:"ports"` AllowedDirection AllowedTrafficDirection `json:"allowed_traffic_direction"` Enabled bool `json:"enabled"` CreatedBy string `json:"created_by"` @@ -66,7 +95,25 @@ type Acl struct { } type AclPolicyTypes struct { + ProtocolTypes []ProtocolType RuleTypes []AclPolicyType `json:"policy_types"` SrcGroupTypes []AclGroupType `json:"src_grp_types"` DstGroupTypes []AclGroupType `json:"dst_grp_types"` } + +type ProtocolType struct { + Name string `json:"name"` + AllowedProtocols []Protocol `json:"allowed_protocols"` + PortRange string `json:"port_range"` + AllowPortSetting bool `json:"allow_port_setting"` +} + +type AclRule struct { + ID string `json:"id"` + IPList []net.IPNet `json:"ip_list"` + IP6List []net.IPNet `json:"ip6_list"` + AllowedProtocol Protocol `json:"allowed_protocols"` // tcp, udp, etc. + AllowedPorts []string `json:"allowed_ports"` + Direction AllowedTrafficDirection `json:"direction"` // single or two-way + Allowed bool +} diff --git a/models/mqtt.go b/models/mqtt.go index 3e687c90..4a8a8c34 100644 --- a/models/mqtt.go +++ b/models/mqtt.go @@ -30,9 +30,11 @@ type HostPeerUpdate struct { } type FwRule struct { - SrcIP net.IPNet - DstIP net.IPNet - Allow bool + SrcIP net.IPNet `json:"src_ip"` + DstIP net.IPNet `json:"dst_ip"` + AllowedProtocol Protocol `json:"allowed_protocols"` // tcp, udp, etc. + AllowedPorts []string `json:"allowed_ports"` + Allow bool `json:"allow"` } // IngressInfo - struct for ingress info @@ -92,10 +94,13 @@ type KeyUpdate struct { // FwUpdate - struct for firewall updates type FwUpdate struct { + AllowAll bool `json:"allow_all"` + Networks []net.IPNet `json:"networks"` IsEgressGw bool `json:"is_egress_gw"` IsIngressGw bool `json:"is_ingress_gw"` EgressInfo map[string]EgressInfo `json:"egress_info"` IngressInfo map[string]IngressInfo `json:"ingress_info"` + AclRules map[string]AclRule `json:"acl_rules"` } // FailOverMeReq - struct for failover req diff --git a/models/node.go b/models/node.go index 1462c8ad..8ed63c0b 100644 --- a/models/node.go +++ b/models/node.go @@ -215,6 +215,19 @@ func (node *Node) PrimaryAddress() string { return node.Address6.IP.String() } +func (node *Node) AddressIPNet4() net.IPNet { + return net.IPNet{ + IP: node.Address.IP, + Mask: net.CIDRMask(32, 32), + } +} +func (node *Node) AddressIPNet6() net.IPNet { + return net.IPNet{ + IP: node.Address6.IP, + Mask: net.CIDRMask(128, 128), + } +} + // ExtClient.PrimaryAddress - returns ipv4 IPNet format func (extPeer *ExtClient) AddressIPNet4() net.IPNet { return net.IPNet{ diff --git a/pro/logic/status.go b/pro/logic/status.go index 225189ec..39dccd26 100644 --- a/pro/logic/status.go +++ b/pro/logic/status.go @@ -133,7 +133,8 @@ func checkPeerStatus(node *models.Node, defaultAclPolicy bool) { if err != nil { continue } - if !defaultAclPolicy && !logic.IsNodeAllowedToCommunicate(*node, peer, false) { + allowed, _ := logic.IsNodeAllowedToCommunicate(*node, peer, false) + if !defaultAclPolicy && !allowed { continue } @@ -167,7 +168,8 @@ func checkPeerConnectivity(node *models.Node, metrics *models.Metrics, defaultAc if err != nil { continue } - if !defaultAclPolicy && !logic.IsNodeAllowedToCommunicate(*node, peer, false) { + allowed, _ := logic.IsNodeAllowedToCommunicate(*node, peer, false) + if !defaultAclPolicy && !allowed { continue }