mirror of
				https://github.com/gravitl/netmaker.git
				synced 2025-10-31 20:22:44 +08:00 
			
		
		
		
	resolve merge conflicts
This commit is contained in:
		| @@ -17,6 +17,17 @@ var extClientConfigCmd = &cobra.Command{ | |||||||
| 	}, | 	}, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | var extClientHAConfigCmd = &cobra.Command{ | ||||||
|  | 	Use:   "auto_config [NETWORK NAME]", | ||||||
|  | 	Args:  cobra.ExactArgs(1), | ||||||
|  | 	Short: "Get an External Client Configuration", | ||||||
|  | 	Long:  `Get an External Client Configuration`, | ||||||
|  | 	Run: func(cmd *cobra.Command, args []string) { | ||||||
|  | 		fmt.Println(functions.GetExtClientHAConfig(args[0])) | ||||||
|  | 	}, | ||||||
|  | } | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	rootCmd.AddCommand(extClientConfigCmd) | 	rootCmd.AddCommand(extClientConfigCmd) | ||||||
|  | 	rootCmd.AddCommand(extClientHAConfigCmd) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -27,6 +27,11 @@ func GetExtClientConfig(networkName, clientID string) string { | |||||||
| 	return get(fmt.Sprintf("/api/extclients/%s/%s/file", networkName, clientID)) | 	return get(fmt.Sprintf("/api/extclients/%s/%s/file", networkName, clientID)) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // GetExtClientConfig - auto fetch a client config | ||||||
|  | func GetExtClientHAConfig(networkName string) string { | ||||||
|  | 	return get(fmt.Sprintf("/api/v1/client_conf/%s", networkName)) | ||||||
|  | } | ||||||
|  |  | ||||||
| // CreateExtClient - create an external client | // CreateExtClient - create an external client | ||||||
| func CreateExtClient(networkName, nodeID string, extClient models.CustomExtClient) { | func CreateExtClient(networkName, nodeID string, extClient models.CustomExtClient) { | ||||||
| 	request[any](http.MethodPost, fmt.Sprintf("/api/extclients/%s/%s", networkName, nodeID), extClient) | 	request[any](http.MethodPost, fmt.Sprintf("/api/extclients/%s/%s", networkName, nodeID), extClient) | ||||||
|   | |||||||
| @@ -42,6 +42,7 @@ func extClientHandlers(r *mux.Router) { | |||||||
| 		Methods(http.MethodDelete) | 		Methods(http.MethodDelete) | ||||||
| 	r.HandleFunc("/api/extclients/{network}/{nodeid}", logic.SecurityCheck(false, checkFreeTierLimits(limitChoiceMachines, http.HandlerFunc(createExtClient)))). | 	r.HandleFunc("/api/extclients/{network}/{nodeid}", logic.SecurityCheck(false, checkFreeTierLimits(limitChoiceMachines, http.HandlerFunc(createExtClient)))). | ||||||
| 		Methods(http.MethodPost) | 		Methods(http.MethodPost) | ||||||
|  | 	r.HandleFunc("/api/v1/client_conf/{network}", logic.SecurityCheck(false, http.HandlerFunc(getExtClientHAConf))).Methods(http.MethodGet) | ||||||
| } | } | ||||||
|  |  | ||||||
| func checkIngressExists(nodeID string) bool { | func checkIngressExists(nodeID string) bool { | ||||||
| @@ -387,6 +388,251 @@ Endpoint = %s | |||||||
| 	json.NewEncoder(w).Encode(client) | 	json.NewEncoder(w).Encode(client) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // @Summary     Get an individual remote access client | ||||||
|  | // @Router      /api/extclients/{network}/{clientid}/{type} [get] | ||||||
|  | // @Tags        Remote Access Client | ||||||
|  | // @Security    oauth2 | ||||||
|  | // @Success     200 {object} models.ExtClient | ||||||
|  | // @Failure     500 {object} models.ErrorResponse | ||||||
|  | // @Failure     403 {object} models.ErrorResponse | ||||||
|  | func getExtClientHAConf(w http.ResponseWriter, r *http.Request) { | ||||||
|  |  | ||||||
|  | 	var params = mux.Vars(r) | ||||||
|  | 	networkid := params["network"] | ||||||
|  | 	network, err := logic.GetParentNetwork(networkid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		logger.Log( | ||||||
|  | 			1, | ||||||
|  | 			r.Header.Get("user"), | ||||||
|  | 			"Could not retrieve Ingress Gateway Network", | ||||||
|  | 			networkid, | ||||||
|  | 		) | ||||||
|  | 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	// fetch client based on availability | ||||||
|  | 	nodes, _ := logic.GetNetworkNodes(networkid) | ||||||
|  | 	defaultPolicy, _ := logic.GetDefaultPolicy(models.NetworkID(networkid), models.DevicePolicy) | ||||||
|  | 	var targetGwID string | ||||||
|  | 	var connectionCnt int = -1 | ||||||
|  | 	for _, nodeI := range nodes { | ||||||
|  | 		if nodeI.IsGw { | ||||||
|  | 			// check health status | ||||||
|  | 			logic.GetNodeStatus(&nodeI, defaultPolicy.Enabled) | ||||||
|  | 			if nodeI.Status != models.OnlineSt { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			// Get Total connections on the gw | ||||||
|  | 			clients := logic.GetGwExtclients(nodeI.ID.String(), networkid) | ||||||
|  |  | ||||||
|  | 			if connectionCnt == -1 || len(clients) < connectionCnt { | ||||||
|  | 				connectionCnt = len(clients) | ||||||
|  | 				targetGwID = nodeI.ID.String() | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	gwnode, err := logic.GetNodeByID(targetGwID) | ||||||
|  | 	if err != nil { | ||||||
|  | 		logger.Log( | ||||||
|  | 			0, | ||||||
|  | 			r.Header.Get("user"), | ||||||
|  | 			fmt.Sprintf( | ||||||
|  | 				"failed to get ingress gateway node [%s] info: %v", | ||||||
|  | 				gwnode.ID, | ||||||
|  | 				err, | ||||||
|  | 			), | ||||||
|  | 		) | ||||||
|  | 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	host, err := logic.GetHost(gwnode.HostID.String()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		logger.Log(0, r.Header.Get("user"), | ||||||
|  | 			fmt.Sprintf("failed to get ingress gateway host for node [%s] info: %v", gwnode.ID, err)) | ||||||
|  | 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var userName string | ||||||
|  | 	if r.Header.Get("ismaster") == "yes" { | ||||||
|  | 		userName = logic.MasterUser | ||||||
|  | 	} else { | ||||||
|  | 		caller, err := logic.GetUser(r.Header.Get("user")) | ||||||
|  | 		if err != nil { | ||||||
|  | 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		userName = caller.UserName | ||||||
|  | 	} | ||||||
|  | 	// create client | ||||||
|  | 	var extclient models.ExtClient | ||||||
|  | 	extclient.OwnerID = userName | ||||||
|  | 	extclient.IngressGatewayID = targetGwID | ||||||
|  | 	extclient.Network = networkid | ||||||
|  | 	extclient.Tags = make(map[models.TagID]struct{}) | ||||||
|  | 	// extclient.Tags[models.TagID(fmt.Sprintf("%s.%s", extclient.Network, | ||||||
|  | 	// 	models.RemoteAccessTagName))] = struct{}{} | ||||||
|  | 	// set extclient dns to ingressdns if extclient dns is not explicitly set | ||||||
|  | 	if (extclient.DNS == "") && (gwnode.IngressDNS != "") { | ||||||
|  | 		extclient.DNS = gwnode.IngressDNS | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	listenPort := logic.GetPeerListenPort(host) | ||||||
|  | 	extclient.IngressGatewayEndpoint = fmt.Sprintf("%s:%d", host.EndpointIP.String(), listenPort) | ||||||
|  | 	extclient.Enabled = true | ||||||
|  |  | ||||||
|  | 	if err = logic.CreateExtClient(&extclient); err != nil { | ||||||
|  | 		slog.Error( | ||||||
|  | 			"failed to create extclient", | ||||||
|  | 			"user", | ||||||
|  | 			r.Header.Get("user"), | ||||||
|  | 			"network", | ||||||
|  | 			networkid, | ||||||
|  | 			"error", | ||||||
|  | 			err, | ||||||
|  | 		) | ||||||
|  | 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	client, err := logic.GetExtClient(extclient.ClientID, networkid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	addrString := client.Address | ||||||
|  | 	if addrString != "" { | ||||||
|  | 		addrString += "/32" | ||||||
|  | 	} | ||||||
|  | 	if client.Address6 != "" { | ||||||
|  | 		if addrString != "" { | ||||||
|  | 			addrString += "," | ||||||
|  | 		} | ||||||
|  | 		addrString += client.Address6 + "/128" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	keepalive := "" | ||||||
|  | 	if network.DefaultKeepalive != 0 { | ||||||
|  | 		keepalive = "PersistentKeepalive = " + strconv.Itoa(int(network.DefaultKeepalive)) | ||||||
|  | 	} | ||||||
|  | 	if gwnode.IngressPersistentKeepalive != 0 { | ||||||
|  | 		keepalive = "PersistentKeepalive = " + strconv.Itoa(int(gwnode.IngressPersistentKeepalive)) | ||||||
|  | 	} | ||||||
|  | 	var newAllowedIPs string | ||||||
|  | 	if logic.IsInternetGw(gwnode) || gwnode.InternetGwID != "" { | ||||||
|  | 		egressrange := "0.0.0.0/0" | ||||||
|  | 		if gwnode.Address6.IP != nil && client.Address6 != "" { | ||||||
|  | 			egressrange += "," + "::/0" | ||||||
|  | 		} | ||||||
|  | 		newAllowedIPs = egressrange | ||||||
|  | 	} else { | ||||||
|  | 		newAllowedIPs = network.AddressRange | ||||||
|  | 		if newAllowedIPs != "" && network.AddressRange6 != "" { | ||||||
|  | 			newAllowedIPs += "," | ||||||
|  | 		} | ||||||
|  | 		if network.AddressRange6 != "" { | ||||||
|  | 			newAllowedIPs += network.AddressRange6 | ||||||
|  | 		} | ||||||
|  | 		if egressGatewayRanges, err := logic.GetEgressRangesOnNetwork(&client); err == nil { | ||||||
|  | 			for _, egressGatewayRange := range egressGatewayRanges { | ||||||
|  | 				newAllowedIPs += "," + egressGatewayRange | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	gwendpoint := "" | ||||||
|  | 	if host.EndpointIP.To4() == nil { | ||||||
|  | 		gwendpoint = fmt.Sprintf("[%s]:%d", host.EndpointIPv6.String(), host.ListenPort) | ||||||
|  | 	} else { | ||||||
|  | 		gwendpoint = fmt.Sprintf("%s:%d", host.EndpointIP.String(), host.ListenPort) | ||||||
|  | 	} | ||||||
|  | 	defaultDNS := "" | ||||||
|  | 	if client.DNS != "" { | ||||||
|  | 		defaultDNS = "DNS = " + client.DNS | ||||||
|  | 	} else if gwnode.IngressDNS != "" { | ||||||
|  | 		defaultDNS = "DNS = " + gwnode.IngressDNS | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	defaultMTU := 1420 | ||||||
|  | 	if host.MTU != 0 { | ||||||
|  | 		defaultMTU = host.MTU | ||||||
|  | 	} | ||||||
|  | 	if gwnode.IngressMTU != 0 { | ||||||
|  | 		defaultMTU = int(gwnode.IngressMTU) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	postUp := strings.Builder{} | ||||||
|  | 	if client.PostUp != "" && params["type"] != "qr" { | ||||||
|  | 		for _, loc := range strings.Split(client.PostUp, "\n") { | ||||||
|  | 			postUp.WriteString(fmt.Sprintf("PostUp = %s\n", loc)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	postDown := strings.Builder{} | ||||||
|  | 	if client.PostDown != "" && params["type"] != "qr" { | ||||||
|  | 		for _, loc := range strings.Split(client.PostDown, "\n") { | ||||||
|  | 			postDown.WriteString(fmt.Sprintf("PostDown = %s\n", loc)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	config := fmt.Sprintf(`[Interface] | ||||||
|  | Address = %s | ||||||
|  | PrivateKey = %s | ||||||
|  | MTU = %d | ||||||
|  | %s | ||||||
|  | %s | ||||||
|  | %s | ||||||
|  |  | ||||||
|  | [Peer] | ||||||
|  | PublicKey = %s | ||||||
|  | AllowedIPs = %s | ||||||
|  | Endpoint = %s | ||||||
|  | %s | ||||||
|  |  | ||||||
|  | `, addrString, | ||||||
|  | 		client.PrivateKey, | ||||||
|  | 		defaultMTU, | ||||||
|  | 		defaultDNS, | ||||||
|  | 		postUp.String(), | ||||||
|  | 		postDown.String(), | ||||||
|  | 		host.PublicKey, | ||||||
|  | 		newAllowedIPs, | ||||||
|  | 		gwendpoint, | ||||||
|  | 		keepalive, | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	go func() { | ||||||
|  | 		if err := logic.SetClientDefaultACLs(&extclient); err != nil { | ||||||
|  | 			slog.Error( | ||||||
|  | 				"failed to set default acls for extclient", | ||||||
|  | 				"user", | ||||||
|  | 				r.Header.Get("user"), | ||||||
|  | 				"network", | ||||||
|  | 				networkid, | ||||||
|  | 				"error", | ||||||
|  | 				err, | ||||||
|  | 			) | ||||||
|  | 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if err := mq.PublishPeerUpdate(false); err != nil { | ||||||
|  | 			logger.Log(1, "error publishing peer update ", err.Error()) | ||||||
|  | 		} | ||||||
|  | 		if servercfg.IsDNSMode() { | ||||||
|  | 			logic.SetDNS() | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	name := client.ClientID + ".conf" | ||||||
|  | 	w.Header().Set("Content-Type", "application/config") | ||||||
|  | 	w.Header().Set("Content-Disposition", "attachment; filename=\""+name+"\"") | ||||||
|  | 	w.WriteHeader(http.StatusOK) | ||||||
|  | 	_, err = fmt.Fprint(w, config) | ||||||
|  | 	if err != nil { | ||||||
|  | 		logger.Log(1, r.Header.Get("user"), "response writer error (file) ", err.Error()) | ||||||
|  | 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // @Summary     Create an individual remote access client | // @Summary     Create an individual remote access client | ||||||
| // @Router      /api/extclients/{network}/{nodeid} [post] | // @Router      /api/extclients/{network}/{nodeid} [post] | ||||||
| // @Tags        Remote Access Client | // @Tags        Remote Access Client | ||||||
|   | |||||||
| @@ -559,7 +559,7 @@ func createNetwork(w http.ResponseWriter, r *http.Request) { | |||||||
| 	logic.CreateDefaultNetworkRolesAndGroups(models.NetworkID(network.NetID)) | 	logic.CreateDefaultNetworkRolesAndGroups(models.NetworkID(network.NetID)) | ||||||
| 	logic.CreateDefaultAclNetworkPolicies(models.NetworkID(network.NetID)) | 	logic.CreateDefaultAclNetworkPolicies(models.NetworkID(network.NetID)) | ||||||
| 	logic.CreateDefaultTags(models.NetworkID(network.NetID)) | 	logic.CreateDefaultTags(models.NetworkID(network.NetID)) | ||||||
| 	//add new network to allocated ip map |  | ||||||
| 	go logic.AddNetworkToAllocatedIpMap(network.NetID) | 	go logic.AddNetworkToAllocatedIpMap(network.NetID) | ||||||
|  |  | ||||||
| 	go func() { | 	go func() { | ||||||
| @@ -640,6 +640,7 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	netNew := netOld | 	netNew := netOld | ||||||
|  | 	netNew.NameServers = payload.NameServers | ||||||
| 	netNew.DefaultACL = payload.DefaultACL | 	netNew.DefaultACL = payload.DefaultACL | ||||||
| 	_, _, _, err = logic.UpdateNetwork(&netOld, &netNew) | 	_, _, _, err = logic.UpdateNetwork(&netOld, &netNew) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -647,7 +648,7 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) { | |||||||
| 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) | 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | 	go mq.PublishPeerUpdate(false) | ||||||
| 	slog.Info("updated network", "network", payload.NetID, "user", r.Header.Get("user")) | 	slog.Info("updated network", "network", payload.NetID, "user", r.Header.Get("user")) | ||||||
| 	w.WriteHeader(http.StatusOK) | 	w.WriteHeader(http.StatusOK) | ||||||
| 	json.NewEncoder(w).Encode(payload) | 	json.NewEncoder(w).Encode(payload) | ||||||
|   | |||||||
| @@ -21,6 +21,10 @@ var linuxHost models.Host | |||||||
| func TestCreateEgressGateway(t *testing.T) { | func TestCreateEgressGateway(t *testing.T) { | ||||||
| 	var gateway models.EgressGatewayRequest | 	var gateway models.EgressGatewayRequest | ||||||
| 	gateway.Ranges = []string{"10.100.100.0/24"} | 	gateway.Ranges = []string{"10.100.100.0/24"} | ||||||
|  | 	gateway.RangesWithMetric = append(gateway.RangesWithMetric, models.EgressRangeMetric{ | ||||||
|  | 		Network:     "10.100.100.0/24", | ||||||
|  | 		RouteMetric: 256, | ||||||
|  | 	}) | ||||||
| 	gateway.NetID = "skynet" | 	gateway.NetID = "skynet" | ||||||
| 	deleteAllNetworks() | 	deleteAllNetworks() | ||||||
| 	createNet() | 	createNet() | ||||||
|   | |||||||
| @@ -17,7 +17,6 @@ import ( | |||||||
| var ( | var ( | ||||||
| 	aclCacheMutex = &sync.RWMutex{} | 	aclCacheMutex = &sync.RWMutex{} | ||||||
| 	aclCacheMap   = make(map[string]models.Acl) | 	aclCacheMap   = make(map[string]models.Acl) | ||||||
| 	aclTagsMutex  = &sync.RWMutex{} |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func MigrateAclPolicies() { | func MigrateAclPolicies() { | ||||||
| @@ -620,10 +619,22 @@ func IsPeerAllowed(node, peer models.Node, checkDefaultPolicy bool) bool { | |||||||
| 	} else { | 	} else { | ||||||
| 		peerId = peer.ID.String() | 		peerId = peer.ID.String() | ||||||
| 	} | 	} | ||||||
| 	aclTagsMutex.RLock() |  | ||||||
| 	peerTags := maps.Clone(peer.Tags) | 	var nodeTags, peerTags map[models.TagID]struct{} | ||||||
| 	nodeTags := maps.Clone(node.Tags) | 	if node.Mutex != nil { | ||||||
| 	aclTagsMutex.RUnlock() | 		node.Mutex.Lock() | ||||||
|  | 		nodeTags = maps.Clone(node.Tags) | ||||||
|  | 		node.Mutex.Unlock() | ||||||
|  | 	} else { | ||||||
|  | 		nodeTags = node.Tags | ||||||
|  | 	} | ||||||
|  | 	if peer.Mutex != nil { | ||||||
|  | 		peer.Mutex.Lock() | ||||||
|  | 		peerTags = maps.Clone(peer.Tags) | ||||||
|  | 		peer.Mutex.Unlock() | ||||||
|  | 	} else { | ||||||
|  | 		peerTags = peer.Tags | ||||||
|  | 	} | ||||||
| 	nodeTags[models.TagID(nodeId)] = struct{}{} | 	nodeTags[models.TagID(nodeId)] = struct{}{} | ||||||
| 	peerTags[models.TagID(peerId)] = struct{}{} | 	peerTags[models.TagID(peerId)] = struct{}{} | ||||||
| 	if checkDefaultPolicy { | 	if checkDefaultPolicy { | ||||||
| @@ -854,10 +865,21 @@ func IsNodeAllowedToCommunicateV1(node, peer models.Node, checkDefaultPolicy boo | |||||||
| 		peerId = peer.ID.String() | 		peerId = peer.ID.String() | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	aclTagsMutex.RLock() | 	var nodeTags, peerTags map[models.TagID]struct{} | ||||||
| 	peerTags := maps.Clone(peer.Tags) | 	if node.Mutex != nil { | ||||||
| 	nodeTags := maps.Clone(node.Tags) | 		node.Mutex.Lock() | ||||||
| 	aclTagsMutex.RUnlock() | 		nodeTags = maps.Clone(node.Tags) | ||||||
|  | 		node.Mutex.Unlock() | ||||||
|  | 	} else { | ||||||
|  | 		nodeTags = node.Tags | ||||||
|  | 	} | ||||||
|  | 	if peer.Mutex != nil { | ||||||
|  | 		peer.Mutex.Lock() | ||||||
|  | 		peerTags = maps.Clone(peer.Tags) | ||||||
|  | 		peer.Mutex.Unlock() | ||||||
|  | 	} else { | ||||||
|  | 		peerTags = peer.Tags | ||||||
|  | 	} | ||||||
| 	nodeTags[models.TagID(nodeId)] = struct{}{} | 	nodeTags[models.TagID(nodeId)] = struct{}{} | ||||||
| 	peerTags[models.TagID(peerId)] = struct{}{} | 	peerTags[models.TagID(peerId)] = struct{}{} | ||||||
| 	if checkDefaultPolicy { | 	if checkDefaultPolicy { | ||||||
| @@ -996,10 +1018,21 @@ func IsNodeAllowedToCommunicate(node, peer models.Node, checkDefaultPolicy bool) | |||||||
| 		peerId = peer.ID.String() | 		peerId = peer.ID.String() | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	aclTagsMutex.RLock() | 	var nodeTags, peerTags map[models.TagID]struct{} | ||||||
| 	peerTags := maps.Clone(peer.Tags) | 	if node.Mutex != nil { | ||||||
| 	nodeTags := maps.Clone(node.Tags) | 		node.Mutex.Lock() | ||||||
| 	aclTagsMutex.RUnlock() | 		nodeTags = maps.Clone(node.Tags) | ||||||
|  | 		node.Mutex.Unlock() | ||||||
|  | 	} else { | ||||||
|  | 		nodeTags = node.Tags | ||||||
|  | 	} | ||||||
|  | 	if peer.Mutex != nil { | ||||||
|  | 		peer.Mutex.Lock() | ||||||
|  | 		peerTags = maps.Clone(peer.Tags) | ||||||
|  | 		peer.Mutex.Unlock() | ||||||
|  | 	} else { | ||||||
|  | 		peerTags = peer.Tags | ||||||
|  | 	} | ||||||
| 	nodeTags[models.TagID(nodeId)] = struct{}{} | 	nodeTags[models.TagID(nodeId)] = struct{}{} | ||||||
| 	peerTags[models.TagID(peerId)] = struct{}{} | 	peerTags[models.TagID(peerId)] = struct{}{} | ||||||
| 	if checkDefaultPolicy { | 	if checkDefaultPolicy { | ||||||
| @@ -1222,13 +1255,20 @@ func getUserAclRulesForNode(targetnode *models.Node, | |||||||
| 	userGrpMap := GetUserGrpMap() | 	userGrpMap := GetUserGrpMap() | ||||||
| 	allowedUsers := make(map[string][]models.Acl) | 	allowedUsers := make(map[string][]models.Acl) | ||||||
| 	acls := listUserPolicies(models.NetworkID(targetnode.Network)) | 	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) | ||||||
|  | 	} | ||||||
| 	for _, acl := range acls { | 	for _, acl := range acls { | ||||||
| 		if !acl.Enabled { | 		if !acl.Enabled { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		dstTags := convAclTagToValueMap(acl.Dst) | 		dstTags := convAclTagToValueMap(acl.Dst) | ||||||
| 		for nodeTag := range targetnode.Tags { | 		for nodeTag := range targetNodeTags { | ||||||
| 			if _, ok := dstTags[nodeTag.String()]; !ok { | 			if _, ok := dstTags[nodeTag.String()]; !ok { | ||||||
| 				if _, ok = dstTags[targetnode.ID.String()]; !ok { | 				if _, ok = dstTags[targetnode.ID.String()]; !ok { | ||||||
| 					continue | 					continue | ||||||
| @@ -1338,7 +1378,15 @@ func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRu | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	acls := listDevicePolicies(models.NetworkID(targetnode.Network)) | 	acls := listDevicePolicies(models.NetworkID(targetnode.Network)) | ||||||
| 	targetnode.Tags["*"] = 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["*"] = struct{}{} | ||||||
|  |  | ||||||
| 	for _, acl := range acls { | 	for _, acl := range acls { | ||||||
| 		if !acl.Enabled { | 		if !acl.Enabled { | ||||||
| @@ -1355,7 +1403,7 @@ func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRu | |||||||
| 			Direction:       acl.AllowedDirection, | 			Direction:       acl.AllowedDirection, | ||||||
| 			Allowed:         true, | 			Allowed:         true, | ||||||
| 		} | 		} | ||||||
| 		for nodeTag := range targetnode.Tags { | 		for nodeTag := range targetNodeTags { | ||||||
| 			if acl.AllowedDirection == models.TrafficDirectionBi { | 			if acl.AllowedDirection == models.TrafficDirectionBi { | ||||||
| 				var existsInSrcTag bool | 				var existsInSrcTag bool | ||||||
| 				var existsInDstTag bool | 				var existsInDstTag bool | ||||||
|   | |||||||
| @@ -28,6 +28,9 @@ var ( | |||||||
| func getAllExtClientsFromCache() (extClients []models.ExtClient) { | func getAllExtClientsFromCache() (extClients []models.ExtClient) { | ||||||
| 	extClientCacheMutex.RLock() | 	extClientCacheMutex.RLock() | ||||||
| 	for _, extclient := range extClientCacheMap { | 	for _, extclient := range extClientCacheMap { | ||||||
|  | 		if extclient.Mutex == nil { | ||||||
|  | 			extclient.Mutex = &sync.Mutex{} | ||||||
|  | 		} | ||||||
| 		extClients = append(extClients, extclient) | 		extClients = append(extClients, extclient) | ||||||
| 	} | 	} | ||||||
| 	extClientCacheMutex.RUnlock() | 	extClientCacheMutex.RUnlock() | ||||||
| @@ -43,12 +46,18 @@ func deleteExtClientFromCache(key string) { | |||||||
| func getExtClientFromCache(key string) (extclient models.ExtClient, ok bool) { | func getExtClientFromCache(key string) (extclient models.ExtClient, ok bool) { | ||||||
| 	extClientCacheMutex.RLock() | 	extClientCacheMutex.RLock() | ||||||
| 	extclient, ok = extClientCacheMap[key] | 	extclient, ok = extClientCacheMap[key] | ||||||
|  | 	if extclient.Mutex == nil { | ||||||
|  | 		extclient.Mutex = &sync.Mutex{} | ||||||
|  | 	} | ||||||
| 	extClientCacheMutex.RUnlock() | 	extClientCacheMutex.RUnlock() | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
| func storeExtClientInCache(key string, extclient models.ExtClient) { | func storeExtClientInCache(key string, extclient models.ExtClient) { | ||||||
| 	extClientCacheMutex.Lock() | 	extClientCacheMutex.Lock() | ||||||
|  | 	if extclient.Mutex == nil { | ||||||
|  | 		extclient.Mutex = &sync.Mutex{} | ||||||
|  | 	} | ||||||
| 	extClientCacheMap[key] = extclient | 	extClientCacheMap[key] = extclient | ||||||
| 	extClientCacheMutex.Unlock() | 	extClientCacheMutex.Unlock() | ||||||
| } | } | ||||||
| @@ -96,14 +105,14 @@ func DeleteExtClient(network string, clientid string) error { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	//recycle ip address |  | ||||||
| 	if extClient.Address != "" { |  | ||||||
| 		RemoveIpFromAllocatedIpMap(network, extClient.Address) |  | ||||||
| 	} |  | ||||||
| 	if extClient.Address6 != "" { |  | ||||||
| 		RemoveIpFromAllocatedIpMap(network, extClient.Address6) |  | ||||||
| 	} |  | ||||||
| 	if servercfg.CacheEnabled() { | 	if servercfg.CacheEnabled() { | ||||||
|  | 		// recycle ip address | ||||||
|  | 		if extClient.Address != "" { | ||||||
|  | 			RemoveIpFromAllocatedIpMap(network, extClient.Address) | ||||||
|  | 		} | ||||||
|  | 		if extClient.Address6 != "" { | ||||||
|  | 			RemoveIpFromAllocatedIpMap(network, extClient.Address6) | ||||||
|  | 		} | ||||||
| 		deleteExtClientFromCache(key) | 		deleteExtClientFromCache(key) | ||||||
| 	} | 	} | ||||||
| 	go RemoveNodeFromAclPolicy(extClient.ConvertToStaticNode()) | 	go RemoveNodeFromAclPolicy(extClient.ConvertToStaticNode()) | ||||||
| @@ -334,15 +343,16 @@ func SaveExtClient(extclient *models.ExtClient) error { | |||||||
| 	} | 	} | ||||||
| 	if servercfg.CacheEnabled() { | 	if servercfg.CacheEnabled() { | ||||||
| 		storeExtClientInCache(key, *extclient) | 		storeExtClientInCache(key, *extclient) | ||||||
| 	} | 		if _, ok := allocatedIpMap[extclient.Network]; ok { | ||||||
| 	if _, ok := allocatedIpMap[extclient.Network]; ok { | 			if extclient.Address != "" { | ||||||
| 		if extclient.Address != "" { | 				AddIpToAllocatedIpMap(extclient.Network, net.ParseIP(extclient.Address)) | ||||||
| 			AddIpToAllocatedIpMap(extclient.Network, net.ParseIP(extclient.Address)) | 			} | ||||||
| 		} | 			if extclient.Address6 != "" { | ||||||
| 		if extclient.Address6 != "" { | 				AddIpToAllocatedIpMap(extclient.Network, net.ParseIP(extclient.Address6)) | ||||||
| 			AddIpToAllocatedIpMap(extclient.Network, net.ParseIP(extclient.Address6)) | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return SetNetworkNodesLastModified(extclient.Network) | 	return SetNetworkNodesLastModified(extclient.Network) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,6 +3,8 @@ package logic | |||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"slices" | ||||||
|  | 	"sort" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/gravitl/netmaker/database" | 	"github.com/gravitl/netmaker/database" | ||||||
| @@ -77,6 +79,14 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro | |||||||
| 	if host.FirewallInUse == models.FIREWALL_NONE { | 	if host.FirewallInUse == models.FIREWALL_NONE { | ||||||
| 		return models.Node{}, errors.New("please install iptables or nftables on the device") | 		return models.Node{}, errors.New("please install iptables or nftables on the device") | ||||||
| 	} | 	} | ||||||
|  | 	if len(gateway.RangesWithMetric) == 0 && len(gateway.Ranges) > 0 { | ||||||
|  | 		for _, rangeI := range gateway.Ranges { | ||||||
|  | 			gateway.RangesWithMetric = append(gateway.RangesWithMetric, models.EgressRangeMetric{ | ||||||
|  | 				Network:     rangeI, | ||||||
|  | 				RouteMetric: 256, | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	for i := len(gateway.Ranges) - 1; i >= 0; i-- { | 	for i := len(gateway.Ranges) - 1; i >= 0; i-- { | ||||||
| 		// check if internet gateway IPv4 | 		// check if internet gateway IPv4 | ||||||
| 		if gateway.Ranges[i] == "0.0.0.0/0" || gateway.Ranges[i] == "::/0" { | 		if gateway.Ranges[i] == "0.0.0.0/0" || gateway.Ranges[i] == "::/0" { | ||||||
| @@ -91,6 +101,28 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro | |||||||
| 		gateway.Ranges[i] = normalized | 		gateway.Ranges[i] = normalized | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
|  | 	rangesWithMetric := []string{} | ||||||
|  | 	for i := len(gateway.RangesWithMetric) - 1; i >= 0; i-- { | ||||||
|  | 		if gateway.RangesWithMetric[i].Network == "0.0.0.0/0" || gateway.RangesWithMetric[i].Network == "::/0" { | ||||||
|  | 			// remove inet range | ||||||
|  | 			gateway.RangesWithMetric = append(gateway.RangesWithMetric[:i], gateway.RangesWithMetric[i+1:]...) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		normalized, err := NormalizeCIDR(gateway.RangesWithMetric[i].Network) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return models.Node{}, err | ||||||
|  | 		} | ||||||
|  | 		gateway.RangesWithMetric[i].Network = normalized | ||||||
|  | 		rangesWithMetric = append(rangesWithMetric, gateway.RangesWithMetric[i].Network) | ||||||
|  | 		if gateway.RangesWithMetric[i].RouteMetric <= 0 || gateway.RangesWithMetric[i].RouteMetric > 999 { | ||||||
|  | 			gateway.RangesWithMetric[i].RouteMetric = 256 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	sort.Strings(gateway.Ranges) | ||||||
|  | 	sort.Strings(rangesWithMetric) | ||||||
|  | 	if !slices.Equal(gateway.Ranges, rangesWithMetric) { | ||||||
|  | 		return models.Node{}, errors.New("invalid ranges") | ||||||
|  | 	} | ||||||
| 	if gateway.NatEnabled == "" { | 	if gateway.NatEnabled == "" { | ||||||
| 		gateway.NatEnabled = "yes" | 		gateway.NatEnabled = "yes" | ||||||
| 	} | 	} | ||||||
| @@ -104,6 +136,7 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro | |||||||
| 	node.IsEgressGateway = true | 	node.IsEgressGateway = true | ||||||
| 	node.EgressGatewayRanges = gateway.Ranges | 	node.EgressGatewayRanges = gateway.Ranges | ||||||
| 	node.EgressGatewayNatEnabled = models.ParseBool(gateway.NatEnabled) | 	node.EgressGatewayNatEnabled = models.ParseBool(gateway.NatEnabled) | ||||||
|  |  | ||||||
| 	node.EgressGatewayRequest = gateway // store entire request for use when preserving the egress gateway | 	node.EgressGatewayRequest = gateway // store entire request for use when preserving the egress gateway | ||||||
| 	node.SetLastModified() | 	node.SetLastModified() | ||||||
| 	if err = UpsertNode(&node); err != nil { | 	if err = UpsertNode(&node); err != nil { | ||||||
|   | |||||||
| @@ -30,6 +30,9 @@ var ( | |||||||
|  |  | ||||||
| // SetAllocatedIpMap - set allocated ip map for networks | // SetAllocatedIpMap - set allocated ip map for networks | ||||||
| func SetAllocatedIpMap() error { | func SetAllocatedIpMap() error { | ||||||
|  | 	if !servercfg.CacheEnabled() { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
| 	logger.Log(0, "start setting up allocated ip map") | 	logger.Log(0, "start setting up allocated ip map") | ||||||
| 	if allocatedIpMap == nil { | 	if allocatedIpMap == nil { | ||||||
| 		allocatedIpMap = map[string]map[string]net.IP{} | 		allocatedIpMap = map[string]map[string]net.IP{} | ||||||
| @@ -84,16 +87,25 @@ func SetAllocatedIpMap() error { | |||||||
|  |  | ||||||
| // ClearAllocatedIpMap - set allocatedIpMap to nil | // ClearAllocatedIpMap - set allocatedIpMap to nil | ||||||
| func ClearAllocatedIpMap() { | func ClearAllocatedIpMap() { | ||||||
|  | 	if !servercfg.CacheEnabled() { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 	allocatedIpMap = nil | 	allocatedIpMap = nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func AddIpToAllocatedIpMap(networkName string, ip net.IP) { | func AddIpToAllocatedIpMap(networkName string, ip net.IP) { | ||||||
|  | 	if !servercfg.CacheEnabled() { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 	networkCacheMutex.Lock() | 	networkCacheMutex.Lock() | ||||||
| 	allocatedIpMap[networkName][ip.String()] = ip | 	allocatedIpMap[networkName][ip.String()] = ip | ||||||
| 	networkCacheMutex.Unlock() | 	networkCacheMutex.Unlock() | ||||||
| } | } | ||||||
|  |  | ||||||
| func RemoveIpFromAllocatedIpMap(networkName string, ip string) { | func RemoveIpFromAllocatedIpMap(networkName string, ip string) { | ||||||
|  | 	if !servercfg.CacheEnabled() { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 	networkCacheMutex.Lock() | 	networkCacheMutex.Lock() | ||||||
| 	delete(allocatedIpMap[networkName], ip) | 	delete(allocatedIpMap[networkName], ip) | ||||||
| 	networkCacheMutex.Unlock() | 	networkCacheMutex.Unlock() | ||||||
| @@ -101,6 +113,10 @@ func RemoveIpFromAllocatedIpMap(networkName string, ip string) { | |||||||
|  |  | ||||||
| // AddNetworkToAllocatedIpMap - add network to allocated ip map when network is added | // AddNetworkToAllocatedIpMap - add network to allocated ip map when network is added | ||||||
| func AddNetworkToAllocatedIpMap(networkName string) { | func AddNetworkToAllocatedIpMap(networkName string) { | ||||||
|  | 	//add new network to allocated ip map | ||||||
|  | 	if !servercfg.CacheEnabled() { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 	networkCacheMutex.Lock() | 	networkCacheMutex.Lock() | ||||||
| 	allocatedIpMap[networkName] = make(map[string]net.IP) | 	allocatedIpMap[networkName] = make(map[string]net.IP) | ||||||
| 	networkCacheMutex.Unlock() | 	networkCacheMutex.Unlock() | ||||||
| @@ -108,6 +124,9 @@ func AddNetworkToAllocatedIpMap(networkName string) { | |||||||
|  |  | ||||||
| // RemoveNetworkFromAllocatedIpMap - remove network from allocated ip map when network is deleted | // RemoveNetworkFromAllocatedIpMap - remove network from allocated ip map when network is deleted | ||||||
| func RemoveNetworkFromAllocatedIpMap(networkName string) { | func RemoveNetworkFromAllocatedIpMap(networkName string) { | ||||||
|  | 	if !servercfg.CacheEnabled() { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 	networkCacheMutex.Lock() | 	networkCacheMutex.Lock() | ||||||
| 	delete(allocatedIpMap, networkName) | 	delete(allocatedIpMap, networkName) | ||||||
| 	networkCacheMutex.Unlock() | 	networkCacheMutex.Unlock() | ||||||
| @@ -354,7 +373,7 @@ func GetNetworkSettings(networkname string) (models.Network, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // UniqueAddress - get a unique ipv4 address | // UniqueAddress - get a unique ipv4 address | ||||||
| func UniqueAddress(networkName string, reverse bool) (net.IP, error) { | func UniqueAddressCache(networkName string, reverse bool) (net.IP, error) { | ||||||
| 	add := net.IP{} | 	add := net.IP{} | ||||||
| 	var network models.Network | 	var network models.Network | ||||||
| 	network, err := GetParentNetwork(networkName) | 	network, err := GetParentNetwork(networkName) | ||||||
| @@ -396,6 +415,49 @@ func UniqueAddress(networkName string, reverse bool) (net.IP, error) { | |||||||
| 	return add, errors.New("ERROR: No unique addresses available. Check network subnet") | 	return add, errors.New("ERROR: No unique addresses available. Check network subnet") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // UniqueAddress - get a unique ipv4 address | ||||||
|  | func UniqueAddressDB(networkName string, reverse bool) (net.IP, error) { | ||||||
|  | 	add := net.IP{} | ||||||
|  | 	var network models.Network | ||||||
|  | 	network, err := GetParentNetwork(networkName) | ||||||
|  | 	if err != nil { | ||||||
|  | 		logger.Log(0, "UniqueAddressServer encountered  an error") | ||||||
|  | 		return add, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if network.IsIPv4 == "no" { | ||||||
|  | 		return add, fmt.Errorf("IPv4 not active on network " + networkName) | ||||||
|  | 	} | ||||||
|  | 	//ensure AddressRange is valid | ||||||
|  | 	if _, _, err := net.ParseCIDR(network.AddressRange); err != nil { | ||||||
|  | 		logger.Log(0, "UniqueAddress encountered  an error") | ||||||
|  | 		return add, err | ||||||
|  | 	} | ||||||
|  | 	net4 := iplib.Net4FromStr(network.AddressRange) | ||||||
|  | 	newAddrs := net4.FirstAddress() | ||||||
|  |  | ||||||
|  | 	if reverse { | ||||||
|  | 		newAddrs = net4.LastAddress() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, false) && | ||||||
|  | 			IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, false) { | ||||||
|  | 			return newAddrs, nil | ||||||
|  | 		} | ||||||
|  | 		if reverse { | ||||||
|  | 			newAddrs, err = net4.PreviousIP(newAddrs) | ||||||
|  | 		} else { | ||||||
|  | 			newAddrs, err = net4.NextIP(newAddrs) | ||||||
|  | 		} | ||||||
|  | 		if err != nil { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return add, errors.New("ERROR: No unique addresses available. Check network subnet") | ||||||
|  | } | ||||||
|  |  | ||||||
| // IsIPUnique - checks if an IP is unique | // IsIPUnique - checks if an IP is unique | ||||||
| func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool { | func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool { | ||||||
|  |  | ||||||
| @@ -439,9 +501,67 @@ func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool { | |||||||
|  |  | ||||||
| 	return isunique | 	return isunique | ||||||
| } | } | ||||||
|  | func UniqueAddress(networkName string, reverse bool) (net.IP, error) { | ||||||
|  | 	if servercfg.CacheEnabled() { | ||||||
|  | 		return UniqueAddressCache(networkName, reverse) | ||||||
|  | 	} | ||||||
|  | 	return UniqueAddressDB(networkName, reverse) | ||||||
|  | } | ||||||
|  |  | ||||||
| // UniqueAddress6 - see if ipv6 address is unique |  | ||||||
| func UniqueAddress6(networkName string, reverse bool) (net.IP, error) { | func UniqueAddress6(networkName string, reverse bool) (net.IP, error) { | ||||||
|  | 	if servercfg.CacheEnabled() { | ||||||
|  | 		return UniqueAddress6Cache(networkName, reverse) | ||||||
|  | 	} | ||||||
|  | 	return UniqueAddress6DB(networkName, reverse) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UniqueAddress6DB - see if ipv6 address is unique | ||||||
|  | func UniqueAddress6DB(networkName string, reverse bool) (net.IP, error) { | ||||||
|  | 	add := net.IP{} | ||||||
|  | 	var network models.Network | ||||||
|  | 	network, err := GetParentNetwork(networkName) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Println("Network Not Found") | ||||||
|  | 		return add, err | ||||||
|  | 	} | ||||||
|  | 	if network.IsIPv6 == "no" { | ||||||
|  | 		return add, fmt.Errorf("IPv6 not active on network " + networkName) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	//ensure AddressRange is valid | ||||||
|  | 	if _, _, err := net.ParseCIDR(network.AddressRange6); err != nil { | ||||||
|  | 		return add, err | ||||||
|  | 	} | ||||||
|  | 	net6 := iplib.Net6FromStr(network.AddressRange6) | ||||||
|  |  | ||||||
|  | 	newAddrs, err := net6.NextIP(net6.FirstAddress()) | ||||||
|  | 	if reverse { | ||||||
|  | 		newAddrs, err = net6.PreviousIP(net6.LastAddress()) | ||||||
|  | 	} | ||||||
|  | 	if err != nil { | ||||||
|  | 		return add, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, true) && | ||||||
|  | 			IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, true) { | ||||||
|  | 			return newAddrs, nil | ||||||
|  | 		} | ||||||
|  | 		if reverse { | ||||||
|  | 			newAddrs, err = net6.PreviousIP(newAddrs) | ||||||
|  | 		} else { | ||||||
|  | 			newAddrs, err = net6.NextIP(newAddrs) | ||||||
|  | 		} | ||||||
|  | 		if err != nil { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return add, errors.New("ERROR: No unique IPv6 addresses available. Check network subnet") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UniqueAddress6Cache - see if ipv6 address is unique using cache | ||||||
|  | func UniqueAddress6Cache(networkName string, reverse bool) (net.IP, error) { | ||||||
| 	add := net.IP{} | 	add := net.IP{} | ||||||
| 	var network models.Network | 	var network models.Network | ||||||
| 	network, err := GetParentNetwork(networkName) | 	network, err := GetParentNetwork(networkName) | ||||||
|   | |||||||
							
								
								
									
										119
									
								
								logic/nodes.go
									
									
									
									
									
								
							
							
						
						
									
										119
									
								
								logic/nodes.go
									
									
									
									
									
								
							| @@ -35,12 +35,20 @@ var ( | |||||||
| func getNodeFromCache(nodeID string) (node models.Node, ok bool) { | func getNodeFromCache(nodeID string) (node models.Node, ok bool) { | ||||||
| 	nodeCacheMutex.RLock() | 	nodeCacheMutex.RLock() | ||||||
| 	node, ok = nodesCacheMap[nodeID] | 	node, ok = nodesCacheMap[nodeID] | ||||||
|  | 	if node.Mutex == nil { | ||||||
|  | 		node.Mutex = &sync.Mutex{} | ||||||
|  | 	} | ||||||
| 	nodeCacheMutex.RUnlock() | 	nodeCacheMutex.RUnlock() | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| func getNodesFromCache() (nodes []models.Node) { | func getNodesFromCache() (nodes []models.Node) { | ||||||
| 	nodeCacheMutex.RLock() | 	nodeCacheMutex.RLock() | ||||||
| 	nodes = slices.Collect(maps.Values(nodesCacheMap)) | 	for _, node := range nodesCacheMap { | ||||||
|  | 		if node.Mutex == nil { | ||||||
|  | 			node.Mutex = &sync.Mutex{} | ||||||
|  | 		} | ||||||
|  | 		nodes = append(nodes, node) | ||||||
|  | 	} | ||||||
| 	nodeCacheMutex.RUnlock() | 	nodeCacheMutex.RUnlock() | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| @@ -357,12 +365,15 @@ func DeleteNodeByID(node *models.Node) error { | |||||||
| 		logger.Log(1, "unable to remove metrics from DB for node", node.ID.String(), err.Error()) | 		logger.Log(1, "unable to remove metrics from DB for node", node.ID.String(), err.Error()) | ||||||
| 	} | 	} | ||||||
| 	//recycle ip address | 	//recycle ip address | ||||||
| 	if node.Address.IP != nil { | 	if servercfg.CacheEnabled() { | ||||||
| 		RemoveIpFromAllocatedIpMap(node.Network, node.Address.IP.String()) | 		if node.Address.IP != nil { | ||||||
| 	} | 			RemoveIpFromAllocatedIpMap(node.Network, node.Address.IP.String()) | ||||||
| 	if node.Address6.IP != nil { | 		} | ||||||
| 		RemoveIpFromAllocatedIpMap(node.Network, node.Address6.IP.String()) | 		if node.Address6.IP != nil { | ||||||
|  | 			RemoveIpFromAllocatedIpMap(node.Network, node.Address6.IP.String()) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -424,6 +435,9 @@ func GetAllNodes() ([]models.Node, error) { | |||||||
| 		} | 		} | ||||||
| 		// add node to our array | 		// add node to our array | ||||||
| 		nodes = append(nodes, node) | 		nodes = append(nodes, node) | ||||||
|  | 		if node.Mutex == nil { | ||||||
|  | 			node.Mutex = &sync.Mutex{} | ||||||
|  | 		} | ||||||
| 		nodesMap[node.ID.String()] = node | 		nodesMap[node.ID.String()] = node | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -699,15 +713,16 @@ func createNode(node *models.Node) error { | |||||||
| 	if servercfg.CacheEnabled() { | 	if servercfg.CacheEnabled() { | ||||||
| 		storeNodeInCache(*node) | 		storeNodeInCache(*node) | ||||||
| 		storeNodeInNetworkCache(*node, node.Network) | 		storeNodeInNetworkCache(*node, node.Network) | ||||||
| 	} | 		if _, ok := allocatedIpMap[node.Network]; ok { | ||||||
| 	if _, ok := allocatedIpMap[node.Network]; ok { | 			if node.Address.IP != nil { | ||||||
| 		if node.Address.IP != nil { | 				AddIpToAllocatedIpMap(node.Network, node.Address.IP) | ||||||
| 			AddIpToAllocatedIpMap(node.Network, node.Address.IP) | 			} | ||||||
| 		} | 			if node.Address6.IP != nil { | ||||||
| 		if node.Address6.IP != nil { | 				AddIpToAllocatedIpMap(node.Network, node.Address6.IP) | ||||||
| 			AddIpToAllocatedIpMap(node.Network, node.Address6.IP) | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	_, err = nodeacls.CreateNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), defaultACLVal) | 	_, err = nodeacls.CreateNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), defaultACLVal) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		logger.Log(1, "failed to create node ACL for node,", node.ID.String(), "err:", err.Error()) | 		logger.Log(1, "failed to create node ACL for node,", node.ID.String(), "err:", err.Error()) | ||||||
| @@ -753,16 +768,14 @@ func ValidateParams(nodeid, netid string) (models.Node, error) { | |||||||
| func ValidateNodeIp(currentNode *models.Node, newNode *models.ApiNode) error { | func ValidateNodeIp(currentNode *models.Node, newNode *models.ApiNode) error { | ||||||
|  |  | ||||||
| 	if currentNode.Address.IP != nil && currentNode.Address.String() != newNode.Address { | 	if currentNode.Address.IP != nil && currentNode.Address.String() != newNode.Address { | ||||||
| 		newIp, _, _ := net.ParseCIDR(newNode.Address) | 		if !IsIPUnique(newNode.Network, newNode.Address, database.NODES_TABLE_NAME, false) || | ||||||
| 		ipAllocated := allocatedIpMap[currentNode.Network] | 			!IsIPUnique(newNode.Network, newNode.Address, database.EXT_CLIENT_TABLE_NAME, false) { | ||||||
| 		if _, ok := ipAllocated[newIp.String()]; ok { |  | ||||||
| 			return errors.New("ip specified is already allocated:  " + newNode.Address) | 			return errors.New("ip specified is already allocated:  " + newNode.Address) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if currentNode.Address6.IP != nil && currentNode.Address6.String() != newNode.Address6 { | 	if currentNode.Address6.IP != nil && currentNode.Address6.String() != newNode.Address6 { | ||||||
| 		newIp, _, _ := net.ParseCIDR(newNode.Address6) | 		if !IsIPUnique(newNode.Network, newNode.Address6, database.NODES_TABLE_NAME, false) || | ||||||
| 		ipAllocated := allocatedIpMap[currentNode.Network] | 			!IsIPUnique(newNode.Network, newNode.Address6, database.EXT_CLIENT_TABLE_NAME, false) { | ||||||
| 		if _, ok := ipAllocated[newIp.String()]; ok { |  | ||||||
| 			return errors.New("ip specified is already allocated:  " + newNode.Address6) | 			return errors.New("ip specified is already allocated:  " + newNode.Address6) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -825,9 +838,16 @@ func GetTagMapWithNodes() (tagNodesMap map[models.TagID][]models.Node) { | |||||||
| 		if nodeI.Tags == nil { | 		if nodeI.Tags == nil { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  | 		if nodeI.Mutex != nil { | ||||||
|  | 			nodeI.Mutex.Lock() | ||||||
|  | 		} | ||||||
| 		for nodeTagID := range nodeI.Tags { | 		for nodeTagID := range nodeI.Tags { | ||||||
| 			tagNodesMap[nodeTagID] = append(tagNodesMap[nodeTagID], nodeI) | 			tagNodesMap[nodeTagID] = append(tagNodesMap[nodeTagID], nodeI) | ||||||
| 		} | 		} | ||||||
|  | 		if nodeI.Mutex != nil { | ||||||
|  | 			nodeI.Mutex.Unlock() | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| @@ -842,9 +862,15 @@ func GetTagMapWithNodesByNetwork(netID models.NetworkID, withStaticNodes bool) ( | |||||||
| 		if nodeI.Tags == nil { | 		if nodeI.Tags == nil { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  | 		if nodeI.Mutex != nil { | ||||||
|  | 			nodeI.Mutex.Lock() | ||||||
|  | 		} | ||||||
| 		for nodeTagID := range nodeI.Tags { | 		for nodeTagID := range nodeI.Tags { | ||||||
| 			tagNodesMap[nodeTagID] = append(tagNodesMap[nodeTagID], nodeI) | 			tagNodesMap[nodeTagID] = append(tagNodesMap[nodeTagID], nodeI) | ||||||
| 		} | 		} | ||||||
|  | 		if nodeI.Mutex != nil { | ||||||
|  | 			nodeI.Mutex.Unlock() | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	tagNodesMap["*"] = nodes | 	tagNodesMap["*"] = nodes | ||||||
| 	if !withStaticNodes { | 	if !withStaticNodes { | ||||||
| @@ -873,17 +899,16 @@ func AddTagMapWithStaticNodes(netID models.NetworkID, | |||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		for tagID := range extclient.Tags { | 		if extclient.Mutex != nil { | ||||||
| 			tagNodesMap[tagID] = append(tagNodesMap[tagID], models.Node{ | 			extclient.Mutex.Lock() | ||||||
| 				IsStatic:   true, | 		} | ||||||
| 				StaticNode: extclient, | 		for tagID := range extclient.Tags { | ||||||
| 			}) | 			tagNodesMap[tagID] = append(tagNodesMap[tagID], extclient.ConvertToStaticNode()) | ||||||
| 			tagNodesMap["*"] = append(tagNodesMap["*"], models.Node{ | 			tagNodesMap["*"] = append(tagNodesMap["*"], extclient.ConvertToStaticNode()) | ||||||
| 				IsStatic:   true, | 		} | ||||||
| 				StaticNode: extclient, | 		if extclient.Mutex != nil { | ||||||
| 			}) | 			extclient.Mutex.Unlock() | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
| 	return tagNodesMap | 	return tagNodesMap | ||||||
| } | } | ||||||
| @@ -904,11 +929,14 @@ func AddTagMapWithStaticNodesWithUsers(netID models.NetworkID, | |||||||
| 		if extclient.Tags == nil { | 		if extclient.Tags == nil { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  | 		if extclient.Mutex != nil { | ||||||
|  | 			extclient.Mutex.Lock() | ||||||
|  | 		} | ||||||
| 		for tagID := range extclient.Tags { | 		for tagID := range extclient.Tags { | ||||||
| 			tagNodesMap[tagID] = append(tagNodesMap[tagID], models.Node{ | 			tagNodesMap[tagID] = append(tagNodesMap[tagID], extclient.ConvertToStaticNode()) | ||||||
| 				IsStatic:   true, | 		} | ||||||
| 				StaticNode: extclient, | 		if extclient.Mutex != nil { | ||||||
| 			}) | 			extclient.Mutex.Unlock() | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
| @@ -926,9 +954,15 @@ func GetNodesWithTag(tagID models.TagID) map[string]models.Node { | |||||||
| 		if nodeI.Tags == nil { | 		if nodeI.Tags == nil { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  | 		if nodeI.Mutex != nil { | ||||||
|  | 			nodeI.Mutex.Lock() | ||||||
|  | 		} | ||||||
| 		if _, ok := nodeI.Tags[tagID]; ok { | 		if _, ok := nodeI.Tags[tagID]; ok { | ||||||
| 			nMap[nodeI.ID.String()] = nodeI | 			nMap[nodeI.ID.String()] = nodeI | ||||||
| 		} | 		} | ||||||
|  | 		if nodeI.Mutex != nil { | ||||||
|  | 			nodeI.Mutex.Unlock() | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	return AddStaticNodesWithTag(tag, nMap) | 	return AddStaticNodesWithTag(tag, nMap) | ||||||
| } | } | ||||||
| @@ -942,13 +976,15 @@ func AddStaticNodesWithTag(tag models.Tag, nMap map[string]models.Node) map[stri | |||||||
| 		if extclient.RemoteAccessClientID != "" { | 		if extclient.RemoteAccessClientID != "" { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		if _, ok := extclient.Tags[tag.ID]; ok { | 		if extclient.Mutex != nil { | ||||||
| 			nMap[extclient.ClientID] = models.Node{ | 			extclient.Mutex.Lock() | ||||||
| 				IsStatic:   true, | 		} | ||||||
| 				StaticNode: extclient, | 		if _, ok := extclient.Tags[tag.ID]; ok { | ||||||
| 			} | 			nMap[extclient.ClientID] = extclient.ConvertToStaticNode() | ||||||
|  | 		} | ||||||
|  | 		if extclient.Mutex != nil { | ||||||
|  | 			extclient.Mutex.Unlock() | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
| 	return nMap | 	return nMap | ||||||
| } | } | ||||||
| @@ -964,10 +1000,7 @@ func GetStaticNodeWithTag(tagID models.TagID) map[string]models.Node { | |||||||
| 		return nMap | 		return nMap | ||||||
| 	} | 	} | ||||||
| 	for _, extclient := range extclients { | 	for _, extclient := range extclients { | ||||||
| 		nMap[extclient.ClientID] = models.Node{ | 		nMap[extclient.ClientID] = extclient.ConvertToStaticNode() | ||||||
| 			IsStatic:   true, |  | ||||||
| 			StaticNode: extclient, |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	return nMap | 	return nMap | ||||||
| } | } | ||||||
|   | |||||||
| @@ -269,7 +269,11 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N | |||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		networkSettings, err := GetNetwork(node.Network) | ||||||
|  | 		if err != nil { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		hostPeerUpdate.NameServers = append(hostPeerUpdate.NameServers, networkSettings.NameServers...) | ||||||
| 		currentPeers := GetNetworkNodesMemory(allNodes, node.Network) | 		currentPeers := GetNetworkNodesMemory(allNodes, node.Network) | ||||||
| 		for _, peer := range currentPeers { | 		for _, peer := range currentPeers { | ||||||
| 			peer := peer | 			peer := peer | ||||||
| @@ -291,11 +295,12 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N | |||||||
| 			} | 			} | ||||||
| 			if peer.IsEgressGateway { | 			if peer.IsEgressGateway { | ||||||
| 				hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, models.EgressNetworkRoutes{ | 				hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, models.EgressNetworkRoutes{ | ||||||
| 					EgressGwAddr:  peer.Address, | 					EgressGwAddr:           peer.Address, | ||||||
| 					EgressGwAddr6: peer.Address6, | 					EgressGwAddr6:          peer.Address6, | ||||||
| 					NodeAddr:      node.Address, | 					NodeAddr:               node.Address, | ||||||
| 					NodeAddr6:     node.Address6, | 					NodeAddr6:              node.Address6, | ||||||
| 					EgressRanges:  peer.EgressGatewayRanges, | 					EgressRanges:           peer.EgressGatewayRanges, | ||||||
|  | 					EgressRangesWithMetric: peer.EgressGatewayRequest.RangesWithMetric, | ||||||
| 				}) | 				}) | ||||||
| 			} | 			} | ||||||
| 			if peer.IsIngressGateway { | 			if peer.IsIngressGateway { | ||||||
|   | |||||||
| @@ -225,6 +225,16 @@ func updateNodes() { | |||||||
| 				node.EgressGatewayRanges = egressRanges | 				node.EgressGatewayRanges = egressRanges | ||||||
| 				logic.UpsertNode(&node) | 				logic.UpsertNode(&node) | ||||||
| 			} | 			} | ||||||
|  | 			if len(node.EgressGatewayRequest.Ranges) > 0 && len(node.EgressGatewayRequest.RangesWithMetric) == 0 { | ||||||
|  | 				for _, egressRangeI := range node.EgressGatewayRequest.Ranges { | ||||||
|  | 					node.EgressGatewayRequest.RangesWithMetric = append(node.EgressGatewayRequest.RangesWithMetric, models.EgressRangeMetric{ | ||||||
|  | 						Network:     egressRangeI, | ||||||
|  | 						RouteMetric: 256, | ||||||
|  | 					}) | ||||||
|  | 				} | ||||||
|  | 				logic.UpsertNode(&node) | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,35 +17,36 @@ type ApiNodeStatus struct { | |||||||
|  |  | ||||||
| // ApiNode is a stripped down Node DTO that exposes only required fields to external systems | // ApiNode is a stripped down Node DTO that exposes only required fields to external systems | ||||||
| type ApiNode struct { | type ApiNode struct { | ||||||
| 	ID                         string   `json:"id,omitempty" validate:"required,min=5,id_unique"` | 	ID                            string              `json:"id,omitempty" validate:"required,min=5,id_unique"` | ||||||
| 	HostID                     string   `json:"hostid,omitempty" validate:"required,min=5,id_unique"` | 	HostID                        string              `json:"hostid,omitempty" validate:"required,min=5,id_unique"` | ||||||
| 	Address                    string   `json:"address" validate:"omitempty,cidrv4"` | 	Address                       string              `json:"address" validate:"omitempty,cidrv4"` | ||||||
| 	Address6                   string   `json:"address6" validate:"omitempty,cidrv6"` | 	Address6                      string              `json:"address6" validate:"omitempty,cidrv6"` | ||||||
| 	LocalAddress               string   `json:"localaddress" validate:"omitempty,cidr"` | 	LocalAddress                  string              `json:"localaddress" validate:"omitempty,cidr"` | ||||||
| 	AllowedIPs                 []string `json:"allowedips"` | 	AllowedIPs                    []string            `json:"allowedips"` | ||||||
| 	LastModified               int64    `json:"lastmodified" swaggertype:"primitive,integer" format:"int64"` | 	LastModified                  int64               `json:"lastmodified" swaggertype:"primitive,integer" format:"int64"` | ||||||
| 	ExpirationDateTime         int64    `json:"expdatetime" swaggertype:"primitive,integer" format:"int64"` | 	ExpirationDateTime            int64               `json:"expdatetime" swaggertype:"primitive,integer" format:"int64"` | ||||||
| 	LastCheckIn                int64    `json:"lastcheckin" swaggertype:"primitive,integer" format:"int64"` | 	LastCheckIn                   int64               `json:"lastcheckin" swaggertype:"primitive,integer" format:"int64"` | ||||||
| 	LastPeerUpdate             int64    `json:"lastpeerupdate" swaggertype:"primitive,integer" format:"int64"` | 	LastPeerUpdate                int64               `json:"lastpeerupdate" swaggertype:"primitive,integer" format:"int64"` | ||||||
| 	Network                    string   `json:"network"` | 	Network                       string              `json:"network"` | ||||||
| 	NetworkRange               string   `json:"networkrange"` | 	NetworkRange                  string              `json:"networkrange"` | ||||||
| 	NetworkRange6              string   `json:"networkrange6"` | 	NetworkRange6                 string              `json:"networkrange6"` | ||||||
| 	IsRelayed                  bool     `json:"isrelayed"` | 	IsRelayed                     bool                `json:"isrelayed"` | ||||||
| 	IsRelay                    bool     `json:"isrelay"` | 	IsRelay                       bool                `json:"isrelay"` | ||||||
| 	RelayedBy                  string   `json:"relayedby" bson:"relayedby" yaml:"relayedby"` | 	RelayedBy                     string              `json:"relayedby" bson:"relayedby" yaml:"relayedby"` | ||||||
| 	RelayedNodes               []string `json:"relaynodes" yaml:"relayedNodes"` | 	RelayedNodes                  []string            `json:"relaynodes" yaml:"relayedNodes"` | ||||||
| 	IsEgressGateway            bool     `json:"isegressgateway"` | 	IsEgressGateway               bool                `json:"isegressgateway"` | ||||||
| 	IsIngressGateway           bool     `json:"isingressgateway"` | 	IsIngressGateway              bool                `json:"isingressgateway"` | ||||||
| 	EgressGatewayRanges        []string `json:"egressgatewayranges"` | 	EgressGatewayRanges           []string            `json:"egressgatewayranges"` | ||||||
| 	EgressGatewayNatEnabled    bool     `json:"egressgatewaynatenabled"` | 	EgressGatewayNatEnabled       bool                `json:"egressgatewaynatenabled"` | ||||||
| 	DNSOn                      bool     `json:"dnson"` | 	EgressGatewayRangesWithMetric []EgressRangeMetric `json:"egressgatewayranges_with_metric"` | ||||||
| 	IngressDns                 string   `json:"ingressdns"` | 	DNSOn                         bool                `json:"dnson"` | ||||||
| 	IngressPersistentKeepalive int32    `json:"ingresspersistentkeepalive"` | 	IngressDns                    string              `json:"ingressdns"` | ||||||
| 	IngressMTU                 int32    `json:"ingressmtu"` | 	IngressPersistentKeepalive    int32               `json:"ingresspersistentkeepalive"` | ||||||
| 	Server                     string   `json:"server"` | 	IngressMTU                    int32               `json:"ingressmtu"` | ||||||
| 	Connected                  bool     `json:"connected"` | 	Server                        string              `json:"server"` | ||||||
| 	PendingDelete              bool     `json:"pendingdelete"` | 	Connected                     bool                `json:"connected"` | ||||||
| 	Metadata                   string   `json:"metadata"` | 	PendingDelete                 bool                `json:"pendingdelete"` | ||||||
|  | 	Metadata                      string              `json:"metadata"` | ||||||
| 	// == PRO == | 	// == PRO == | ||||||
| 	DefaultACL        string              `json:"defaultacl,omitempty" validate:"checkyesornoorunset"` | 	DefaultACL        string              `json:"defaultacl,omitempty" validate:"checkyesornoorunset"` | ||||||
| 	IsFailOver        bool                `json:"is_fail_over"` | 	IsFailOver        bool                `json:"is_fail_over"` | ||||||
| @@ -189,6 +190,7 @@ func (nm *Node) ConvertToAPINode() *ApiNode { | |||||||
| 	apiNode.IsEgressGateway = nm.IsEgressGateway | 	apiNode.IsEgressGateway = nm.IsEgressGateway | ||||||
| 	apiNode.IsIngressGateway = nm.IsIngressGateway | 	apiNode.IsIngressGateway = nm.IsIngressGateway | ||||||
| 	apiNode.EgressGatewayRanges = nm.EgressGatewayRanges | 	apiNode.EgressGatewayRanges = nm.EgressGatewayRanges | ||||||
|  | 	apiNode.EgressGatewayRangesWithMetric = nm.EgressGatewayRequest.RangesWithMetric | ||||||
| 	apiNode.EgressGatewayNatEnabled = nm.EgressGatewayNatEnabled | 	apiNode.EgressGatewayNatEnabled = nm.EgressGatewayNatEnabled | ||||||
| 	apiNode.DNSOn = nm.DNSOn | 	apiNode.DNSOn = nm.DNSOn | ||||||
| 	apiNode.IngressDns = nm.IngressDNS | 	apiNode.IngressDns = nm.IngressDNS | ||||||
|   | |||||||
| @@ -1,5 +1,7 @@ | |||||||
| package models | package models | ||||||
|  |  | ||||||
|  | import "sync" | ||||||
|  |  | ||||||
| // ExtClient - struct for external clients | // ExtClient - struct for external clients | ||||||
| type ExtClient struct { | type ExtClient struct { | ||||||
| 	ClientID               string              `json:"clientid" bson:"clientid"` | 	ClientID               string              `json:"clientid" bson:"clientid"` | ||||||
| @@ -25,6 +27,7 @@ type ExtClient struct { | |||||||
| 	DeviceName             string              `json:"device_name"` | 	DeviceName             string              `json:"device_name"` | ||||||
| 	PublicEndpoint         string              `json:"public_endpoint"` | 	PublicEndpoint         string              `json:"public_endpoint"` | ||||||
| 	Country                string              `json:"country"` | 	Country                string              `json:"country"` | ||||||
|  | 	Mutex                  *sync.Mutex         `json:"-"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // CustomExtClient - struct for CustomExtClient params | // CustomExtClient - struct for CustomExtClient params | ||||||
| @@ -57,5 +60,6 @@ func (ext *ExtClient) ConvertToStaticNode() Node { | |||||||
| 		Tags:       ext.Tags, | 		Tags:       ext.Tags, | ||||||
| 		IsStatic:   true, | 		IsStatic:   true, | ||||||
| 		StaticNode: *ext, | 		StaticNode: *ext, | ||||||
|  | 		Mutex:      ext.Mutex, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -27,6 +27,7 @@ type HostPeerUpdate struct { | |||||||
| 	EgressRoutes    []EgressNetworkRoutes `json:"egress_network_routes"` | 	EgressRoutes    []EgressNetworkRoutes `json:"egress_network_routes"` | ||||||
| 	FwUpdate        FwUpdate              `json:"fw_update"` | 	FwUpdate        FwUpdate              `json:"fw_update"` | ||||||
| 	ReplacePeers    bool                  `json:"replace_peers"` | 	ReplacePeers    bool                  `json:"replace_peers"` | ||||||
|  | 	NameServers     []string              `json:"name_servers"` | ||||||
| 	ServerConfig | 	ServerConfig | ||||||
| 	OldPeerUpdateFields | 	OldPeerUpdateFields | ||||||
| } | } | ||||||
| @@ -69,11 +70,12 @@ type EgressInfo struct { | |||||||
|  |  | ||||||
| // EgressNetworkRoutes - struct for egress network routes for adding routes to peer's interface | // EgressNetworkRoutes - struct for egress network routes for adding routes to peer's interface | ||||||
| type EgressNetworkRoutes struct { | type EgressNetworkRoutes struct { | ||||||
| 	EgressGwAddr  net.IPNet `json:"egress_gw_addr" yaml:"egress_gw_addr"` | 	EgressGwAddr           net.IPNet           `json:"egress_gw_addr" yaml:"egress_gw_addr"` | ||||||
| 	EgressGwAddr6 net.IPNet `json:"egress_gw_addr6" yaml:"egress_gw_addr6"` | 	EgressGwAddr6          net.IPNet           `json:"egress_gw_addr6" yaml:"egress_gw_addr6"` | ||||||
| 	NodeAddr      net.IPNet `json:"node_addr"` | 	NodeAddr               net.IPNet           `json:"node_addr"` | ||||||
| 	NodeAddr6     net.IPNet `json:"node_addr6"` | 	NodeAddr6              net.IPNet           `json:"node_addr6"` | ||||||
| 	EgressRanges  []string  `json:"egress_ranges"` | 	EgressRanges           []string            `json:"egress_ranges"` | ||||||
|  | 	EgressRangesWithMetric []EgressRangeMetric `json:"egress_ranges_metric"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // PeerRouteInfo - struct for peer info for an ext. client | // PeerRouteInfo - struct for peer info for an ext. client | ||||||
|   | |||||||
| @@ -8,22 +8,23 @@ import ( | |||||||
| // Network Struct - contains info for a given unique network | // Network Struct - contains info for a given unique network | ||||||
| // At  some point, need to replace all instances of Name with something else like  Identifier | // At  some point, need to replace all instances of Name with something else like  Identifier | ||||||
| type Network struct { | type Network struct { | ||||||
| 	AddressRange        string `json:"addressrange" bson:"addressrange" validate:"omitempty,cidrv4"` | 	AddressRange        string   `json:"addressrange" bson:"addressrange" validate:"omitempty,cidrv4"` | ||||||
| 	AddressRange6       string `json:"addressrange6" bson:"addressrange6" validate:"omitempty,cidrv6"` | 	AddressRange6       string   `json:"addressrange6" bson:"addressrange6" validate:"omitempty,cidrv6"` | ||||||
| 	NetID               string `json:"netid" bson:"netid" validate:"required,min=1,max=32,netid_valid"` | 	NetID               string   `json:"netid" bson:"netid" validate:"required,min=1,max=32,netid_valid"` | ||||||
| 	NodesLastModified   int64  `json:"nodeslastmodified" bson:"nodeslastmodified" swaggertype:"primitive,integer" format:"int64"` | 	NodesLastModified   int64    `json:"nodeslastmodified" bson:"nodeslastmodified" swaggertype:"primitive,integer" format:"int64"` | ||||||
| 	NetworkLastModified int64  `json:"networklastmodified" bson:"networklastmodified" swaggertype:"primitive,integer" format:"int64"` | 	NetworkLastModified int64    `json:"networklastmodified" bson:"networklastmodified" swaggertype:"primitive,integer" format:"int64"` | ||||||
| 	DefaultInterface    string `json:"defaultinterface" bson:"defaultinterface" validate:"min=1,max=35"` | 	DefaultInterface    string   `json:"defaultinterface" bson:"defaultinterface" validate:"min=1,max=35"` | ||||||
| 	DefaultListenPort   int32  `json:"defaultlistenport,omitempty" bson:"defaultlistenport,omitempty" validate:"omitempty,min=1024,max=65535"` | 	DefaultListenPort   int32    `json:"defaultlistenport,omitempty" bson:"defaultlistenport,omitempty" validate:"omitempty,min=1024,max=65535"` | ||||||
| 	NodeLimit           int32  `json:"nodelimit" bson:"nodelimit"` | 	NodeLimit           int32    `json:"nodelimit" bson:"nodelimit"` | ||||||
| 	DefaultPostDown     string `json:"defaultpostdown" bson:"defaultpostdown"` | 	DefaultPostDown     string   `json:"defaultpostdown" bson:"defaultpostdown"` | ||||||
| 	DefaultKeepalive    int32  `json:"defaultkeepalive" bson:"defaultkeepalive" validate:"omitempty,max=1000"` | 	DefaultKeepalive    int32    `json:"defaultkeepalive" bson:"defaultkeepalive" validate:"omitempty,max=1000"` | ||||||
| 	AllowManualSignUp   string `json:"allowmanualsignup" bson:"allowmanualsignup" validate:"checkyesorno"` | 	AllowManualSignUp   string   `json:"allowmanualsignup" bson:"allowmanualsignup" validate:"checkyesorno"` | ||||||
| 	IsIPv4              string `json:"isipv4" bson:"isipv4" validate:"checkyesorno"` | 	IsIPv4              string   `json:"isipv4" bson:"isipv4" validate:"checkyesorno"` | ||||||
| 	IsIPv6              string `json:"isipv6" bson:"isipv6" validate:"checkyesorno"` | 	IsIPv6              string   `json:"isipv6" bson:"isipv6" validate:"checkyesorno"` | ||||||
| 	DefaultUDPHolePunch string `json:"defaultudpholepunch" bson:"defaultudpholepunch" validate:"checkyesorno"` | 	DefaultUDPHolePunch string   `json:"defaultudpholepunch" bson:"defaultudpholepunch" validate:"checkyesorno"` | ||||||
| 	DefaultMTU          int32  `json:"defaultmtu" bson:"defaultmtu"` | 	DefaultMTU          int32    `json:"defaultmtu" bson:"defaultmtu"` | ||||||
| 	DefaultACL          string `json:"defaultacl" bson:"defaultacl" yaml:"defaultacl" validate:"checkyesorno"` | 	DefaultACL          string   `json:"defaultacl" bson:"defaultacl" yaml:"defaultacl" validate:"checkyesorno"` | ||||||
|  | 	NameServers         []string `json:"dns_nameservers"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // SaveData - sensitive fields of a network that should be kept the same | // SaveData - sensitive fields of a network that should be kept the same | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import ( | |||||||
| 	"math/rand" | 	"math/rand" | ||||||
| 	"net" | 	"net" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/google/uuid" | 	"github.com/google/uuid" | ||||||
| @@ -119,6 +120,7 @@ type Node struct { | |||||||
| 	IsUserNode        bool                `json:"is_user_node"` | 	IsUserNode        bool                `json:"is_user_node"` | ||||||
| 	StaticNode        ExtClient           `json:"static_node"` | 	StaticNode        ExtClient           `json:"static_node"` | ||||||
| 	Status            NodeStatus          `json:"node_status"` | 	Status            NodeStatus          `json:"node_status"` | ||||||
|  | 	Mutex             *sync.Mutex         `json:"-"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // LegacyNode - legacy struct for node model | // LegacyNode - legacy struct for node model | ||||||
|   | |||||||
| @@ -151,12 +151,18 @@ type ExtPeersResponse struct { | |||||||
| 	KeepAlive       int32  `json:"persistentkeepalive" bson:"persistentkeepalive"` | 	KeepAlive       int32  `json:"persistentkeepalive" bson:"persistentkeepalive"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type EgressRangeMetric struct { | ||||||
|  | 	Network     string `json:"network"` | ||||||
|  | 	RouteMetric uint32 `json:"route_metric"` // preffered range 1-999 | ||||||
|  | } | ||||||
|  |  | ||||||
| // EgressGatewayRequest - egress gateway request | // EgressGatewayRequest - egress gateway request | ||||||
| type EgressGatewayRequest struct { | type EgressGatewayRequest struct { | ||||||
| 	NodeID     string   `json:"nodeid" bson:"nodeid"` | 	NodeID           string              `json:"nodeid" bson:"nodeid"` | ||||||
| 	NetID      string   `json:"netid" bson:"netid"` | 	NetID            string              `json:"netid" bson:"netid"` | ||||||
| 	NatEnabled string   `json:"natenabled" bson:"natenabled"` | 	NatEnabled       string              `json:"natenabled" bson:"natenabled"` | ||||||
| 	Ranges     []string `json:"ranges" bson:"ranges"` | 	Ranges           []string            `json:"ranges" bson:"ranges"` | ||||||
|  | 	RangesWithMetric []EgressRangeMetric `json:"ranges_with_metric"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // RelayRequest - relay request struct | // RelayRequest - relay request struct | ||||||
|   | |||||||
| @@ -245,6 +245,12 @@ func getUserEmailFromClaims(token string) string { | |||||||
| 		return "" | 		return "" | ||||||
| 	} | 	} | ||||||
| 	claims, _ := accessToken.Claims.(jwt.MapClaims) | 	claims, _ := accessToken.Claims.(jwt.MapClaims) | ||||||
|  | 	if claims == nil { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 	if claims["email"] == nil { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
| 	return claims["email"].(string) | 	return claims["email"].(string) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -199,6 +199,10 @@ func getAzureUserInfo(state string, code string) (*OAuthUser, error) { | |||||||
| 	if userInfo.Email == "" { | 	if userInfo.Email == "" { | ||||||
| 		userInfo.Email = getUserEmailFromClaims(token.AccessToken) | 		userInfo.Email = getUserEmailFromClaims(token.AccessToken) | ||||||
| 	} | 	} | ||||||
|  | 	if userInfo.Email == "" && userInfo.UserPrincipalName != "" { | ||||||
|  | 		userInfo.Email = userInfo.UserPrincipalName | ||||||
|  |  | ||||||
|  | 	} | ||||||
| 	if userInfo.Email == "" { | 	if userInfo.Email == "" { | ||||||
| 		err = errors.New("failed to fetch user email from SSO state") | 		err = errors.New("failed to fetch user email from SSO state") | ||||||
| 		return userInfo, err | 		return userInfo, err | ||||||
|   | |||||||
| @@ -1108,6 +1108,9 @@ func getUserRemoteAccessGwsV1(w http.ResponseWriter, r *http.Request) { | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			gws := userGws[node.Network] | 			gws := userGws[node.Network] | ||||||
|  | 			if extClient.DNS == "" { | ||||||
|  | 				extClient.DNS = node.IngressDNS | ||||||
|  | 			} | ||||||
| 			extClient.AllowedIPs = logic.GetExtclientAllowedIPs(extClient) | 			extClient.AllowedIPs = logic.GetExtclientAllowedIPs(extClient) | ||||||
| 			gws = append(gws, models.UserRemoteGws{ | 			gws = append(gws, models.UserRemoteGws{ | ||||||
| 				GwID:              node.ID.String(), | 				GwID:              node.ID.String(), | ||||||
|   | |||||||
| @@ -55,6 +55,6 @@ func GetClient() (e EmailSender) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func IsValid(email string) bool { | func IsValid(email string) bool { | ||||||
| 	emailRegex := regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`) | 	emailRegex := regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`) | ||||||
| 	return emailRegex.MatchString(email) | 	return emailRegex.MatchString(email) | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 abhishek9686
					abhishek9686