Browse Source

Merge branch 'develop' of https://github.com/gravitl/netmaker into NET-390-scale-latest

Abhishek Kondur 2 years ago
parent
commit
950a303f66

+ 5 - 0
config/config.go

@@ -83,6 +83,11 @@ type ServerConfig struct {
 	TurnUserName               string    `yaml:"turn_username"`
 	TurnUserName               string    `yaml:"turn_username"`
 	TurnPassword               string    `yaml:"turn_password"`
 	TurnPassword               string    `yaml:"turn_password"`
 	UseTurn                    bool      `yaml:"use_turn"`
 	UseTurn                    bool      `yaml:"use_turn"`
+	UsersLimit                 int       `yaml:"user_limit"`
+	ClientsLimit               int       `yaml:"client_limit"`
+	NetworksLimit              int       `yaml:"network_limit"`
+	HostsLimit                 int       `yaml:"host_limit"`
+	DeployedByOperator         bool      `yaml:"deployed_by_operator"`
 }
 }
 
 
 // ProxyMode - default proxy mode for server
 // ProxyMode - default proxy mode for server

+ 1 - 9
controllers/limits.go

@@ -6,7 +6,6 @@ import (
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/servercfg"
 )
 )
 
 
 // limit consts
 // limit consts
@@ -23,20 +22,13 @@ func checkFreeTierLimits(limit_choice int, next http.Handler) http.HandlerFunc {
 			Code: http.StatusForbidden, Message: "free tier limits exceeded on networks",
 			Code: http.StatusForbidden, Message: "free tier limits exceeded on networks",
 		}
 		}
 
 
