Explorar el Código

Merge pull request #3168 from gravitl/NET-1615-ext

NET-1615: ExtClients Acls
Abhishek K hace 10 meses
padre
commit
505f31e7e9
Se han modificado 9 ficheros con 216 adiciones y 9 borrados
  1. 1 2
      controllers/ext_client.go
  2. 3 0
      controllers/node.go
  3. 3 0
      controllers/user.go
  4. 16 0
      logic/acls.go
  5. 152 1
      logic/extpeers.go
  6. 1 1
      logic/nodes.go
  7. 16 1
      logic/peers.go
  8. 16 4
      models/mqtt.go
  9. 8 0
      pro/logic/user_mgmt.go

+ 1 - 2
controllers/ext_client.go

@@ -452,6 +452,7 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 	extclient.OwnerID = userName
 	extclient.RemoteAccessClientID = customExtClient.RemoteAccessClientID
 	extclient.IngressGatewayID = nodeid
+	extclient.Network = node.Network
 	extclient.Tags = make(map[models.TagID]struct{})
 	extclient.Tags[models.TagID(fmt.Sprintf("%s.%s", extclient.Network,
 		models.RemoteAccessTagName))] = struct{}{}
@@ -459,8 +460,6 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 	if (extclient.DNS == "") && (node.IngressDNS != "") {
 		extclient.DNS = node.IngressDNS
 	}
-
-	extclient.Network = node.Network
 	host, err := logic.GetHost(node.HostID.String())
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),

+ 3 - 0
controllers/node.go

@@ -590,6 +590,7 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
 		if err := mq.NodeUpdate(&node); err != nil {
 			slog.Error("error publishing node update to node", "node", node.ID, "error", err)
 		}
+		mq.PublishPeerUpdate(false)
 	}()
 }
 
@@ -634,6 +635,7 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 				if err := mq.PublishSingleHostPeerUpdate(host, allNodes, nil, removedClients[:], false, nil); err != nil {
 					slog.Error("publishSingleHostUpdate", "host", host.Name, "error", err)
 				}
+				mq.PublishPeerUpdate(false)
 				if err := mq.NodeUpdate(&node); err != nil {
 					slog.Error(
 						"error publishing node update to node",
@@ -749,6 +751,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 				logger.Log(0, "error during node ACL update for node", newNode.ID.String())
 			}
 		}
+		mq.PublishPeerUpdate(false)
 		if servercfg.IsDNSMode() {
 			logic.SetDNS()
 		}

+ 3 - 0
controllers/user.go

@@ -451,6 +451,7 @@ func createUser(w http.ResponseWriter, r *http.Request) {
 	}
 	logic.DeleteUserInvite(user.UserName)
 	logic.DeletePendingUser(user.UserName)
+	go mq.PublishPeerUpdate(false)
 	slog.Info("user was created", "username", user.UserName)
 	json.NewEncoder(w).Encode(logic.ToReturnUser(user))
 }
@@ -590,6 +591,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 	}
+	go mq.PublishPeerUpdate(false)
 	logger.Log(1, username, "was updated")
 	json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
 }
@@ -692,6 +694,7 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
 				}
 			}
 		}
+		mq.PublishPeerUpdate(false)
 		if servercfg.IsDNSMode() {
 			logic.SetDNS()
 		}

+ 16 - 0
logic/acls.go

