NET-1910: Acl controls for Egress Traffic (#3377)

* add support for egress ranges on acl policy

* add egress ranges to acl rules

* add egress ranges to acl policies

* Add egress ranges to acl rules

* add egress ranges to fw update

* fetch acl rules for egress networks

* apply egress policies for devices

* configure user policies for egresss routes

* fix gw tag name migration

* fix egress acl rules for static nodes

* add egress ranges for static nodes on ingress gw

* fileter acl IPs to be unique

* cleanup IOT logic from peer update

* make acl Rule Dst List

* cleanup egress ranges from acl policies

* create user group default acl policy for gateways

* remove remote access name ids

* rm egress ranges removal from acl policies

* simplify user permissions on nodes

* add additional nameservers to extclient dns

* remove debug logs

* fix static checks
This commit is contained in:
Abhishek K 2025-03-18 13:25:55 +04:00 committed by GitHub
parent 372c797e60
commit 3d765f9cf1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 737 additions and 436 deletions

View file

@ -4,7 +4,7 @@ ARG tags
WORKDIR /app
COPY . .
RUN GOOS=linux CGO_ENABLED=1 go build -ldflags="-s -w " -tags ${tags} .
RUN GOOS=linux CGO_ENABLED=1 go build -race -ldflags="-s -w " -tags ${tags} .
# RUN go build -tags=ee . -o netmaker main.go
FROM alpine:3.21.2

View file

@ -51,6 +51,7 @@ func aclPolicyTypes(w http.ResponseWriter, r *http.Request) {
DstGroupTypes: []models.AclGroupType{
models.NodeTagID,
models.NodeID,
models.EgressRange,
// models.NetmakerIPAclID,
// models.NetmakerSubNetRangeAClID,
},

View file

@ -475,7 +475,18 @@ func getExtClientHAConf(w http.ResponseWriter, r *http.Request) {
// models.RemoteAccessTagName))] = struct{}{}
// set extclient dns to ingressdns if extclient dns is not explicitly set
if (extclient.DNS == "") && (gwnode.IngressDNS != "") {
extclient.DNS = gwnode.IngressDNS
network, _ := logic.GetNetwork(gwnode.Network)
dns := gwnode.IngressDNS
if len(network.NameServers) > 0 {
if dns == "" {
dns = strings.Join(network.NameServers, ",")
} else {
dns += "," + strings.Join(network.NameServers, ",")
}
}
extclient.DNS = dns
}
listenPort := logic.GetPeerListenPort(host)

View file

@ -140,60 +140,12 @@ func upgradeHost(w http.ResponseWriter, r *http.Request) {
// @Failure 500 {object} models.ErrorResponse
func getHosts(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
currentHosts := []models.Host{}
var err error
if r.Header.Get("ismaster") == "yes" {
currentHosts, err = logic.GetAllHosts()
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to fetch hosts: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
} else {
username := r.Header.Get("user")
user, err := logic.GetUser(username)
if err != nil {
return
}
userPlatformRole, err := logic.GetRole(user.PlatformRoleID)
if err != nil {
return
}
respHostsMap := make(map[string]struct{})
if !userPlatformRole.FullAccess {
nodes, err := logic.GetAllNodes()
if err != nil {
logger.Log(0, "error fetching all nodes info: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
filteredNodes := logic.GetFilteredNodesByUserAccess(*user, nodes)
if len(filteredNodes) > 0 {
currentHostsMap, err := logic.GetHostsMap()
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to fetch hosts: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
for _, node := range filteredNodes {
if _, ok := respHostsMap[node.HostID.String()]; ok {
continue
}
if host, ok := currentHostsMap[node.HostID.String()]; ok {
currentHosts = append(currentHosts, host)
respHostsMap[host.ID.String()] = struct{}{}
}
}
}
} else {
currentHosts, err = logic.GetAllHosts()
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to fetch hosts: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
}
currentHosts, err := logic.GetAllHosts()
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to fetch hosts: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
apiHosts := logic.GetAllHostsAPI(currentHosts[:])

View file

@ -41,6 +41,7 @@ func networkHandlers(r *mux.Router) {
Methods(http.MethodPut)
r.HandleFunc("/api/networks/{networkname}/acls", logic.SecurityCheck(true, http.HandlerFunc(getNetworkACL))).
Methods(http.MethodGet)
r.HandleFunc("/api/networks/{networkname}/egress_routes", logic.SecurityCheck(true, http.HandlerFunc(getNetworkEgressRoutes)))
}
// @Summary Lists all networks
@ -429,6 +430,33 @@ func getNetworkACL(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(networkACL)
}
// @Summary Get a network Egress routes
// @Router /api/networks/{networkname}/egress_routes [get]
// @Tags Networks
// @Security oauth
// @Param networkname path string true "Network name"
// @Produce json
// @Success 200 {object} acls.SuccessResponse
// @Failure 500 {object} models.ErrorResponse
func getNetworkEgressRoutes(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
netname := params["networkname"]
// check if network exists
_, err := logic.GetNetwork(netname)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to fetch ACLs for network [%s]: %v", netname, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
nodeEgressRoutes, _, err := logic.GetEgressRanges(models.NetworkID(netname))
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
logic.ReturnSuccessResponseWithJson(w, r, nodeEgressRoutes, "fetched network egress routes")
}
// @Summary Delete a network
// @Router /api/networks/{networkname} [delete]
// @Tags Networks

View file

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"maps"
"net"
"sort"
"sync"
"time"
@ -222,6 +223,104 @@ func IsAclExists(aclID string) bool {
_, err := GetAcl(aclID)
return err == nil
}
func GetEgressRanges(netID models.NetworkID) (map[string][]string, map[string]struct{}, error) {
resultMap := make(map[string]struct{})
nodeEgressMap := make(map[string][]string)
networkNodes, err := GetNetworkNodes(netID.String())
if err != nil {
return nil, nil, err
}
for _, currentNode := range networkNodes {
if currentNode.Network != netID.String() {
continue
}
if currentNode.IsEgressGateway { // add the egress gateway range(s) to the result
if len(currentNode.EgressGatewayRanges) > 0 {
nodeEgressMap[currentNode.ID.String()] = currentNode.EgressGatewayRanges
for _, egressRangeI := range currentNode.EgressGatewayRanges {
resultMap[egressRangeI] = struct{}{}
}
}
}
}
extclients, _ := GetNetworkExtClients(netID.String())
for _, extclient := range extclients {
if len(extclient.ExtraAllowedIPs) > 0 {
nodeEgressMap[extclient.ClientID] = extclient.ExtraAllowedIPs
for _, extraAllowedIP := range extclient.ExtraAllowedIPs {
resultMap[extraAllowedIP] = struct{}{}
}
}
}
return nodeEgressMap, resultMap, nil
}
func checkIfAclTagisValid(t models.AclPolicyTag, netID models.NetworkID, policyType models.AclPolicyType, isSrc bool) bool {
switch t.ID {
case models.NodeTagID:
if policyType == models.UserPolicy && isSrc {
return false
}
// check if tag is valid
_, err := GetTag(models.TagID(t.Value))
if err != nil {
return false
}
case models.NodeID:
if policyType == models.UserPolicy && isSrc {
return false
}
_, nodeErr := GetNodeByID(t.Value)
if nodeErr != nil {
_, staticNodeErr := GetExtClient(t.Value, netID.String())
if staticNodeErr != nil {
return false
}
}
case models.EgressRange:
if isSrc {
return false
}
// _, rangesMap, err := GetEgressRanges(netID)
// if err != nil {
// return false
// }
// if _, ok := rangesMap[t.Value]; !ok {
// return false
// }
case models.UserAclID:
if policyType == models.DevicePolicy {
return false
}
if !isSrc {
return false
}
_, err := GetUser(t.Value)
if err != nil {
return false
}
case models.UserGroupAclID:
if policyType == models.DevicePolicy {
return false
}
if !isSrc {
return false
}
err := IsGroupValid(models.UserGroupID(t.Value))
if err != nil {
return false
}
// check if group belongs to this network
netGrps := GetUserGroupsInNetwork(netID)
if _, ok := netGrps[models.UserGroupID(t.Value)]; !ok {
return false
}
default:
return false
}
return true
}
// IsAclPolicyValid - validates if acl policy is valid
func IsAclPolicyValid(acl models.Acl) bool {
@ -235,115 +334,43 @@ func IsAclPolicyValid(acl models.Acl) bool {
// src list should only contain users
for _, srcI := range acl.Src {
if srcI.ID == "" || srcI.Value == "" {
return false
}
if srcI.Value == "*" {
continue
}
if srcI.ID != models.UserAclID && srcI.ID != models.UserGroupAclID {
// check if user group is valid
if !checkIfAclTagisValid(srcI, acl.NetworkID, acl.RuleType, true) {
return false
}
// check if user group is valid
if srcI.ID == models.UserAclID {
_, err := GetUser(srcI.Value)
if err != nil {
return false
}
} else if srcI.ID == models.UserGroupAclID {
err := IsGroupValid(models.UserGroupID(srcI.Value))
if err != nil {
return false
}
// check if group belongs to this network
netGrps := GetUserGroupsInNetwork(acl.NetworkID)
if _, ok := netGrps[models.UserGroupID(srcI.Value)]; !ok {
return false
}
}
}
for _, dstI := range acl.Dst {
if dstI.ID == "" || dstI.Value == "" {
return false
}
if dstI.ID != models.NodeTagID && dstI.ID != models.NodeID {
return false
}
if dstI.Value == "*" {
continue
}
if dstI.ID == models.NodeTagID {
// check if tag is valid
_, err := GetTag(models.TagID(dstI.Value))
if err != nil {
return false
}
} else {
_, nodeErr := GetNodeByID(dstI.Value)
if nodeErr != nil {
_, staticNodeErr := GetExtClient(dstI.Value, acl.NetworkID.String())
if staticNodeErr != nil {
return false
}
}
// check if user group is valid
if !checkIfAclTagisValid(dstI, acl.NetworkID, acl.RuleType, false) {
return false
}
}
case models.DevicePolicy:
for _, srcI := range acl.Src {
if srcI.ID == "" || srcI.Value == "" {
return false
}
if srcI.ID != models.NodeTagID && srcI.ID != models.NodeID {
return false
}
if srcI.Value == "*" {
continue
}
if srcI.ID == models.NodeTagID {
// check if tag is valid
_, err := GetTag(models.TagID(srcI.Value))
if err != nil {
return false
}
} else {
_, nodeErr := GetNodeByID(srcI.Value)
if nodeErr != nil {
_, staticNodeErr := GetExtClient(srcI.Value, acl.NetworkID.String())
if staticNodeErr != nil {
return false
}
}
// check if user group is valid
if !checkIfAclTagisValid(srcI, acl.NetworkID, acl.RuleType, true) {
return false
}
}
for _, dstI := range acl.Dst {
if dstI.ID == "" || dstI.Value == "" {
return false
}
if dstI.ID != models.NodeTagID && dstI.ID != models.NodeID {
return false
}
if dstI.Value == "*" {
continue
}
if dstI.ID == models.NodeTagID {
// check if tag is valid
_, err := GetTag(models.TagID(dstI.Value))
if err != nil {
return false
}
} else {
_, nodeErr := GetNodeByID(dstI.Value)
if nodeErr != nil {
_, staticNodeErr := GetExtClient(dstI.Value, acl.NetworkID.String())
if staticNodeErr != nil {
return false
}
}
// check if user group is valid
if !checkIfAclTagisValid(dstI, acl.NetworkID, acl.RuleType, false) {
return false
}
}
}
@ -688,8 +715,8 @@ func RemoveUserFromAclPolicy(userName string) {
delete := false
update := false
if acl.RuleType == models.UserPolicy {
for i, srcI := range acl.Src {
if srcI.ID == models.UserAclID && srcI.Value == userName {
for i := len(acl.Src) - 1; i >= 0; i-- {
if acl.Src[i].ID == models.UserAclID && acl.Src[i].Value == userName {
if len(acl.Src) == 1 {
// delete policy
delete = true
@ -723,8 +750,8 @@ func RemoveNodeFromAclPolicy(node models.Node) {
delete := false
update := false
if acl.RuleType == models.DevicePolicy {
for i, srcI := range acl.Src {
if srcI.ID == models.NodeID && srcI.Value == nodeID {
for i := len(acl.Src) - 1; i >= 0; i-- {
if acl.Src[i].ID == models.NodeID && acl.Src[i].Value == nodeID {
if len(acl.Src) == 1 {
// delete policy
delete = true
@ -739,8 +766,8 @@ func RemoveNodeFromAclPolicy(node models.Node) {
DeleteAcl(acl)
continue
}
for i, dstI := range acl.Dst {
if dstI.ID == models.NodeID && dstI.Value == nodeID {
for i := len(acl.Dst) - 1; i >= 0; i-- {
if acl.Dst[i].ID == models.NodeID && acl.Dst[i].Value == nodeID {
if len(acl.Dst) == 1 {
// delete policy
delete = true
@ -761,8 +788,8 @@ func RemoveNodeFromAclPolicy(node models.Node) {
}
if acl.RuleType == models.UserPolicy {
for i, dstI := range acl.Dst {
if dstI.ID == models.NodeID && dstI.Value == nodeID {
for i := len(acl.Dst) - 1; i >= 0; i-- {
if acl.Dst[i].ID == models.NodeID && acl.Dst[i].Value == nodeID {
if len(acl.Dst) == 1 {
// delete policy
delete = true
@ -1239,17 +1266,17 @@ func RemoveDeviceTagFromAclPolicies(tagID models.TagID, netID models.NetworkID)
acls := listDevicePolicies(netID)
update := false
for _, acl := range acls {
for i, srcTagI := range acl.Src {
if srcTagI.ID == models.NodeTagID {
if tagID.String() == srcTagI.Value {
for i := len(acl.Src) - 1; i >= 0; i-- {
if acl.Src[i].ID == models.NodeTagID {
if tagID.String() == acl.Src[i].Value {
acl.Src = append(acl.Src[:i], acl.Src[i+1:]...)
update = true
}
}
}
for i, dstTagI := range acl.Dst {
if dstTagI.ID == models.NodeTagID {
if tagID.String() == dstTagI.Value {
for i := len(acl.Dst) - 1; i >= 0; i-- {
if acl.Dst[i].ID == models.NodeTagID {
if tagID.String() == acl.Dst[i].Value {
acl.Dst = append(acl.Dst[:i], acl.Dst[i+1:]...)
update = true
}
@ -1262,19 +1289,16 @@ func RemoveDeviceTagFromAclPolicies(tagID models.TagID, netID models.NetworkID)
return nil
}
func getUserAclRulesForNode(targetnode *models.Node,
func getEgressUserRulesForNode(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))
var targetNodeTags = make(map[models.TagID]struct{})
if targetnode.Mutex != nil {
targetnode.Mutex.Lock()
targetNodeTags = maps.Clone(targetnode.Tags)
targetnode.Mutex.Unlock()
} else {
targetNodeTags = maps.Clone(targetnode.Tags)
targetNodeTags["*"] = struct{}{}
for _, rangeI := range targetnode.EgressGatewayRanges {
targetNodeTags[models.TagID(rangeI)] = struct{}{}
}
for _, acl := range acls {
if !acl.Enabled {
@ -1285,10 +1309,9 @@ func getUserAclRulesForNode(targetnode *models.Node,
addUsers := false
if !all {
for nodeTag := range targetNodeTags {
if _, ok := dstTags[nodeTag.String()]; !ok {
if _, ok = dstTags[targetnode.ID.String()]; !ok {
break
}
if _, ok := dstTags[nodeTag.String()]; ok {
addUsers = true
break
}
}
} else {
@ -1326,7 +1349,110 @@ func getUserAclRulesForNode(targetnode *models.Node,
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.EgressRange {
ip, cidr, err := net.ParseCIDR(dstI.Value)
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
}
}
}
return rules
}
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))
var targetNodeTags = make(map[models.TagID]struct{})
if targetnode.Mutex != nil {
targetnode.Mutex.Lock()
targetNodeTags = maps.Clone(targetnode.Tags)
targetnode.Mutex.Unlock()
} else {
targetNodeTags = maps.Clone(targetnode.Tags)
}
targetNodeTags[models.TagID(targetnode.ID.String())] = struct{}{}
for _, acl := range acls {
if !acl.Enabled {
continue
}
dstTags := 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
}
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
}
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,
@ -1354,7 +1480,16 @@ func getUserAclRulesForNode(targetnode *models.Node,
}
func checkIfAnyPolicyisUniDirectional(targetNode models.Node) bool {
targetNode.Tags[models.TagID(targetNode.ID.String())] = struct{}{}
var targetNodeTags = make(map[models.TagID]struct{})
if targetNode.Mutex != nil {
targetNode.Mutex.Lock()
targetNodeTags = maps.Clone(targetNode.Tags)
targetNode.Mutex.Unlock()
} else {
targetNodeTags = maps.Clone(targetNode.Tags)
}
targetNodeTags[models.TagID(targetNode.ID.String())] = struct{}{}
targetNodeTags["*"] = struct{}{}
acls := listDevicePolicies(models.NetworkID(targetNode.Network))
for _, acl := range acls {
if !acl.Enabled {
@ -1363,9 +1498,12 @@ func checkIfAnyPolicyisUniDirectional(targetNode models.Node) bool {
if acl.AllowedDirection == models.TrafficDirectionBi {
continue
}
if acl.Proto != models.ALL || acl.ServiceType != models.Any {
return true
}
srcTags := convAclTagToValueMap(acl.Src)
dstTags := convAclTagToValueMap(acl.Dst)
for nodeTag := range targetNode.Tags {
for nodeTag := range targetNodeTags {
if _, ok := srcTags[nodeTag.String()]; ok {
return true
}
@ -1385,12 +1523,10 @@ func checkIfAnyPolicyisUniDirectional(targetNode models.Node) bool {
func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRule) {
targetnode := *targetnodeI
targetnode.Tags[models.TagID(targetnode.ID.String())] = struct{}{}
defer func() {
if !targetnode.IsIngressGateway {
rules = getUserAclRulesForNode(&targetnode, rules)
}
}()
rules = make(map[string]models.AclRule)
var taggedNodes map[models.TagID][]models.Node
@ -1409,8 +1545,8 @@ func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRu
} else {
targetNodeTags = maps.Clone(targetnode.Tags)
}
targetNodeTags[models.TagID(targetnode.ID.String())] = struct{}{}
targetNodeTags["*"] = struct{}{}
for _, acl := range acls {
if !acl.Enabled {
continue
@ -1593,9 +1729,245 @@ func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRu
}
}
if len(aclRule.IPList) > 0 || len(aclRule.IP6List) > 0 {
aclRule.IPList = UniqueIPNetList(aclRule.IPList)
aclRule.IP6List = UniqueIPNetList(aclRule.IP6List)
rules[acl.ID] = aclRule
}
}
return rules
}
func UniqueIPNetList(ipnets []net.IPNet) []net.IPNet {
uniqueMap := make(map[string]net.IPNet)
for _, ipnet := range ipnets {
key := ipnet.String() // Uses CIDR notation as a unique key
if _, exists := uniqueMap[key]; !exists {
uniqueMap[key] = ipnet
}
}
// Convert map back to slice
uniqueList := make([]net.IPNet, 0, len(uniqueMap))
for _, ipnet := range uniqueMap {
uniqueList = append(uniqueList, ipnet)
}
return uniqueList
}
func GetEgressRulesForNode(targetnode models.Node) (rules map[string]models.AclRule) {
rules = make(map[string]models.AclRule)
defer func() {
rules = getEgressUserRulesForNode(&targetnode, rules)
}()
taggedNodes := GetTagMapWithNodesByNetwork(models.NetworkID(targetnode.Network), true)
acls := listDevicePolicies(models.NetworkID(targetnode.Network))
var targetNodeTags = make(map[models.TagID]struct{})
targetNodeTags["*"] = struct{}{}
/*
if target node is egress gateway
if acl policy has egress route and it is present in target node egress ranges
fetches all the nodes in that policy and add rules
*/
for _, rangeI := range targetnode.EgressGatewayRanges {
targetNodeTags[models.TagID(rangeI)] = struct{}{}
}
for _, acl := range acls {
if !acl.Enabled {
continue
}
srcTags := convAclTagToValueMap(acl.Src)
dstTags := convAclTagToValueMap(acl.Dst)
_, srcAll := srcTags["*"]
_, dstAll := dstTags["*"]
for nodeTag := range targetNodeTags {
aclRule := models.AclRule{
ID: acl.ID,
AllowedProtocol: acl.Proto,
AllowedPorts: acl.Port,
Direction: acl.AllowedDirection,
Allowed: true,
}
if nodeTag != "*" {
ip, cidr, err := net.ParseCIDR(nodeTag.String())
if err != nil {
continue
}
if ip.To4() != nil {
aclRule.Dst = append(aclRule.Dst, *cidr)
} else {
aclRule.Dst6 = append(aclRule.Dst6, *cidr)
}
} else {
aclRule.Dst = append(aclRule.Dst, net.IPNet{
IP: net.IPv4zero, // 0.0.0.0
Mask: net.CIDRMask(0, 32), // /0 means match all IPv4
})
aclRule.Dst6 = append(aclRule.Dst6, net.IPNet{
IP: net.IPv6zero, // ::
Mask: net.CIDRMask(0, 128), // /0 means match all IPv6
})
}
if acl.AllowedDirection == models.TrafficDirectionBi {
var existsInSrcTag bool
var existsInDstTag bool
if _, ok := srcTags[nodeTag.String()]; ok || srcAll {
existsInSrcTag = true
}
if _, ok := dstTags[nodeTag.String()]; ok || dstAll {
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)]
if dst != targetnode.ID.String() {
node, err := GetNodeByID(dst)
if err == nil {
nodes = append(nodes, node)
}
}
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)]
if src != targetnode.ID.String() {
node, err := GetNodeByID(src)
if err == nil {
nodes = append(nodes, node)
}
}
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 srcID := range srcTags {
if srcID == targetnode.ID.String() {
continue
}
node, err := GetNodeByID(srcID)
if err == nil {
nodes = append(nodes, node)
}
}
for dstID := range dstTags {
if dstID == targetnode.ID.String() {
continue
}
node, err := GetNodeByID(dstID)
if err == nil {
nodes = append(nodes, node)
}
}
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 {
_, all := dstTags["*"]
if _, ok := dstTags[nodeTag.String()]; ok || all {
// 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 {
aclRule.IPList = UniqueIPNetList(aclRule.IPList)
aclRule.IP6List = UniqueIPNetList(aclRule.IP6List)
rules[acl.ID] = aclRule
}
}
}
return
}

View file

@ -591,132 +591,130 @@ func getFwRulesForNodeAndPeerOnGw(node, peer models.Node, allowedPolicies []mode
}
}
// add egress range rules
for _, dstI := range policy.Dst {
if dstI.ID == models.EgressRange {
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,
})
}
}
}
}
}
}
return
}
func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) {
// fetch user access to static clients via policies
func getFwRulesForUserNodesOnGw(node models.Node, nodes []models.Node) (rules []models.FwRule) {
defaultUserPolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.UserPolicy)
defaultDevicePolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy)
nodes, _ := GetNetworkNodes(node.Network)
nodes = append(nodes, GetStaticNodesByNetwork(models.NetworkID(node.Network), true)...)
userNodes := GetStaticUserNodesByNetwork(models.NetworkID(node.Network))
for _, userNodeI := range userNodes {
for _, peer := range nodes {
if peer.IsUserNode {
continue
}
if ok, allowedPolicies := IsUserAllowedToCommunicate(userNodeI.StaticNode.OwnerID, peer); ok {
if peer.IsStatic {
if userNodeI.StaticNode.Address != "" {
if !defaultUserPolicy.Enabled {
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,
})
peer = peer.StaticNode.ConvertToStaticNode()
}
if !defaultUserPolicy.Enabled {
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.ID == models.EgressRange {
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,
})
}
}
}
}
}
if userNodeI.StaticNode.Address6 != "" {
if !defaultUserPolicy.Enabled {
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,
})
}
}
}
if len(peer.StaticNode.ExtraAllowedIPs) > 0 {
for _, additionalAllowedIPNet := range peer.StaticNode.ExtraAllowedIPs {
_, ipNet, err := net.ParseCIDR(additionalAllowedIPNet)
if err != nil {
continue
}
if ipNet.IP.To4() != nil {
rules = append(rules, models.FwRule{
SrcIP: userNodeI.StaticNode.AddressIPNet4(),
DstIP: *ipNet,
Allow: true,
})
} else {
rules = append(rules, models.FwRule{
SrcIP: userNodeI.StaticNode.AddressIPNet6(),
DstIP: *ipNet,
Allow: true,
})
}
}
}
} else {
if userNodeI.StaticNode.Address != "" {
if !defaultUserPolicy.Enabled {
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 != "" {
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,
})
}
}
}
}
}
}
}
return
}
func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) {
// fetch user access to static clients via policies
defaultDevicePolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy)
nodes, _ := GetNetworkNodes(node.Network)
nodes = append(nodes, GetStaticNodesByNetwork(models.NetworkID(node.Network), true)...)
rules = getFwRulesForUserNodesOnGw(node, nodes)
if defaultDevicePolicy.Enabled {
return
}
@ -739,17 +737,34 @@ func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) {
if peer.IsStatic {
peer = peer.StaticNode.ConvertToStaticNode()
}
if ok, allowedPolicies := IsNodeAllowedToCommunicateV1(nodeI.StaticNode.ConvertToStaticNode(), peer, true); ok {
rules = append(rules, getFwRulesForNodeAndPeerOnGw(nodeI.StaticNode.ConvertToStaticNode(), peer, allowedPolicies)...)
var allowedPolicies1 []models.Acl
var ok bool
if ok, allowedPolicies1 = IsNodeAllowedToCommunicateV1(nodeI.StaticNode.ConvertToStaticNode(), peer, true); ok {
rules = append(rules, getFwRulesForNodeAndPeerOnGw(nodeI.StaticNode.ConvertToStaticNode(), peer, allowedPolicies1)...)
}
if ok, allowedPolicies := IsNodeAllowedToCommunicateV1(peer, nodeI.StaticNode.ConvertToStaticNode(), true); ok {
rules = append(rules, getFwRulesForNodeAndPeerOnGw(peer, nodeI.StaticNode.ConvertToStaticNode(), allowedPolicies)...)
if ok, allowedPolicies2 := IsNodeAllowedToCommunicateV1(peer, nodeI.StaticNode.ConvertToStaticNode(), true); ok {
rules = append(rules,
getFwRulesForNodeAndPeerOnGw(peer, nodeI.StaticNode.ConvertToStaticNode(),
GetUniquePolicies(allowedPolicies1, allowedPolicies2))...)
}
}
}
return
}
func GetUniquePolicies(policies1, policies2 []models.Acl) []models.Acl {
policies1Map := make(map[string]struct{})
for _, policy1I := range policies1 {
policies1Map[policy1I.ID] = struct{}{}
}
for i := len(policies2) - 1; i >= 0; i-- {
if _, ok := policies1Map[policies2[i].ID]; ok {
policies2 = append(policies2[:i], policies2[i+1:]...)
}
}
return policies2
}
func GetExtPeers(node, peer *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, []models.EgressNetworkRoutes, error) {
var peers []wgtypes.PeerConfig
var idsAndAddr []models.IDandAddr
@ -764,13 +779,11 @@ func GetExtPeers(node, peer *models.Node) ([]wgtypes.PeerConfig, []models.IDandA
}
for _, extPeer := range extPeers {
extPeer := extPeer
fmt.Println("=====> checking EXT peer: ", extPeer.ClientID)
if !IsClientNodeAllowed(&extPeer, peer.ID.String()) {
continue
}
if extPeer.RemoteAccessClientID == "" {
if ok := IsPeerAllowed(extPeer.ConvertToStaticNode(), *peer, true); !ok {
fmt.Println("=====>1 checking EXT peer: ", extPeer.ClientID)
continue
}
} else {
@ -934,16 +947,11 @@ func GetStaticUserNodesByNetwork(network models.NetworkID) (staticNode []models.
for _, extI := range extClients {
if extI.Network == network.String() {
if extI.RemoteAccessClientID != "" {
n := models.Node{
IsStatic: true,
StaticNode: extI,
IsUserNode: extI.RemoteAccessClientID != "",
}
n := extI.ConvertToStaticNode()
staticNode = append(staticNode, n)
}
}
}
return
}

View file

@ -866,6 +866,9 @@ func GetTagMapWithNodesByNetwork(netID models.NetworkID, withStaticNodes bool) (
nodeI.Mutex.Lock()
}
for nodeTagID := range nodeI.Tags {
if nodeTagID == models.TagID(nodeI.ID.String()) {
continue
}
tagNodesMap[nodeTagID] = append(tagNodesMap[nodeTagID], nodeI)
}
if nodeI.Mutex != nil {
@ -903,6 +906,9 @@ func AddTagMapWithStaticNodes(netID models.NetworkID,
extclient.Mutex.Lock()
}
for tagID := range extclient.Tags {
if tagID == models.TagID(extclient.ClientID) {
continue
}
tagNodesMap[tagID] = append(tagNodesMap[tagID], extclient.ConvertToStaticNode())
tagNodesMap["*"] = append(tagNodesMap["*"], extclient.ConvertToStaticNode())
}

View file

@ -181,6 +181,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
slog.Debug("peer update for host", "hostId", host.ID.String())
peerIndexMap := make(map[string]int)
for _, nodeID := range host.Nodes {
networkAllowAll := true
nodeID := nodeID
node, err := GetNodeByID(nodeID)
if err != nil {
@ -190,60 +191,6 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
if !node.Connected || node.PendingDelete || node.Action == models.NODE_DELETE {
continue
}
if host.OS == models.OS_Types.IoT {
hostPeerUpdate.NodeAddrs = append(hostPeerUpdate.NodeAddrs, node.PrimaryAddressIPNet())
if node.IsRelayed {
relayNode, err := GetNodeByID(node.RelayedBy)
if err != nil {
continue
}
relayHost, err := GetHost(relayNode.HostID.String())
if err != nil {
continue
}
relayPeer := wgtypes.PeerConfig{
PublicKey: relayHost.PublicKey,
PersistentKeepaliveInterval: &relayHost.PersistentKeepalive,
ReplaceAllowedIPs: true,
AllowedIPs: GetAllowedIPs(&node, &relayNode, nil),
}
uselocal := false
if host.EndpointIP.String() == relayHost.EndpointIP.String() {
// peer is on same network
// set to localaddress
uselocal = true
if node.LocalAddress.IP == nil {
// use public endpint
uselocal = false
}
if node.LocalAddress.String() == relayNode.LocalAddress.String() {
uselocal = false
}
}
relayPeer.Endpoint = &net.UDPAddr{
IP: relayHost.EndpointIP,
Port: GetPeerListenPort(relayHost),
}
if uselocal {
relayPeer.Endpoint.IP = relayNode.LocalAddress.IP
relayPeer.Endpoint.Port = relayHost.ListenPort
}
hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, relayPeer)
} else if deletedNode != nil && deletedNode.IsRelay {
relayHost, err := GetHost(deletedNode.HostID.String())
if err != nil {
continue
}
relayPeer := wgtypes.PeerConfig{
PublicKey: relayHost.PublicKey,
Remove: true,
}
hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, relayPeer)
}
continue
}
hostPeerUpdate = SetDefaultGw(node, hostPeerUpdate)
if !hostPeerUpdate.IsInternetGw {
hostPeerUpdate.IsInternetGw = IsInternetGw(node)
@ -259,6 +206,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
hostPeerUpdate.FwUpdate.AllowedNetworks = append(hostPeerUpdate.FwUpdate.AllowedNetworks, node.NetworkRange6)
}
} else {
networkAllowAll = false
hostPeerUpdate.FwUpdate.AllowAll = false
rules := GetAclRulesForNode(&node)
if len(hostPeerUpdate.FwUpdate.AclRules) == 0 {
@ -370,7 +318,6 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
}
if uselocal {
peerConfig.Endpoint.IP = peer.LocalAddress.IP
peerConfig.Endpoint.Port = peerHost.ListenPort
}
var allowedToComm bool
@ -438,7 +385,6 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
IngressID: node.ID.String(),
Network: node.NetworkRange,
Network6: node.NetworkRange6,
AllowAll: defaultDevicePolicy.Enabled && defaultUserPolicy.Default,
StaticNodeIps: GetStaticNodeIps(node),
Rules: GetFwRulesOnIngressGateway(node),
}
@ -472,10 +418,23 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
IP: node.Address6.IP,
Mask: getCIDRMaskFromAddr(node.Address6.IP.String()),
},
EgressGWCfg: node.EgressGatewayRequest,
EgressGWCfg: node.EgressGatewayRequest,
EgressFwRules: make(map[string]models.AclRule),
}
}
if node.IsEgressGateway {
if !networkAllowAll {
egressInfo := hostPeerUpdate.FwUpdate.EgressInfo[node.ID.String()]
if egressInfo.EgressFwRules == nil {
egressInfo.EgressFwRules = make(map[string]models.AclRule)
}
egressInfo.EgressFwRules = GetEgressRulesForNode(node)
hostPeerUpdate.FwUpdate.EgressInfo[node.ID.String()] = egressInfo
}
}
if IsInternetGw(node) {
hostPeerUpdate.FwUpdate.IsEgressGw = true
egressrange := []string{"0.0.0.0/0"}

View file

@ -278,7 +278,7 @@ func CheckIDSyntax(id string) error {
}
func CreateDefaultTags(netID models.NetworkID) {
// create tag for remote access gws in the network
// create tag for gws in the network
tag := models.Tag{
ID: models.TagID(fmt.Sprintf("%s.%s", netID.String(), models.GwTagName)),
TagName: models.GwTagName,
@ -292,7 +292,7 @@ func CreateDefaultTags(netID models.NetworkID) {
}
err = InsertTag(tag)
if err != nil {
slog.Error("failed to create remote access gw tag", "error", err.Error())
slog.Error("failed to create gw tag", "error", err.Error())
return
}
}

View file

@ -468,14 +468,14 @@ func migrateToGws() {
upsert := false
for i, srcI := range acl.Src {
if srcI.ID == models.NodeTagID && srcI.Value == fmt.Sprintf("%s.%s", acl.NetworkID.String(), models.OldRemoteAccessTagName) {
srcI.Value = models.GwTagName
srcI.Value = fmt.Sprintf("%s.%s", acl.NetworkID.String(), models.GwTagName)
acl.Src[i] = srcI
upsert = true
}
}
for i, dstI := range acl.Dst {
if dstI.ID == models.NodeTagID && dstI.Value == fmt.Sprintf("%s.%s", acl.NetworkID.String(), models.OldRemoteAccessTagName) {
dstI.Value = models.GwTagName
dstI.Value = fmt.Sprintf("%s.%s", acl.NetworkID.String(), models.GwTagName)
acl.Dst[i] = dstI
upsert = true
}

View file

@ -117,5 +117,7 @@ type AclRule struct {
AllowedProtocol Protocol `json:"allowed_protocols"` // tcp, udp, etc.
AllowedPorts []string `json:"allowed_ports"`
Direction AllowedTrafficDirection `json:"direction"` // single or two-way
Dst []net.IPNet `json:"dst"`
Dst6 []net.IPNet `json:"dst6"`
Allowed bool
}

View file

@ -60,6 +60,7 @@ func (ext *ExtClient) ConvertToStaticNode() Node {
Tags: ext.Tags,
IsStatic: true,
StaticNode: *ext,
IsUserNode: ext.RemoteAccessClientID != "",
Mutex: ext.Mutex,
}
}

View file

@ -53,7 +53,6 @@ type IngressInfo struct {
Network6 net.IPNet `json:"network6"`
StaticNodeIps []net.IP `json:"static_node_ips"`
Rules []FwRule `json:"rules"`
AllowAll bool `json:"allow_all"`
EgressRanges []net.IPNet `json:"egress_ranges"`
EgressRanges6 []net.IPNet `json:"egress_ranges6"`
}
@ -66,6 +65,7 @@ type EgressInfo struct {
Network6 net.IPNet `json:"network6" yaml:"network6"`
EgressGwAddr6 net.IPNet `json:"egress_gw_addr6" yaml:"egress_gw_addr6"`
EgressGWCfg EgressGatewayRequest `json:"egress_gateway_cfg" yaml:"egress_gateway_cfg"`
EgressFwRules map[string]AclRule `json:"egress_fw_rules"`
}
// EgressNetworkRoutes - struct for egress network routes for adding routes to peer's interface

View file

@ -98,7 +98,7 @@ var oauthNotConfigured = fmt.Sprintf(htmlBaseTemplate, `<h2>Your Netmaker server
var oauthStateInvalid = fmt.Sprintf(htmlBaseTemplate, `<h2>Invalid OAuth Session. Please re-try again.</h2>`)
var userNotAllowed = fmt.Sprintf(htmlBaseTemplate, `<h2>Your account does not have access to the dashboard. Please contact your administrator for more information about your account.</h2>
<p>Non-Admins can access the netmaker networks using <a href="https://docs.netmaker.io/docs/remote-access-client-rac#downloadinstallation" target="_blank" rel="noopener">our Remote Access Client.</a></p>`)
<p>Non-Admins can access the netmaker networks using <a href="https://docs.netmaker.io/docs/remote-access-client-rac#downloadinstallation" target="_blank" rel="noopener">our Netmaker Desktop App.</a></p>`)
var userFirstTimeSignUp = fmt.Sprintf(htmlBaseTemplate, `<h2>Thank you for signing up. Please contact your administrator for access.</h2>`)

View file

@ -8,7 +8,9 @@ import (
"net/http"
"net/url"
"strings"
"time"
"github.com/google/uuid"
"github.com/gorilla/mux"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
@ -411,6 +413,44 @@ func createUserGroup(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
networks, err := logic.GetNetworks()
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
for _, network := range networks {
acl := models.Acl{
ID: uuid.New().String(),
Name: fmt.Sprintf("%s group", userGroupReq.Group.Name),
MetaData: "This Policy allows user group to communicate with all gateways",
Default: true,
ServiceType: models.Any,
NetworkID: models.NetworkID(network.NetID),
Proto: models.ALL,
RuleType: models.UserPolicy,
Src: []models.AclPolicyTag{
{
ID: models.UserGroupAclID,
Value: userGroupReq.Group.ID.String(),
},
},
Dst: []models.AclPolicyTag{
{
ID: models.NodeTagID,
Value: fmt.Sprintf("%s.%s", models.NetworkID(network.NetID), models.GwTagName),
}},
AllowedDirection: models.TrafficDirectionUni,
Enabled: true,
CreatedBy: "auto",
CreatedAt: time.Now().UTC(),
}
err = logic.InsertAcl(acl)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
}
for _, userID := range userGroupReq.Members {
user, err := logic.GetUser(userID)
if err != nil {

View file

@ -2,6 +2,7 @@ package email
import (
"fmt"
"github.com/gravitl/netmaker/models"
proLogic "github.com/gravitl/netmaker/pro/logic"
"github.com/gravitl/netmaker/servercfg"
@ -31,11 +32,11 @@ func (invite UserInvitedMail) GetBody(info Notification) string {
content := invite.BodyBuilder.
WithParagraph("Hi,").
WithParagraph("You've been invited to access a secure network via Netmaker's Remote Access Client (RAC). Follow these simple steps to get connected:").
WithParagraph("You've been invited to access a secure network via Netmaker Desktop App. Follow these simple steps to get connected:").
WithHtml("<ol>").
WithHtml(fmt.Sprintf("<li>Click <a href=\"%s\">here</a> to accept your invitation and setup your account.</li>", invite.InviteURL)).
WithHtml("<br>").
WithHtml(fmt.Sprintf("<li><a href=\"%s\">Download the Remote Access Client (RAC)</a>.</li>", downloadLink))
WithHtml(fmt.Sprintf("<li><a href=\"%s\">Download the Netmaker Desktop App</a>.</li>", downloadLink))
if invite.PlatformRoleID == models.AdminRole.String() || invite.PlatformRoleID == models.PlatformUser.String() {
content = content.

View file

@ -40,7 +40,7 @@ var NetworkAdminAllPermissionTemplate = models.UserRolePermissionTemplate{
var NetworkUserAllPermissionTemplate = models.UserRolePermissionTemplate{
ID: models.UserRoleID(fmt.Sprintf("global-%s", models.NetworkUser)),
Name: "Network Users",
MetaData: "Can connect to nodes in your networks via Remote Access Client.",
MetaData: "Can connect to nodes in your networks via Netmaker Desktop App.",
Default: true,
FullAccess: false,
NetworkID: models.AllNetworks,
@ -131,7 +131,7 @@ func UserGroupsInit() {
models.UserRoleID(fmt.Sprintf("global-%s", models.NetworkUser)): {},
},
},
MetaData: "Provides read-only dashboard access to platform users and allows connection to network nodes via the Remote Access Client.",
MetaData: "Provides read-only dashboard access to platform users and allows connection to network nodes via the Netmaker Desktop App.",
}
d, _ := json.Marshal(NetworkGlobalAdminGroup)
database.Insert(NetworkGlobalAdminGroup.ID.String(), string(d), database.USER_GROUPS_TABLE_NAME)
@ -156,7 +156,7 @@ func CreateDefaultNetworkRolesAndGroups(netID models.NetworkID) {
var NetworkUserPermissionTemplate = models.UserRolePermissionTemplate{
ID: models.UserRoleID(fmt.Sprintf("%s-%s", netID, models.NetworkUser)),
Name: fmt.Sprintf("%s User", netID),
MetaData: fmt.Sprintf("Can connect to nodes in your network `%s` via Remote Access Client.", netID),
MetaData: fmt.Sprintf("Can connect to nodes in your network `%s` via Netmaker Desktop App.", netID),
Default: true,
FullAccess: false,
NetworkID: netID,
@ -235,7 +235,7 @@ func CreateDefaultNetworkRolesAndGroups(netID models.NetworkID) {
models.UserRoleID(fmt.Sprintf("%s-%s", netID, models.NetworkUser)): {},
},
},
MetaData: fmt.Sprintf("Can connect to nodes in your network `%s` via Remote Access Client. Platform users will have read-only access to the the dashboard.", netID),
MetaData: fmt.Sprintf("Can connect to nodes in your network `%s` via Netmaker Desktop App. Platform users will have read-only access to the the dashboard.", netID),
}
d, _ = json.Marshal(NetworkAdminGroup)
database.Insert(NetworkAdminGroup.ID.String(), string(d), database.USER_GROUPS_TABLE_NAME)
@ -810,87 +810,7 @@ func GetUserNetworkRolesWithRemoteVPNAccess(user models.User) (gwAccess map[mode
}
func GetFilteredNodesByUserAccess(user models.User, nodes []models.Node) (filteredNodes []models.Node) {
nodesMap := make(map[string]struct{})
allNetworkRoles := make(map[models.UserRoleID]struct{})
defer func() {
filteredNodes = logic.AddStaticNodestoList(filteredNodes)
}()
if len(user.NetworkRoles) > 0 {
for _, netRoles := range user.NetworkRoles {
for netRoleI := range netRoles {
allNetworkRoles[netRoleI] = struct{}{}
}
}
}
if _, ok := user.NetworkRoles[models.AllNetworks]; ok {
filteredNodes = nodes
return
}
if len(user.UserGroups) > 0 {
for userGID := range user.UserGroups {
userG, err := GetUserGroup(userGID)
if err == nil {
if len(userG.NetworkRoles) > 0 {
if _, ok := userG.NetworkRoles[models.AllNetworks]; ok {
filteredNodes = nodes
return
}
for _, netRoles := range userG.NetworkRoles {
for netRoleI := range netRoles {
allNetworkRoles[netRoleI] = struct{}{}
}
}
}
}
}
}
for networkRoleID := range allNetworkRoles {
userPermTemplate, err := logic.GetRole(networkRoleID)
if err != nil {
continue
}
networkNodes := logic.GetNetworkNodesMemory(nodes, userPermTemplate.NetworkID.String())
if userPermTemplate.FullAccess {
for _, node := range networkNodes {
if _, ok := nodesMap[node.ID.String()]; ok {
continue
}
nodesMap[node.ID.String()] = struct{}{}
filteredNodes = append(filteredNodes, node)
}
continue
}
if rsrcPerms, ok := userPermTemplate.NetworkLevelAccess[models.RemoteAccessGwRsrc]; ok {
if _, ok := rsrcPerms[models.AllRemoteAccessGwRsrcID]; ok {
for _, node := range networkNodes {
if _, ok := nodesMap[node.ID.String()]; ok {
continue
}
if node.IsIngressGateway {
nodesMap[node.ID.String()] = struct{}{}
filteredNodes = append(filteredNodes, node)
}
}
} else {
for gwID, scope := range rsrcPerms {
if _, ok := nodesMap[gwID.String()]; ok {
continue
}
if scope.Read {
gwNode, err := logic.GetNodeByID(gwID.String())
if err == nil && gwNode.IsIngressGateway {
nodesMap[gwNode.ID.String()] = struct{}{}
filteredNodes = append(filteredNodes, gwNode)
}
}
}
}
}
}
return
return filteredNodes
}
func FilterNetworksByRole(allnetworks []models.Network, user models.User) []models.Network {
@ -1211,7 +1131,7 @@ func CreateDefaultUserPolicies(netID models.NetworkID) {
defaultUserAcl := models.Acl{
ID: fmt.Sprintf("%s.%s-grp", netID, models.NetworkAdmin),
Name: "Network Admin",
MetaData: "This Policy allows all network admins to communicate with all remote access gateways",
MetaData: "This Policy allows all network admins to communicate with all gateways",
Default: true,
ServiceType: models.Any,
NetworkID: netID,
@ -1244,7 +1164,7 @@ func CreateDefaultUserPolicies(netID models.NetworkID) {
defaultUserAcl := models.Acl{
ID: fmt.Sprintf("%s.%s-grp", netID, models.NetworkUser),
Name: "Network User",
MetaData: "This Policy allows all network users to communicate with all remote access gateways",
MetaData: "This Policy allows all network users to communicate with all gateways",
Default: true,
ServiceType: models.Any,
NetworkID: netID,