-		if logic.Free_Tier && servercfg.Is_EE { // check that free tier limits not exceeded
+		if logic.Free_Tier { // check that free tier limits not exceeded
 			if limit_choice == networks_l {
 			if limit_choice == networks_l {
 				currentNetworks, err := logic.GetNetworks()
 				currentNetworks, err := logic.GetNetworks()
 				if (err != nil && !database.IsEmptyRecord(err)) || len(currentNetworks) >= logic.Networks_Limit {
 				if (err != nil && !database.IsEmptyRecord(err)) || len(currentNetworks) >= logic.Networks_Limit {
 					logic.ReturnErrorResponse(w, r, errorResponse)
 					logic.ReturnErrorResponse(w, r, errorResponse)
 					return
 					return
 				}
 				}
-			} else if limit_choice == node_l {
-				nodes, err := logic.GetAllNodes()
-				if (err != nil && !database.IsEmptyRecord(err)) || len(nodes) >= logic.Node_Limit {
-					errorResponse.Message = "free tier limits exceeded on nodes"
-					logic.ReturnErrorResponse(w, r, errorResponse)
-					return
-				}
 			} else if limit_choice == users_l {
 			} else if limit_choice == users_l {
 				users, err := logic.GetUsers()
 				users, err := logic.GetUsers()
 				if (err != nil && !database.IsEmptyRecord(err)) || len(users) >= logic.Users_Limit {
 				if (err != nil && !database.IsEmptyRecord(err)) || len(users) >= logic.Users_Limit {

+ 38 - 0
controllers/server.go

@@ -22,6 +22,38 @@ func serverHandlers(r *mux.Router) {
 	r.HandleFunc("/api/server/getconfig", allowUsers(http.HandlerFunc(getConfig))).Methods(http.MethodGet)
 	r.HandleFunc("/api/server/getconfig", allowUsers(http.HandlerFunc(getConfig))).Methods(http.MethodGet)
 	r.HandleFunc("/api/server/getserverinfo", Authorize(true, false, "node", http.HandlerFunc(getServerInfo))).Methods(http.MethodGet)
 	r.HandleFunc("/api/server/getserverinfo", Authorize(true, false, "node", http.HandlerFunc(getServerInfo))).Methods(http.MethodGet)
 	r.HandleFunc("/api/server/status", http.HandlerFunc(getStatus)).Methods(http.MethodGet)
 	r.HandleFunc("/api/server/status", http.HandlerFunc(getStatus)).Methods(http.MethodGet)
+	r.HandleFunc("/api/server/usage", Authorize(true, false, "user", http.HandlerFunc(getUsage))).Methods(http.MethodGet)
+}
+func getUsage(w http.ResponseWriter, r *http.Request) {
+	type usage struct {
+		Hosts    int `json:"hosts"`
+		Clients  int `json:"clients"`
+		Networks int `json:"networks"`
+		Users    int `json:"users"`
+	}
+	var serverUsage usage
+	hosts, err := logic.GetAllHosts()
+	if err == nil {
+		serverUsage.Hosts = len(hosts)
+	}
+	clients, err := logic.GetAllExtClients()
+	if err == nil {
+		serverUsage.Clients = len(clients)
+	}
+	users, err := logic.GetUsers()
+	if err == nil {
+		serverUsage.Users = len(users)
+	}
+	networks, err := logic.GetNetworks()
+	if err == nil {
+		serverUsage.Networks = len(networks)
+	}
+	w.Header().Set("Content-Type", "application/json")
+	json.NewEncoder(w).Encode(models.SuccessResponse{
+		Code:     http.StatusOK,
+		Response: serverUsage,
+	})
+
 }
 }
 
 
 // swagger:route GET /api/server/status server getStatus
 // swagger:route GET /api/server/status server getStatus
@@ -41,6 +73,12 @@ func getStatus(w http.ResponseWriter, r *http.Request) {
 	type status struct {
 	type status struct {
 		DB     bool `json:"db_connected"`
 		DB     bool `json:"db_connected"`
 		Broker bool `json:"broker_connected"`
 		Broker bool `json:"broker_connected"`
+		Usage  struct {
+			Hosts    int `json:"hosts"`
+			Clients  int `json:"clients"`
+			Networks int `json:"networks"`
+			Users    int `json:"users"`
+		} `json:"usage"`
 	}
 	}
 
 
 	currentServerStatus := status{
 	currentServerStatus := status{

+ 10 - 0
ee/ee_controllers/relay.go

@@ -1,6 +1,7 @@
 package ee_controllers
 package ee_controllers
 
 
 import (
 import (
+	"context"
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
 	"net/http"
 	"net/http"
@@ -87,6 +88,15 @@ func deleteRelay(w http.ResponseWriter, r *http.Request) {
 				logger.Log(1, "relayed node update ", relayedNode.ID.String(), "on network", relayedNode.Network, ": ", err.Error())
 				logger.Log(1, "relayed node update ", relayedNode.ID.String(), "on network", relayedNode.Network, ": ", err.Error())
 
 
 			}
 			}
+			h, err := logic.GetHost(relayedNode.HostID.String())
+			if err == nil {
+				if h.OS == models.OS_Types.IoT {
+					node.IsRelay = true // for iot update to recognise that it has to delete relay peer
+					if err = mq.PublishSingleHostPeerUpdate(context.Background(), h, &node, nil); err != nil {
+						logger.Log(1, "failed to publish peer update to host", h.ID.String(), ": ", err.Error())
+					}
+				}
+			}
 		}
 		}
 		mq.PublishPeerUpdate()
 		mq.PublishPeerUpdate()
 	}()
 	}()

+ 3 - 18
ee/initialize.go

@@ -16,6 +16,7 @@ import (
 // InitEE - Initialize EE Logic
 // InitEE - Initialize EE Logic
 func InitEE() {
 func InitEE() {
 	setIsEnterprise()
 	setIsEnterprise()
+	servercfg.Is_EE = true
 	models.SetLogo(retrieveEELogo())
 	models.SetLogo(retrieveEELogo())
 	controller.HttpHandlers = append(
 	controller.HttpHandlers = append(
 		controller.HttpHandlers,
 		controller.HttpHandlers,
@@ -27,13 +28,8 @@ func InitEE() {
 	logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {
 	logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {
 		// == License Handling ==
 		// == License Handling ==
 		ValidateLicense()
 		ValidateLicense()
-		if Limits.FreeTier {
-			logger.Log(0, "proceeding with Free Tier license")
-			logic.SetFreeTierForTelemetry(true)
-		} else {
-			logger.Log(0, "proceeding with Paid Tier license")
-			logic.SetFreeTierForTelemetry(false)
-		}
+		logger.Log(0, "proceeding with Paid Tier license")
+		logic.SetFreeTierForTelemetry(false)
 		// == End License Handling ==
 		// == End License Handling ==
 		AddLicenseHooks()
 		AddLicenseHooks()
 		resetFailover()
 		resetFailover()
@@ -46,17 +42,6 @@ func InitEE() {
 	logic.AllowClientNodeAccess = eelogic.RemoveDeniedNodeFromClient
 	logic.AllowClientNodeAccess = eelogic.RemoveDeniedNodeFromClient
 }
 }
 
 
-func setControllerLimits() {
-	logic.Node_Limit = Limits.Nodes
-	logic.Users_Limit = Limits.Users
-	logic.Clients_Limit = Limits.Clients
-	logic.Free_Tier = Limits.FreeTier
-	servercfg.Is_EE = true
-	if logic.Free_Tier {
-		logic.Networks_Limit = 3
-	}
-}
-
 func resetFailover() {
 func resetFailover() {
 	nets, err := logic.GetNetworks()
 	nets, err := logic.GetNetworks()
 	if err == nil {
 	if err == nil {

+ 13 - 19
ee/license.go

@@ -9,12 +9,13 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
-	"math"
 	"net/http"
 	"net/http"
+	"time"
 
 
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
 	"golang.org/x/crypto/nacl/box"
 	"golang.org/x/crypto/nacl/box"
@@ -31,8 +32,14 @@ type apiServerConf struct {
 
 
 // AddLicenseHooks - adds the validation and cache clear hooks
 // AddLicenseHooks - adds the validation and cache clear hooks
 func AddLicenseHooks() {
 func AddLicenseHooks() {
-	logic.AddHook(ValidateLicense)
-	logic.AddHook(ClearLicenseCache)
+	logic.HookManagerCh <- models.HookDetails{
+		Hook:     ValidateLicense,
+		Interval: time.Hour,
+	}
+	logic.HookManagerCh <- models.HookDetails{
+		Hook:     ClearLicenseCache,
+		Interval: time.Hour,
+	}
 }
 }
 
 
 // ValidateLicense - the initial license check for netmaker server
 // ValidateLicense - the initial license check for netmaker server
@@ -58,8 +65,8 @@ func ValidateLicense() error {
 	}
 	}
 
 
 	licenseSecret := LicenseSecret{
 	licenseSecret := LicenseSecret{
-		UserID: netmakerAccountID,
-		Limits: getCurrentServerLimit(),
+		AssociatedID: netmakerAccountID,
+		Limits:       getCurrentServerLimit(),
 	}
 	}
 
 
 	secretData, err := json.Marshal(&licenseSecret)
 	secretData, err := json.Marshal(&licenseSecret)
@@ -92,17 +99,6 @@ func ValidateLicense() error {
 		logger.FatalLog0(errValidation.Error())
 		logger.FatalLog0(errValidation.Error())
 	}
 	}
 
 
-	Limits.Networks = math.MaxInt
-	Limits.FreeTier = license.FreeTier == "yes"
-	Limits.Clients = license.LimitClients
-	Limits.Nodes = license.LimitNodes
-	Limits.Servers = license.LimitServers
-	Limits.Users = license.LimitUsers
-	if Limits.FreeTier {
-		Limits.Networks = 3
-	}
-	setControllerLimits()
-
 	logger.Log(0, "License validation succeeded!")
 	logger.Log(0, "License validation succeeded!")
 	return nil
 	return nil
 }
 }
@@ -167,6 +163,7 @@ func validateLicenseKey(encryptedData []byte, publicKey *[32]byte) ([]byte, erro
 	}
 	}
 
 
 	msg := ValidateLicenseRequest{
 	msg := ValidateLicenseRequest{
+		LicenseKey:     servercfg.GetLicenseKey(),
 		NmServerPubKey: base64encode(publicKeyBytes),
 		NmServerPubKey: base64encode(publicKeyBytes),
 		EncryptedPart:  base64encode(encryptedData),
 		EncryptedPart:  base64encode(encryptedData),
 	}
 	}
@@ -180,9 +177,6 @@ func validateLicenseKey(encryptedData []byte, publicKey *[32]byte) ([]byte, erro
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	reqParams := req.URL.Query()
-	reqParams.Add("licensevalue", servercfg.GetLicenseKey())
-	req.URL.RawQuery = reqParams.Encode()
 	req.Header.Add("Content-Type", "application/json")
 	req.Header.Add("Content-Type", "application/json")
 	req.Header.Add("Accept", "application/json")
 	req.Header.Add("Accept", "application/json")
 	client := &http.Client{}
 	client := &http.Client{}

+ 20 - 38
ee/types.go

@@ -3,7 +3,7 @@ package ee
 import "fmt"
 import "fmt"
 
 
 const (
 const (
-	api_endpoint               = "https://api.controller.netmaker.io/api/v1/license/validate"
+	api_endpoint               = "https://api.accounts.netmaker.io/api/v1/license/validate"
 	license_cache_key          = "license_response_cache"
 	license_cache_key          = "license_response_cache"
 	license_validation_err_msg = "invalid license"
 	license_validation_err_msg = "invalid license"
 	server_id_key              = "nm-server-id"
 	server_id_key              = "nm-server-id"
@@ -11,38 +11,17 @@ const (
 
 
 var errValidation = fmt.Errorf(license_validation_err_msg)
 var errValidation = fmt.Errorf(license_validation_err_msg)
 
 
-// Limits - limits to be referenced throughout server
-var Limits = GlobalLimits{
-	Servers:  0,
-	Users:    0,
-	Nodes:    0,
-	Clients:  0,
-	Networks: 0,
-	FreeTier: false,
-}
-
-// GlobalLimits - struct for holding global limits on this netmaker server in memory
-type GlobalLimits struct {
-	Servers  int
-	Users    int
-	Nodes    int
-	Clients  int
-	FreeTier bool
-	Networks int
-}
-
 // LicenseKey - the license key struct representation with associated data
 // LicenseKey - the license key struct representation with associated data
 type LicenseKey struct {
 type LicenseKey struct {
-	LicenseValue   string `json:"license_value"` // actual (public) key and the unique value for the key
-	Expiration     int64  `json:"expiration"`
-	LimitServers   int    `json:"limit_servers"`
-	LimitUsers     int    `json:"limit_users"`
-	LimitNodes     int    `json:"limit_nodes"`
-	LimitClients   int    `json:"limit_clients"`
-	Metadata       string `json:"metadata"`
-	SubscriptionID string `json:"subscription_id"` // for a paid subscription (non-free-tier license)
-	FreeTier       string `json:"free_tier"`       // yes if free tier
-	IsActive       string `json:"is_active"`       // yes if active
+	LicenseValue  string `json:"license_value"` // actual (public) key and the unique value for the key
+	Expiration    int64  `json:"expiration"`
+	LimitServers  int    `json:"limit_servers"`
+	LimitUsers    int    `json:"limit_users"`
+	LimitHosts    int    `json:"limit_hosts"`
+	LimitNetworks int    `json:"limit_networks"`
+	LimitClients  int    `json:"limit_clients"`
+	Metadata      string `json:"metadata"`
+	IsActive      bool   `json:"is_active"` // yes if active
 }
 }
 
 
 // ValidatedLicense - the validated license struct
 // ValidatedLicense - the validated license struct
@@ -53,28 +32,31 @@ type ValidatedLicense struct {
 
 
 // LicenseSecret - the encrypted struct for sending user-id
 // LicenseSecret - the encrypted struct for sending user-id
 type LicenseSecret struct {
 type LicenseSecret struct {
-	UserID string        `json:"user_id" binding:"required"` // UUID for user foreign key to User table
-	Limits LicenseLimits `json:"limits" binding:"required"`
+	AssociatedID string        `json:"associated_id" binding:"required"` // UUID for user foreign key to User table
+	Limits       LicenseLimits `json:"limits" binding:"required"`
 }
 }
 
 
 // LicenseLimits - struct license limits
 // LicenseLimits - struct license limits
 type LicenseLimits struct {
 type LicenseLimits struct {
-	Servers int `json:"servers" binding:"required"`
-	Users   int `json:"users" binding:"required"`
-	Nodes   int `json:"nodes" binding:"required"`
-	Clients int `json:"clients" binding:"required"`
+	Servers  int `json:"servers"`
+	Users    int `json:"users"`
+	Hosts    int `json:"hosts"`
+	Clients  int `json:"clients"`
+	Networks int `json:"networks"`
 }
 }
 
 
 // LicenseLimits.SetDefaults - sets the default values for limits
 // LicenseLimits.SetDefaults - sets the default values for limits
 func (l *LicenseLimits) SetDefaults() {
 func (l *LicenseLimits) SetDefaults() {
 	l.Clients = 0
 	l.Clients = 0
 	l.Servers = 1
 	l.Servers = 1
-	l.Nodes = 0
+	l.Hosts = 0
 	l.Users = 1
 	l.Users = 1
+	l.Networks = 0
 }
 }
 
 
 // ValidateLicenseRequest - used for request to validate license endpoint
 // ValidateLicenseRequest - used for request to validate license endpoint
 type ValidateLicenseRequest struct {
 type ValidateLicenseRequest struct {
+	LicenseKey     string `json:"license_key" binding:"required"`
 	NmServerPubKey string `json:"nm_server_pub_key" binding:"required"` // Netmaker server public key used to send data back to Netmaker for the Netmaker server to decrypt (eg output from validating license)
 	NmServerPubKey string `json:"nm_server_pub_key" binding:"required"` // Netmaker server public key used to send data back to Netmaker for the Netmaker server to decrypt (eg output from validating license)
 	EncryptedPart  string `json:"secret" binding:"required"`
 	EncryptedPart  string `json:"secret" binding:"required"`
 }
 }

+ 6 - 3
ee/util.go

@@ -30,12 +30,11 @@ func base64decode(input string) []byte {
 
 
 	return bytes
 	return bytes
 }
 }
-
 func getCurrentServerLimit() (limits LicenseLimits) {
 func getCurrentServerLimit() (limits LicenseLimits) {
 	limits.SetDefaults()
 	limits.SetDefaults()
-	nodes, err := logic.GetAllNodes()
+	hosts, err := logic.GetAllHosts()
 	if err == nil {
 	if err == nil {
-		limits.Nodes = len(nodes)
+		limits.Hosts = len(hosts)
 	}
 	}
 	clients, err := logic.GetAllExtClients()
 	clients, err := logic.GetAllExtClients()
 	if err == nil {
 	if err == nil {
@@ -45,5 +44,9 @@ func getCurrentServerLimit() (limits LicenseLimits) {
 	if err == nil {
 	if err == nil {
 		limits.Users = len(users)
 		limits.Users = len(users)
 	}
 	}
+	networks, err := logic.GetNetworks()
+	if err == nil {
+		limits.Networks = len(networks)
+	}
 	return
 	return
 }
 }

+ 8 - 1
logic/hosts.go

@@ -159,7 +159,14 @@ func GetHost(hostid string) (*models.Host, error) {
 
 
 // CreateHost - creates a host if not exist
 // CreateHost - creates a host if not exist
 func CreateHost(h *models.Host) error {
 func CreateHost(h *models.Host) error {
-	_, err := GetHost(h.ID.String())
+	hosts, err := GetAllHosts()
+	if err != nil && !database.IsEmptyRecord(err) {
+		return err
+	}
+	if len(hosts) >= Hosts_Limit {
+		return errors.New("free tier limits exceeded on hosts")
+	}
+	_, err = GetHost(h.ID.String())
 	if (err != nil && !database.IsEmptyRecord(err)) || (err == nil) {
 	if (err != nil && !database.IsEmptyRecord(err)) || (err == nil) {
 		return ErrHostExists
 		return ErrHostExists
 	}
 	}

+ 55 - 0
logic/peers.go

@@ -131,6 +131,61 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
 		if !node.Connected || node.PendingDelete || node.Action == models.NODE_DELETE {
 		if !node.Connected || node.PendingDelete || node.Action == models.NODE_DELETE {
 			continue
 			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: &relayNode.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: getPeerWgListenPort(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
+		}
+
 		currentPeers := GetNetworkNodesMemory(allNodes, node.Network)
 		currentPeers := GetNetworkNodesMemory(allNodes, node.Network)
 		var nodePeerMap map[string]models.PeerRouteInfo
 		var nodePeerMap map[string]models.PeerRouteInfo
 		if node.IsIngressGateway || node.IsEgressGateway {
 		if node.IsIngressGateway || node.IsEgressGateway {

+ 11 - 2
logic/serverconf.go

@@ -4,17 +4,18 @@ import (
 	"encoding/json"
 	"encoding/json"
 
 
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/servercfg"
 )
 )
 
 
 var (
 var (
-	// Node_Limit - dummy var for community
-	Node_Limit = 1000000000
 	// Networks_Limit - dummy var for community
 	// Networks_Limit - dummy var for community
 	Networks_Limit = 1000000000
 	Networks_Limit = 1000000000
 	// Users_Limit - dummy var for community
 	// Users_Limit - dummy var for community
 	Users_Limit = 1000000000
 	Users_Limit = 1000000000
 	// Clients_Limit - dummy var for community
 	// Clients_Limit - dummy var for community
 	Clients_Limit = 1000000000
 	Clients_Limit = 1000000000
+	// Hosts_Limit - dummy var for community
+	Hosts_Limit = 1000000000
 	// Free_Tier - specifies if free tier
 	// Free_Tier - specifies if free tier
 	Free_Tier = false
 	Free_Tier = false
 )
 )
@@ -85,3 +86,11 @@ func StoreJWTSecret(privateKey string) error {
 	}
 	}
 	return database.Insert("nm-jwt-secret", string(data), database.SERVERCONF_TABLE_NAME)
 	return database.Insert("nm-jwt-secret", string(data), database.SERVERCONF_TABLE_NAME)
 }
 }
+
+func SetFreeTierLimits() {
+	Free_Tier = true
+	Users_Limit = servercfg.GetUserLimit()
+	Clients_Limit = servercfg.GetClientLimit()
+	Networks_Limit = servercfg.GetNetworkLimit()
+	Hosts_Limit = servercfg.GetHostLimit()
+}

+ 3 - 0
logic/telemetry.go

@@ -60,6 +60,7 @@ func sendTelemetry() error {
 		Event:      "daily checkin",
 		Event:      "daily checkin",
 		Properties: posthog.NewProperties().
 		Properties: posthog.NewProperties().
 			Set("nodes", d.Nodes).
 			Set("nodes", d.Nodes).
+			Set("hosts", d.Hosts).
 			Set("servers", d.Servers).
 			Set("servers", d.Servers).
 			Set("non-server nodes", d.Count.NonServer).
 			Set("non-server nodes", d.Count.NonServer).
 			Set("extclients", d.ExtClients).
 			Set("extclients", d.ExtClients).
@@ -84,6 +85,7 @@ func fetchTelemetryData() (telemetryData, error) {
 	data.ExtClients = getDBLength(database.EXT_CLIENT_TABLE_NAME)
 	data.ExtClients = getDBLength(database.EXT_CLIENT_TABLE_NAME)
 	data.Users = getDBLength(database.USERS_TABLE_NAME)
 	data.Users = getDBLength(database.USERS_TABLE_NAME)
 	data.Networks = getDBLength(database.NETWORKS_TABLE_NAME)
 	data.Networks = getDBLength(database.NETWORKS_TABLE_NAME)
+	data.Hosts = getDBLength(database.HOSTS_TABLE_NAME)
 	data.Version = servercfg.GetVersion()
 	data.Version = servercfg.GetVersion()
 	data.Servers = getServerCount()
 	data.Servers = getServerCount()
 	nodes, err := GetAllNodes()
 	nodes, err := GetAllNodes()
@@ -167,6 +169,7 @@ func getDBLength(dbname string) int {
 // telemetryData - What data to send to posthog
 // telemetryData - What data to send to posthog
 type telemetryData struct {
 type telemetryData struct {
 	Nodes      int
 	Nodes      int
+	Hosts      int
 	ExtClients int
 	ExtClients int
 	Users      int
 	Users      int
 	Count      clientCount
 	Count      clientCount

+ 36 - 0
logic/timer.go

@@ -1,10 +1,13 @@
 package logic
 package logic
 
 
 import (
 import (
+	"context"
 	"fmt"
 	"fmt"
+	"sync"
 	"time"
 	"time"
 
 
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/models"
 )
 )
 
 
 // == Constants ==
 // == Constants ==
@@ -12,6 +15,9 @@ import (
 // How long to wait before sending telemetry to server (24 hours)
 // How long to wait before sending telemetry to server (24 hours)
 const timer_hours_between_runs = 24
 const timer_hours_between_runs = 24
 
 
+// HookManagerCh - channel to add any new hooks
+var HookManagerCh = make(chan models.HookDetails, 2)
+
 // == Public ==
 // == Public ==
 
 
 // TimerCheckpoint - Checks if 24 hours has passed since telemetry was last sent. If so, sends telemetry data to posthog
 // TimerCheckpoint - Checks if 24 hours has passed since telemetry was last sent. If so, sends telemetry data to posthog
@@ -40,6 +46,36 @@ func AddHook(ifaceToAdd interface{}) {
 	timeHooks = append(timeHooks, ifaceToAdd)
 	timeHooks = append(timeHooks, ifaceToAdd)
 }
 }
 
 
+// StartHookManager - listens on `HookManagerCh` to run any hook
+func StartHookManager(ctx context.Context, wg *sync.WaitGroup) {
+	defer wg.Done()
+	for {
+		select {
+		case <-ctx.Done():
+			logger.Log(0, "## Stopping Hook Manager")
+			return
+		case newhook := <-HookManagerCh:
+			wg.Add(1)
+			go addHookWithInterval(ctx, wg, newhook.Hook, newhook.Interval)
+		}
+	}
+}
+
+func addHookWithInterval(ctx context.Context, wg *sync.WaitGroup, hook func() error, interval time.Duration) {
+	defer wg.Done()
+	ticker := time.NewTicker(interval)
+	defer ticker.Stop()
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		case <-ticker.C:
+			hook()
+		}
+	}
+
+}
+
 // == private ==
 // == private ==
 
 
 // timeHooks - functions to run once a day, functions must take no parameters
 // timeHooks - functions to run once a day, functions must take no parameters

+ 6 - 1
main.go

@@ -42,6 +42,9 @@ func main() {
 	initialize()                       // initial db and acls
 	initialize()                       // initial db and acls
 	setGarbageCollection()
 	setGarbageCollection()
 	setVerbosity()
 	setVerbosity()
+	if servercfg.DeployedByOperator() && !servercfg.Is_EE {
+		logic.SetFreeTierLimits()
+	}
 	defer database.CloseDB()
 	defer database.CloseDB()
 	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, os.Interrupt)
 	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, os.Interrupt)
 	defer stop()
 	defer stop()
@@ -89,7 +92,6 @@ func initialize() { // Client Mode Prereq Check
 	if err != nil {
 	if err != nil {
 		logger.Log(1, "Timer error occurred: ", err.Error())
 		logger.Log(1, "Timer error occurred: ", err.Error())
 	}
 	}
-
 	logic.EnterpriseCheck()
 	logic.EnterpriseCheck()
 
 
 	var authProvider = auth.InitializeAuthProvider()
 	var authProvider = auth.InitializeAuthProvider()
@@ -150,6 +152,9 @@ func startControllers(wg *sync.WaitGroup, ctx context.Context) {
 	// starts the stun server
 	// starts the stun server
 	wg.Add(1)
 	wg.Add(1)
 	go stunserver.Start(wg, ctx)
 	go stunserver.Start(wg, ctx)
+
+	wg.Add(1)
+	go logic.StartHookManager(ctx, wg)
 }
 }
 
 
 // Should we be using a context vice a waitgroup????????????
 // Should we be using a context vice a waitgroup????????????

+ 2 - 0
models/api_host.go

@@ -34,6 +34,7 @@ type ApiHost struct {
 	RelayedBy          string   `json:"relayed_by" bson:"relayed_by" yaml:"relayed_by"`
 	RelayedBy          string   `json:"relayed_by" bson:"relayed_by" yaml:"relayed_by"`
 	IsRelay            bool     `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
 	IsRelay            bool     `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
 	RelayedHosts       []string `json:"relay_hosts" bson:"relay_hosts" yaml:"relay_hosts"`
 	RelayedHosts       []string `json:"relay_hosts" bson:"relay_hosts" yaml:"relay_hosts"`
+	NatType            string   `json:"nat_type" yaml:"nat_type"`
 }
 }
 
 
 // Host.ConvertNMHostToAPI - converts a Netmaker host to an API editable host
 // Host.ConvertNMHostToAPI - converts a Netmaker host to an API editable host
@@ -67,6 +68,7 @@ func (h *Host) ConvertNMHostToAPI() *ApiHost {
 	a.Verbosity = h.Verbosity
 	a.Verbosity = h.Verbosity
 	a.Version = h.Version
 	a.Version = h.Version
 	a.IsDefault = h.IsDefault
 	a.IsDefault = h.IsDefault
+	a.NatType = h.NatType
 	return &a
 	return &a
 }
 }
 
 

+ 1 - 0
models/mqtt.go

@@ -9,6 +9,7 @@ import (
 // HostPeerUpdate - struct for host peer updates
 // HostPeerUpdate - struct for host peer updates
 type HostPeerUpdate struct {
 type HostPeerUpdate struct {
 	Host              Host                 `json:"host" bson:"host" yaml:"host"`
 	Host              Host                 `json:"host" bson:"host" yaml:"host"`
+	NodeAddrs         []net.IPNet          `json:"nodes_addrs" yaml:"nodes_addrs"`
 	Server            string               `json:"server" bson:"server" yaml:"server"`
 	Server            string               `json:"server" bson:"server" yaml:"server"`
 	ServerVersion     string               `json:"serverversion" bson:"serverversion" yaml:"serverversion"`
 	ServerVersion     string               `json:"serverversion" bson:"serverversion" yaml:"serverversion"`
 	ServerAddrs       []ServerAddr         `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"`
 	ServerAddrs       []ServerAddr         `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"`

+ 8 - 0
models/node.go

@@ -181,6 +181,14 @@ func isLess(ipA string, ipB string) bool {
 	return bytes.Compare(ipNetA, ipNetB) < 0
 	return bytes.Compare(ipNetA, ipNetB) < 0
 }
 }
 
 
+// Node.PrimaryAddress - return ipv4 address if present, else return ipv6
+func (node *Node) PrimaryAddressIPNet() net.IPNet {
+	if node.Address.IP != nil {
+		return node.Address
+	}
+	return node.Address6
+}
+
 // Node.PrimaryAddress - return ipv4 address if present, else return ipv6
 // Node.PrimaryAddress - return ipv4 address if present, else return ipv6
 func (node *Node) PrimaryAddress() string {
 func (node *Node) PrimaryAddress() string {
 	if node.Address.IP != nil {
 	if node.Address.IP != nil {

+ 16 - 0
models/structs.go

@@ -2,6 +2,7 @@ package models
 
 
 import (
 import (
 	"strings"
 	"strings"
+	"time"
 
 
 	jwt "github.com/golang-jwt/jwt/v4"
 	jwt "github.com/golang-jwt/jwt/v4"
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
@@ -274,3 +275,18 @@ type StunServer struct {
 	Domain string `json:"domain" yaml:"domain"`
 	Domain string `json:"domain" yaml:"domain"`
 	Port   int    `json:"port" yaml:"port"`
 	Port   int    `json:"port" yaml:"port"`
 }
 }
+
+// HookDetails - struct to hold hook info
+type HookDetails struct {
+	Hook     func() error
+	Interval time.Duration
+}
+
+// LicenseLimits - struct license limits
+type LicenseLimits struct {
+	Servers  int `json:"servers"`
+	Users    int `json:"users"`
+	Hosts    int `json:"hosts"`
+	Clients  int `json:"clients"`
+	Networks int `json:"networks"`
+}

+ 6 - 1
mq/emqx.go

@@ -6,11 +6,14 @@ import (
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"net/http"
 	"net/http"
+	"strings"
 	"sync"
 	"sync"
 
 
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
 )
 )
 
 
+const already_exists = "ALREADY_EXISTS"
+
 type (
 type (
 	emqxUser struct {
 	emqxUser struct {
 		UserID   string `json:"user_id"`
 		UserID   string `json:"user_id"`
@@ -99,7 +102,9 @@ func CreateEmqxUser(username, password string, admin bool) error {
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		return fmt.Errorf("error creating EMQX user %v", string(msg))
+		if !strings.Contains(string(msg), already_exists) {
+			return fmt.Errorf("error creating EMQX user %v", string(msg))
+		}
 	}
 	}
 	return nil
 	return nil
 }
 }

+ 13 - 11
mq/publishers.go

@@ -100,18 +100,20 @@ func PublishSingleHostPeerUpdate(ctx context.Context, host *models.Host, allNode
 	if len(peerUpdate.Peers) == 0 { // no peers to send
 	if len(peerUpdate.Peers) == 0 { // no peers to send
 		return nil
 		return nil
 	}
 	}
-	proxyUpdate, err := logic.GetProxyUpdateForHost(ctx, host)
-	if err != nil {
-		return err
-	}
-	proxyUpdate.Server = servercfg.GetServer()
-	if host.ProxyEnabled {
-		proxyUpdate.Action = models.ProxyUpdate
-	} else {
-		proxyUpdate.Action = models.NoProxy
-	}
+	if host.OS != models.OS_Types.IoT {
+		proxyUpdate, err := logic.GetProxyUpdateForHost(ctx, host)
+		if err != nil {
+			return err
+		}
+		proxyUpdate.Server = servercfg.GetServer()
+		if host.ProxyEnabled {
+			proxyUpdate.Action = models.ProxyUpdate
+		} else {
+			proxyUpdate.Action = models.NoProxy
+		}
 
 
-	peerUpdate.ProxyUpdate = proxyUpdate
+		peerUpdate.ProxyUpdate = proxyUpdate
+	}
 
 
 	data, err := json.Marshal(&peerUpdate)
 	data, err := json.Marshal(&peerUpdate)
 	if err != nil {
 	if err != nil {

+ 53 - 0
servercfg/serverconf.go

@@ -10,6 +10,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/gravitl/netmaker/config"
 	"github.com/gravitl/netmaker/config"
+
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 )
 )
 
 
@@ -741,6 +742,58 @@ func IsProxyEnabled() bool {
 	return enabled
 	return enabled
 }
 }
 
 
+// GetNetworkLimit - fetches free tier limits on users
+func GetUserLimit() int {
+	var userslimit int
+	if os.Getenv("USERS_LIMIT") != "" {
+		userslimit, _ = strconv.Atoi(os.Getenv("USERS_LIMIT"))
+	} else {
+		userslimit = config.Config.Server.UsersLimit
+	}
+	return userslimit
+}
+
+// GetNetworkLimit - fetches free tier limits on networks
+func GetNetworkLimit() int {
+	var networkslimit int
+	if os.Getenv("NETWORKS_LIMIT") != "" {
+		networkslimit, _ = strconv.Atoi(os.Getenv("NETWORKS_LIMIT"))
+	} else {
+		networkslimit = config.Config.Server.NetworksLimit
+	}
+	return networkslimit
+}
+
+// GetClientLimit - fetches free tier limits on ext. clients
+func GetClientLimit() int {
+	var clientsLimit int
+	if os.Getenv("CLIENTS_LIMIT") != "" {
+		clientsLimit, _ = strconv.Atoi(os.Getenv("CLIENTS_LIMIT"))
+	} else {
+		clientsLimit = config.Config.Server.ClientsLimit
+	}
+	return clientsLimit
+}
+
+// GetHostLimit - fetches free tier limits on hosts
+func GetHostLimit() int {
+	var hostsLimit int
+	if os.Getenv("HOSTS_LIMIT") != "" {
+		hostsLimit, _ = strconv.Atoi(os.Getenv("HOSTS_LIMIT"))
+	} else {
+		hostsLimit = config.Config.Server.HostsLimit
+	}
+	return hostsLimit
+}
+
+// DeployedByOperator - returns true if the instance is deployed by netmaker operator
+func DeployedByOperator() bool {
+	if os.Getenv("DEPLOYED_BY_OPERATOR") != "" {
+		return os.Getenv("DEPLOYED_BY_OPERATOR") == "true"
+	}
+	return config.Config.Server.DeployedByOperator
+}
+
 // GetDefaultProxyMode - default proxy mode for a server
 // GetDefaultProxyMode - default proxy mode for a server
 func GetDefaultProxyMode() config.ProxyMode {
 func GetDefaultProxyMode() config.ProxyMode {
 	var (
 	var (