@@ -441,16 +441,26 @@ 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 {
+	acl, _ := GetDefaultPolicy(models.NetworkID(peer.Network), models.UserPolicy)
+	if acl.Enabled {
+		return true
+	}
 	user, err := GetUser(userName)
 	if err != nil {
 		return false
 	}
+	if peer.IsStatic {
+		peer = peer.StaticNode.ConvertToStaticNode()
+	}
 	policies := listPoliciesOfUser(*user, models.NetworkID(peer.Network))
 	for _, policy := range policies {
 		if !policy.Enabled {
 			continue
 		}
 		dstMap := convAclTagToValueMap(policy.Dst)
+		if _, ok := dstMap["*"]; ok {
+			return true
+		}
 		for tagID := range peer.Tags {
 			if _, ok := dstMap[tagID.String()]; ok {
 				return true
@@ -470,6 +480,12 @@ func IsNodeAllowedToCommunicate(node, peer models.Node) bool {
 			return true
 		}
 	}
+	if node.IsStatic {
+		node = node.StaticNode.ConvertToStaticNode()
+	}
+	if peer.IsStatic {
+		peer = peer.StaticNode.ConvertToStaticNode()
+	}
 	// list device policies
 	policies := listDevicePolicies(models.NetworkID(peer.Network))
 	for _, policy := range policies {

+ 152 - 1
logic/extpeers.go

@@ -136,6 +136,12 @@ func DeleteExtClientAndCleanup(extClient models.ExtClient) error {
 	return nil
 }
 
+//TODO - enforce extclient-to-extclient on ingress gw
+/* 1. fetch all non-user static nodes
+a. check against each user node, if allowed add rule
+
+*/
+
 // GetNetworkExtClients - gets the ext clients of given network
 func GetNetworkExtClients(network string) ([]models.ExtClient, error) {
 	var extclients []models.ExtClient
@@ -396,6 +402,124 @@ func ToggleExtClientConnectivity(client *models.ExtClient, enable bool) (models.
 	return newClient, nil
 }
 
+func GetStaticNodeIps(node models.Node) (ips []net.IP) {
+	extclients := GetStaticNodesByNetwork(models.NetworkID(node.Network), false)
+	for _, extclient := range extclients {
+		if extclient.StaticNode.Address != "" {
+			ips = append(ips, extclient.StaticNode.AddressIPNet4().IP)
+		}
+		if extclient.StaticNode.Address6 != "" {
+			ips = append(ips, extclient.StaticNode.AddressIPNet6().IP)
+		}
+	}
+	return
+}
+
+func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) {
+	// fetch user access to static clients via policies
+
+	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 IsUserAllowedToCommunicate(userNodeI.StaticNode.OwnerID, peer) {
+				if peer.IsStatic {
+					if userNodeI.StaticNode.Address != "" {
+						rules = append(rules, models.FwRule{
+							SrcIp: userNodeI.StaticNode.AddressIPNet4().IP,
+							DstIP: peer.StaticNode.AddressIPNet4().IP,
+							Allow: true,
+						})
+						rules = append(rules, models.FwRule{
+							SrcIp: peer.StaticNode.AddressIPNet4().IP,
+							DstIP: userNodeI.StaticNode.AddressIPNet4().IP,
+							Allow: true,
+						})
+					}
+					if userNodeI.StaticNode.Address6 != "" {
+						rules = append(rules, models.FwRule{
+							SrcIp: userNodeI.StaticNode.AddressIPNet6().IP,
+							DstIP: peer.StaticNode.AddressIPNet6().IP,
+							Allow: true,
+						})
+						rules = append(rules, models.FwRule{
+							SrcIp: peer.StaticNode.AddressIPNet6().IP,
+							DstIP: userNodeI.StaticNode.AddressIPNet6().IP,
+							Allow: true,
+						})
+					}
+				} else {
+					if userNodeI.StaticNode.Address != "" {
+						rules = append(rules, models.FwRule{
+							SrcIp: userNodeI.StaticNode.AddressIPNet4().IP,
+							DstIP: peer.Address.IP,
+							Allow: true,
+						})
+					}
+					if userNodeI.StaticNode.Address6 != "" {
+						rules = append(rules, models.FwRule{
+							SrcIp: userNodeI.StaticNode.AddressIPNet6().IP,
+							DstIP: peer.Address6.IP,
+							Allow: true,
+						})
+					}
+				}
+
+			}
+		}
+	}
+
+	for _, nodeI := range nodes {
+		if !nodeI.IsStatic || nodeI.IsUserNode {
+			continue
+		}
+		for _, peer := range nodes {
+			if peer.StaticNode.ClientID == nodeI.StaticNode.ClientID || peer.IsUserNode {
+				continue
+			}
+			if IsNodeAllowedToCommunicate(nodeI, peer) {
+				if peer.IsStatic {
+					if nodeI.StaticNode.Address != "" {
+						rules = append(rules, models.FwRule{
+							SrcIp: nodeI.StaticNode.AddressIPNet4().IP,
+							DstIP: peer.StaticNode.AddressIPNet4().IP,
+							Allow: true,
+						})
+					}
+					if nodeI.StaticNode.Address6 != "" {
+						rules = append(rules, models.FwRule{
+							SrcIp: nodeI.StaticNode.AddressIPNet6().IP,
+							DstIP: peer.StaticNode.AddressIPNet6().IP,
+							Allow: true,
+						})
+					}
+				} else {
+					if nodeI.StaticNode.Address != "" {
+						rules = append(rules, models.FwRule{
+							SrcIp: nodeI.StaticNode.AddressIPNet4().IP,
+							DstIP: peer.Address.IP,
+							Allow: true,
+						})
+					}
+					if nodeI.StaticNode.Address6 != "" {
+						rules = append(rules, models.FwRule{
+							SrcIp: nodeI.StaticNode.AddressIPNet6().IP,
+							DstIP: peer.Address6.IP,
+							Allow: true,
+						})
+					}
+				}
+
+			}
+		}
+	}
+	return
+}
+
 func GetExtPeers(node, peer *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, []models.EgressNetworkRoutes, error) {
 	var peers []wgtypes.PeerConfig
 	var idsAndAddr []models.IDandAddr
@@ -503,6 +627,9 @@ func getExtpeersExtraRoutes(node models.Node, network string) (egressRoutes []mo
 		if len(extPeer.ExtraAllowedIPs) == 0 {
 			continue
 		}
+		if !IsNodeAllowedToCommunicate(extPeer.ConvertToStaticNode(), node) {
+			continue
+		}
 		egressRoutes = append(egressRoutes, getExtPeerEgressRoute(node, extPeer)...)
 	}
 	return
@@ -540,13 +667,37 @@ func GetExtclientAllowedIPs(client models.ExtClient) (allowedIPs []string) {
 	return
 }
 
-func GetStaticNodesByNetwork(network models.NetworkID) (staticNode []models.Node) {
+func GetStaticUserNodesByNetwork(network models.NetworkID) (staticNode []models.Node) {
+	extClients, err := GetAllExtClients()
+	if err != nil {
+		return
+	}
+	for _, extI := range extClients {
+		if extI.Network == network.String() {
+			if extI.RemoteAccessClientID != "" {
+				n := models.Node{
+					IsStatic:   true,
+					StaticNode: extI,
+					IsUserNode: extI.RemoteAccessClientID != "",
+				}
+				staticNode = append(staticNode, n)
+			}
+		}
+	}
+
+	return
+}
+
+func GetStaticNodesByNetwork(network models.NetworkID, onlyWg bool) (staticNode []models.Node) {
 	extClients, err := GetAllExtClients()
 	if err != nil {
 		return
 	}
 	for _, extI := range extClients {
 		if extI.Network == network.String() {
+			if onlyWg && extI.RemoteAccessClientID != "" {
+				continue
+			}
 			n := models.Node{
 				IsStatic:   true,
 				StaticNode: extI,

+ 1 - 1
logic/nodes.go

@@ -385,7 +385,7 @@ func AddStaticNodestoList(nodes []models.Node) []models.Node {
 			continue
 		}
 		if node.IsIngressGateway {
-			nodes = append(nodes, GetStaticNodesByNetwork(models.NetworkID(node.Network))...)
+			nodes = append(nodes, GetStaticNodesByNetwork(models.NetworkID(node.Network), false)...)
 			netMap[node.Network] = struct{}{}
 		}
 	}

+ 16 - 1
logic/peers.go

@@ -74,7 +74,8 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 		ServerVersion: servercfg.GetVersion(),
 		ServerAddrs:   []models.ServerAddr{},
 		FwUpdate: models.FwUpdate{
-			EgressInfo: make(map[string]models.EgressInfo),
+			EgressInfo:  make(map[string]models.EgressInfo),
+			IngressInfo: make(map[string]models.IngressInfo),
 		},
 		PeerIDs:           make(models.PeerMap, 0),
 		Peers:             []wgtypes.PeerConfig{},
@@ -288,8 +289,22 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 		var extPeerIDAndAddrs []models.IDandAddr
 		var egressRoutes []models.EgressNetworkRoutes
 		if node.IsIngressGateway {
+			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(),
+						Network:       node.NetworkRange,
+						Network6:      node.NetworkRange6,
+						AllowAll:      defaultDevicePolicy.Enabled && defaultUserPolicy.Default,
+						StaticNodeIps: GetStaticNodeIps(node),
+						Rules:         GetFwRulesOnIngressGateway(node),
+					}
+					hostPeerUpdate.FwUpdate.IngressInfo[node.ID.String()] = ingFwUpdate
+				}
 				hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, egressRoutes...)
 				hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, extPeers...)
 				for _, extPeerIdAndAddr := range extPeerIDAndAddrs {

+ 16 - 4
models/mqtt.go

@@ -26,10 +26,20 @@ type HostPeerUpdate struct {
 	EndpointDetection bool                  `json:"endpoint_detection"`
 }
 
+type FwRule struct {
+	SrcIp net.IP
+	DstIP net.IP
+	Allow bool
+}
+
 // IngressInfo - struct for ingress info
 type IngressInfo struct {
-	ExtPeers     map[string]ExtClientInfo `json:"ext_peers" yaml:"ext_peers"`
-	EgressRanges []string                 `json:"egress_ranges" yaml:"egress_ranges"`
+	IngressID     string    `json:"ingress_id"`
+	Network       net.IPNet `json:"network"`
+	Network6      net.IPNet `json:"network6"`
+	StaticNodeIps []net.IP  `json:"static_node_ips"`
+	Rules         []FwRule  `json:"rules"`
+	AllowAll      bool      `json:"allow_all"`
 }
 
 // EgressInfo - struct for egress info
@@ -77,8 +87,10 @@ type KeyUpdate struct {
 
 // FwUpdate - struct for firewall updates
 type FwUpdate struct {
-	IsEgressGw bool                  `json:"is_egress_gw"`
-	EgressInfo map[string]EgressInfo `json:"egress_info"`
+	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"`
 }
 
 // FailOverMeReq - struct for failover req

+ 8 - 0
pro/logic/user_mgmt.go

@@ -519,6 +519,14 @@ func GetUserRAGNodesV1(user models.User) (gws map[string]models.Node) {
 	if err != nil {
 		return
 	}
+	if user.IsAdmin || user.IsSuperAdmin {
+		for _, node := range nodes {
+			if node.IsIngressGateway {
+				gws[node.ID.String()] = node
+			}
+
+		}
+	}
 	tagNodesMap := logic.GetTagMapWithNodes()
 	accessPolices := logic.ListUserPolicies(user)
 	for _, policyI := range accessPolices {