abhishek9686 1 день назад
Родитель
Сommit
6261ab19a1
21 измененных файлов с 400 добавлено и 243 удалено
  1. 1 1
      Dockerfile
  2. 1 1
      Dockerfile-quick
  3. 36 17
      auth/host_session.go
  4. 1 50
      controllers/server.go
  5. 15 15
      go.mod
  6. 40 38
      go.sum
  7. 49 14
      logic/dns.go
  8. 25 0
      logic/nodes.go
  9. 2 2
      logic/security.go
  10. 78 0
      logic/usage.go
  11. 1 0
      models/structs.go
  12. 40 0
      models/usage.go
  13. 1 0
      models/user_mgmt.go
  14. 3 0
      pro/controllers/users.go
  15. 1 1
      pro/license.go
  16. 36 4
      pro/logic/dns.go
  17. 18 0
      pro/logic/security.go
  18. 23 1
      pro/logic/user_mgmt.go
  19. 3 29
      pro/types.go
  20. 0 44
      pro/util.go
  21. 26 26
      scripts/nm-quick.sh

+ 1 - 1
Dockerfile

@@ -6,7 +6,7 @@ COPY . .
 
 RUN GOOS=linux CGO_ENABLED=1 go build -ldflags="-s -w " -tags ${tags} .
 # RUN go build -tags=ee . -o netmaker main.go
-FROM alpine:3.22.1
+FROM alpine:3.22.2
 
 # add a c lib
 # set the working directory

+ 1 - 1
Dockerfile-quick

@@ -1,5 +1,5 @@
 #first stage - builder
-FROM alpine:3.22.1
+FROM alpine:3.22.2
 ARG version 
 WORKDIR /app
 COPY ./netmaker /root/netmaker

+ 36 - 17
auth/host_session.go

@@ -226,7 +226,7 @@ func SessionHandler(conn *websocket.Conn) {
 		if err = conn.WriteMessage(messageType, reponseData); err != nil {
 			logger.Log(0, "error during message writing:", err.Error())
 		}
-		go CheckNetRegAndHostUpdate(models.EnrollmentKey{Value: "user auth", Tags: []string{registerMessage.User}, Networks: netsToAdd}, &result.Host, "")
+		go CheckNetRegAndHostUpdate(models.EnrollmentKey{Networks: netsToAdd}, &result.Host, result.User)
 	case <-timeout: // the read from req.answerCh has timed out
 		logger.Log(0, "timeout signal recv,exiting oauth socket conn")
 		break
@@ -272,22 +272,41 @@ func CheckNetRegAndHostUpdate(key models.EnrollmentKey, h *models.Host, username
 				continue
 			}
 
-			logic.LogEvent(&models.Event{
-				Action: models.JoinHostToNet,
-				Source: models.Subject{
-					ID:   key.Value,
-					Name: key.Tags[0],
-					Type: models.EnrollmentKeySub,
-				},
-				TriggeredBy: username,
-				Target: models.Subject{
-					ID:   h.ID.String(),
-					Name: h.Name,
-					Type: models.DeviceSub,
-				},
-				NetworkID: models.NetworkID(netID),
-				Origin:    models.Dashboard,
-			})
+			if len(username) > 0 {
+				logic.LogEvent(&models.Event{
+					Action: models.JoinHostToNet,
+					Source: models.Subject{
+						ID:   username,
+						Name: username,
+						Type: models.UserSub,
+					},
+					TriggeredBy: username,
+					Target: models.Subject{
+						ID:   h.ID.String(),
+						Name: h.Name,
+						Type: models.DeviceSub,
+					},
+					NetworkID: models.NetworkID(netID),
+					Origin:    models.Dashboard,
+				})
+			} else {
+				logic.LogEvent(&models.Event{
+					Action: models.JoinHostToNet,
+					Source: models.Subject{
+						ID:   key.Value,
+						Name: key.Tags[0],
+						Type: models.EnrollmentKeySub,
+					},
+					TriggeredBy: username,
+					Target: models.Subject{
+						ID:   h.ID.String(),
+						Name: h.Name,
+						Type: models.DeviceSub,
+					},
+					NetworkID: models.NetworkID(netID),
+					Origin:    models.Dashboard,
+				})
+			}
 
 			newNode, err := logic.UpdateHostNetwork(h, netID, true)
 			if err == nil || strings.Contains(err.Error(), "host already part of network") {

+ 1 - 50
controllers/server.go

@@ -1,7 +1,6 @@
 package controller
 
 import (
-	"context"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -12,8 +11,6 @@ import (
 	"time"
 
 	"github.com/google/go-cmp/cmp"
-	"github.com/gravitl/netmaker/db"
-	"github.com/gravitl/netmaker/schema"
 
 	"github.com/gorilla/mux"
 	"golang.org/x/exp/slog"
@@ -83,56 +80,10 @@ func memProfile(w http.ResponseWriter, r *http.Request) {
 }
 
 func getUsage(w http.ResponseWriter, _ *http.Request) {
-	type usage struct {
-		Hosts            int `json:"hosts"`
-		Clients          int `json:"clients"`
-		Networks         int `json:"networks"`
-		Users            int `json:"users"`
-		Ingresses        int `json:"ingresses"`
-		Egresses         int `json:"egresses"`
-		Relays           int `json:"relays"`
-		InternetGateways int `json:"internet_gateways"`
-		FailOvers        int `json:"fail_overs"`
-	}
-	var serverUsage usage
-	hosts, err := logic.GetAllHostsWithStatus(models.OnlineSt)
-	if err == nil {
-		serverUsage.Hosts = len(hosts)
-	}
-	clients, err := logic.GetAllExtClientsWithStatus(models.OnlineSt)
-	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)
-	}
-	// TODO this part bellow can be optimized to get nodes just once
-	ingresses, err := logic.GetAllIngresses()
-	if err == nil {
-		serverUsage.Ingresses = len(ingresses)
-	}
-	serverUsage.Egresses, _ = (&schema.Egress{}).Count(db.WithContext(context.TODO()))
-	relays, err := logic.GetRelays()
-	if err == nil {
-		serverUsage.Relays = len(relays)
-	}
-	gateways, err := logic.GetInternetGateways()
-	if err == nil {
-		serverUsage.InternetGateways = len(gateways)
-	}
-	failOvers, err := logic.GetAllFailOvers()
-	if err == nil {
-		serverUsage.FailOvers = len(failOvers)
-	}
 	w.Header().Set("Content-Type", "application/json")
 	json.NewEncoder(w).Encode(models.SuccessResponse{
 		Code:     http.StatusOK,
-		Response: serverUsage,
+		Response: logic.GetCurrentServerUsage(),
 	})
 }
 

+ 15 - 15
go.mod

@@ -4,7 +4,7 @@ go 1.24.0
 
 require (
 	github.com/blang/semver v3.5.1+incompatible
-	github.com/eclipse/paho.mqtt.golang v1.5.0
+	github.com/eclipse/paho.mqtt.golang v1.5.1
 	github.com/go-playground/validator/v10 v10.27.0
 	github.com/golang-jwt/jwt/v4 v4.5.2
 	github.com/google/uuid v1.6.0
@@ -19,8 +19,8 @@ require (
 	github.com/txn2/txeh v1.5.5
 	go.uber.org/automaxprocs v1.6.0
 	golang.org/x/crypto v0.42.0
-	golang.org/x/net v0.43.0 // indirect
-	golang.org/x/oauth2 v0.30.0
+	golang.org/x/net v0.44.0 // indirect
+	golang.org/x/oauth2 v0.32.0
 	golang.org/x/sys v0.36.0 // indirect
 	golang.org/x/text v0.29.0 // indirect
 	golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb
@@ -30,7 +30,7 @@ require (
 require (
 	filippo.io/edwards25519 v1.1.0
 	github.com/c-robinson/iplib v1.0.8
-	github.com/posthog/posthog-go v1.6.8
+	github.com/posthog/posthog-go v1.6.11
 )
 
 require (
@@ -47,24 +47,24 @@ require (
 	github.com/okta/okta-sdk-golang/v5 v5.0.6
 	github.com/pquerna/otp v1.5.0
 	github.com/spf13/cobra v1.10.1
-	google.golang.org/api v0.248.0
+	google.golang.org/api v0.252.0
 	gopkg.in/mail.v2 v2.3.1
-	gorm.io/datatypes v1.2.6
+	gorm.io/datatypes v1.2.7
 	gorm.io/driver/postgres v1.6.0
 	gorm.io/driver/sqlite v1.6.0
 	gorm.io/gorm v1.31.0
 )
 
 require (
-	cloud.google.com/go/auth v0.16.5 // indirect
+	cloud.google.com/go/auth v0.17.0 // indirect
 	cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
-	cloud.google.com/go/compute/metadata v0.8.0 // indirect
+	cloud.google.com/go/compute/metadata v0.9.0 // indirect
 	github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
 	github.com/cenkalti/backoff/v4 v4.1.3 // indirect
 	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
 	github.com/gabriel-vasile/mimetype v1.4.8 // indirect
 	github.com/go-jose/go-jose/v3 v3.0.3 // indirect
-	github.com/go-jose/go-jose/v4 v4.0.5 // indirect
+	github.com/go-jose/go-jose/v4 v4.1.1 // indirect
 	github.com/go-logr/logr v1.4.3 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-sql-driver/mysql v1.8.1 // indirect
@@ -95,12 +95,12 @@ require (
 	github.com/spf13/pflag v1.0.9 // indirect
 	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
-	go.opentelemetry.io/otel v1.36.0 // indirect
-	go.opentelemetry.io/otel/metric v1.36.0 // indirect
-	go.opentelemetry.io/otel/trace v1.36.0 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect
-	google.golang.org/grpc v1.74.2 // indirect
-	google.golang.org/protobuf v1.36.7 // indirect
+	go.opentelemetry.io/otel v1.37.0 // indirect
+	go.opentelemetry.io/otel/metric v1.37.0 // indirect
+	go.opentelemetry.io/otel/trace v1.37.0 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 // indirect
+	google.golang.org/grpc v1.75.1 // indirect
+	google.golang.org/protobuf v1.36.10 // indirect
 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 	gorm.io/driver/mysql v1.5.6 // indirect
 )

+ 40 - 38
go.sum

@@ -1,9 +1,9 @@
-cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI=
-cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ=
+cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4=
+cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ=
 cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
 cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
-cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA=
-cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw=
+cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
+cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
 filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
 filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
 github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
@@ -23,16 +23,16 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
-github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o=
-github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk=
+github.com/eclipse/paho.mqtt.golang v1.5.1 h1:/VSOv3oDLlpqR2Epjn1Q7b2bSTplJIeV2ISgCl2W7nE=
+github.com/eclipse/paho.mqtt.golang v1.5.1/go.mod h1:1/yJCneuyOoCOzKSsOTUc0AJfpsItBGWvYpBLimhArU=
 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
 github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
 github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
 github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
-github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
-github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
+github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI=
+github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
 github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -140,8 +140,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/posthog/posthog-go v1.6.8 h1:l5H05oKqiZbLYAjxrus3Rvj606bdEu+7EDAhKnSgU6I=
-github.com/posthog/posthog-go v1.6.8/go.mod h1:LcC1Nu4AgvV22EndTtrMXTy+7RGVC0MhChSw7Qk5XkY=
+github.com/posthog/posthog-go v1.6.11 h1:5G8Y3pxnOpc3S4+PK1z1dCmZRuldiWxBsqqvvSfC2+w=
+github.com/posthog/posthog-go v1.6.11/go.mod h1:LcC1Nu4AgvV22EndTtrMXTy+7RGVC0MhChSw7Qk5XkY=
 github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
 github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
 github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
@@ -186,16 +186,16 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
-go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
-go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
-go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
-go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
-go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
-go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
-go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
-go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
-go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
-go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
+go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
+go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
+go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
+go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
+go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
+go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
+go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
+go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
+go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
+go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
 go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
 go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -214,10 +214,10 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
-golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
-golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
-golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
-golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
+golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
+golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
+golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
+golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -248,8 +248,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
 golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
-golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
-golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
+golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
+golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
@@ -257,18 +257,20 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb h1:9aqVcYEDHmSNb0uOWukxV5lHV09WqiSiCuhEgWNETLY=
 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb/go.mod h1:mQqgjkW8GQQcJQsbBvK890TKqUK1DfKWkuBGbOkuMHQ=
-google.golang.org/api v0.248.0 h1:hUotakSkcwGdYUqzCRc5yGYsg4wXxpkKlW5ryVqvC1Y=
-google.golang.org/api v0.248.0/go.mod h1:yAFUAF56Li7IuIQbTFoLwXTCI6XCFKueOlS7S9e4F9k=
+gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
+gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
+google.golang.org/api v0.252.0 h1:xfKJeAJaMwb8OC9fesr369rjciQ704AjU/psjkKURSI=
+google.golang.org/api v0.252.0/go.mod h1:dnHOv81x5RAmumZ7BWLShB/u7JZNeyalImxHmtTHxqw=
 google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
 google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
-google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
-google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=
-google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
-google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
-google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
-google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
+google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
+google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 h1:CirRxTOwnRWVLKzDNrs0CXAaVozJoR4G9xvdRecrdpk=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
+google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
+google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
+google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
+google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -279,8 +281,8 @@ gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gorm.io/datatypes v1.2.6 h1:KafLdXvFUhzNeL2ncm03Gl3eTLONQfNKZ+wJ+9Y4Nck=
-gorm.io/datatypes v1.2.6/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY=
+gorm.io/datatypes v1.2.7 h1:ww9GAhF1aGXZY3EB3cJPJ7//JiuQo7DlQA7NNlVaTdk=
+gorm.io/datatypes v1.2.7/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY=
 gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
 gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
 gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=

+ 49 - 14
logic/dns.go

@@ -432,24 +432,15 @@ func validateNameserverReq(ns schema.Nameserver) error {
 	if len(ns.Servers) == 0 {
 		return errors.New("atleast one nameserver should be specified")
 	}
-	network, err := GetNetwork(ns.NetworkID)
+	_, err := GetNetwork(ns.NetworkID)
 	if err != nil {
 		return errors.New("invalid network id")
 	}
-	_, cidr, err4 := net.ParseCIDR(network.AddressRange)
-	_, cidr6, err6 := net.ParseCIDR(network.AddressRange6)
 	for _, nsIPStr := range ns.Servers {
 		nsIP := net.ParseIP(nsIPStr)
 		if nsIP == nil {
 			return errors.New("invalid nameserver " + nsIPStr)
 		}
-		if err4 == nil && nsIP.To4() != nil {
-			if cidr.Contains(nsIP) {
-				return errors.New("cannot use netmaker IP as nameserver")
-			}
-		} else if err6 == nil && cidr6.Contains(nsIP) {
-			return errors.New("cannot use netmaker IP as nameserver")
-		}
 	}
 	if !ns.MatchAll && len(ns.MatchDomains) == 0 {
 		return errors.New("atleast one match domain is required")
@@ -478,6 +469,15 @@ func validateNameserverReq(ns schema.Nameserver) error {
 }
 
 func getNameserversForNode(node *models.Node) (returnNsLi []models.Nameserver) {
+	filters := make(map[string]bool)
+	if node.Address.IP != nil {
+		filters[node.Address.IP.String()] = true
+	}
+
+	if node.Address6.IP != nil {
+		filters[node.Address6.IP.String()] = true
+	}
+
 	ns := &schema.Nameserver{
 		NetworkID: node.Network,
 	}
@@ -486,11 +486,17 @@ func getNameserversForNode(node *models.Node) (returnNsLi []models.Nameserver) {
 		if !nsI.Status {
 			continue
 		}
+
+		filteredIps := FilterOutIPs(nsI.Servers, filters)
+		if len(filteredIps) == 0 {
+			continue
+		}
+
 		_, all := nsI.Tags["*"]
 		if all {
 			for _, matchDomain := range nsI.MatchDomains {
 				returnNsLi = append(returnNsLi, models.Nameserver{
-					IPs:         nsI.Servers,
+					IPs:         filteredIps,
 					MatchDomain: matchDomain,
 				})
 			}
@@ -500,7 +506,7 @@ func getNameserversForNode(node *models.Node) (returnNsLi []models.Nameserver) {
 		if _, ok := nsI.Nodes[node.ID.String()]; ok {
 			for _, matchDomain := range nsI.MatchDomains {
 				returnNsLi = append(returnNsLi, models.Nameserver{
-					IPs:         nsI.Servers,
+					IPs:         filteredIps,
 					MatchDomain: matchDomain,
 				})
 			}
@@ -528,6 +534,16 @@ func getNameserversForHost(h *models.Host) (returnNsLi []models.Nameserver) {
 		if err != nil {
 			continue
 		}
+
+		filters := make(map[string]bool)
+		if node.Address.IP != nil {
+			filters[node.Address.IP.String()] = true
+		}
+
+		if node.Address6.IP != nil {
+			filters[node.Address6.IP.String()] = true
+		}
+
 		ns := &schema.Nameserver{
 			NetworkID: node.Network,
 		}
@@ -536,11 +552,17 @@ func getNameserversForHost(h *models.Host) (returnNsLi []models.Nameserver) {
 			if !nsI.Status {
 				continue
 			}
+
+			filteredIps := FilterOutIPs(nsI.Servers, filters)
+			if len(filteredIps) == 0 {
+				continue
+			}
+
 			_, all := nsI.Tags["*"]
 			if all {
 				for _, matchDomain := range nsI.MatchDomains {
 					returnNsLi = append(returnNsLi, models.Nameserver{
-						IPs:         nsI.Servers,
+						IPs:         filteredIps,
 						MatchDomain: matchDomain,
 					})
 				}
@@ -550,7 +572,7 @@ func getNameserversForHost(h *models.Host) (returnNsLi []models.Nameserver) {
 			if _, ok := nsI.Nodes[node.ID.String()]; ok {
 				for _, matchDomain := range nsI.MatchDomains {
 					returnNsLi = append(returnNsLi, models.Nameserver{
-						IPs:         nsI.Servers,
+						IPs:         filteredIps,
 						MatchDomain: matchDomain,
 					})
 				}
@@ -631,3 +653,16 @@ func IsValidMatchDomain(s string) bool {
 	}
 	return true
 }
+
+// FilterOutIPs removes ips in the filters map from the ips slice.
+func FilterOutIPs(ips []string, filters map[string]bool) []string {
+	var filteredIps []string
+	for _, ip := range ips {
+		_, ok := filters[ip]
+		if !ok {
+			filteredIps = append(filteredIps, ip)
+		}
+	}
+
+	return filteredIps
+}

+ 25 - 0
logic/nodes.go

@@ -15,10 +15,12 @@ import (
 	validator "github.com/go-playground/validator/v10"
 	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/db"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic/acls"
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
 	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/schema"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/validation"
 	"github.com/seancfoley/ipaddress-go/ipaddr"
@@ -325,6 +327,29 @@ func DeleteNode(node *models.Node, purge bool) error {
 		return err
 	}
 
+	filters := make(map[string]bool)
+	if node.Address.IP != nil {
+		filters[node.Address.IP.String()] = true
+	}
+
+	if node.Address6.IP != nil {
+		filters[node.Address6.IP.String()] = true
+	}
+
+	nameservers, _ := (&schema.Nameserver{
+		NetworkID: node.Network,
+	}).ListByNetwork(db.WithContext(context.TODO()))
+	for _, ns := range nameservers {
+		ns.Servers = FilterOutIPs(ns.Servers, filters)
+		if len(ns.Servers) > 0 {
+			_ = ns.Update(db.WithContext(context.TODO()))
+		} else {
+			// TODO: deleting a nameserver dns server could cause trouble for other nodes.
+			// TODO: try to figure out a sequence that works the best.
+			_ = ns.Delete(db.WithContext(context.TODO()))
+		}
+	}
+
 	go RemoveNodeFromAclPolicy(*node)
 	go RemoveNodeFromEgress(*node)
 	return nil

+ 2 - 2
logic/security.go

@@ -2,10 +2,11 @@ package logic
 
 import (
 	"errors"
-	"github.com/golang-jwt/jwt/v4"
 	"net/http"
 	"strings"
 
+	"github.com/golang-jwt/jwt/v4"
+
 	"github.com/gorilla/mux"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
@@ -24,7 +25,6 @@ var GlobalPermissionsCheck = func(username string, r *http.Request) error { retu
 
 // SecurityCheck - Check if user has appropriate permissions
 func SecurityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc {
-
 	return func(w http.ResponseWriter, r *http.Request) {
 		r.Header.Set("ismaster", "no")
 		isGlobalAccesss := r.Header.Get("IS_GLOBAL_ACCESS") == "yes"

+ 78 - 0
logic/usage.go

@@ -0,0 +1,78 @@
+package logic
+
+import (
+	"context"
+
+	"github.com/gravitl/netmaker/db"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/schema"
+)
+
+func GetCurrentServerUsage() (limits models.Usage) {
+	limits.SetDefaults()
+	hosts, hErr := GetAllHostsWithStatus(models.OnlineSt)
+	if hErr == nil {
+		limits.Hosts = len(hosts)
+	}
+	clients, cErr := GetAllExtClientsWithStatus(models.OnlineSt)
+	if cErr == nil {
+		limits.Clients = len(clients)
+	}
+	users, err := GetUsers()
+	if err == nil {
+		limits.Users = len(users)
+	}
+	networks, err := GetNetworks()
+	if err == nil {
+		limits.Networks = len(networks)
+	}
+	limits.Egresses, _ = (&schema.Egress{}).Count(db.WithContext(context.TODO()))
+
+	nodes, _ := GetAllNodes()
+
+	for _, client := range clients {
+		nodes = append(nodes, client.ConvertToStaticNode())
+	}
+
+	limits.NetworkUsage = make(map[string]models.NetworkUsage)
+	for _, network := range networks {
+		limits.NetworkUsage[network.NetID] = models.NetworkUsage{}
+	}
+
+	for _, node := range nodes {
+		netUsage, ok := limits.NetworkUsage[node.Network]
+		if !ok {
+			// if network doesn't exist, this node is probably awaiting cleanup.
+			// so ignore.
+			continue
+		}
+
+		netUsage.Nodes++
+		if node.IsStatic {
+			netUsage.Clients++
+		}
+		if node.IsIngressGateway {
+			limits.Ingresses++
+			netUsage.Ingresses++
+		}
+		if node.EgressDetails.IsEgressGateway {
+			netUsage.Egresses++
+		}
+		if node.IsRelay {
+			limits.Relays++
+			netUsage.Relays++
+		}
+		if node.IsInternetGateway {
+			limits.InternetGateways++
+			netUsage.InternetGateways++
+		}
+		if node.IsFailOver {
+			limits.FailOvers++
+			netUsage.FailOvers++
+		}
+
+		limits.NetworkUsage[node.Network] = netUsage
+	}
+
+	return
+}

+ 1 - 0
models/structs.go

@@ -52,6 +52,7 @@ type UserRemoteGws struct {
 	AllowedEndpoints  []string   `json:"allowed_endpoints"`
 	NetworkAddresses  []string   `json:"network_addresses"`
 	Status            NodeStatus `json:"status"`
+	ManageDNS         bool       `json:"manage_dns"`
 	DnsAddress        string     `json:"dns_address"`
 	Addresses         string     `json:"addresses"`
 	MatchDomains      []string   `json:"match_domains"`

+ 40 - 0
models/usage.go

@@ -0,0 +1,40 @@
+package models
+
+// Usage - struct for license usage
+type Usage struct {
+	Servers          int                     `json:"servers"`
+	Users            int                     `json:"users"`
+	Hosts            int                     `json:"hosts"`
+	Clients          int                     `json:"clients"`
+	Networks         int                     `json:"networks"`
+	Ingresses        int                     `json:"ingresses"`
+	Egresses         int                     `json:"egresses"`
+	Relays           int                     `json:"relays"`
+	InternetGateways int                     `json:"internet_gateways"`
+	FailOvers        int                     `json:"fail_overs"`
+	NetworkUsage     map[string]NetworkUsage `json:"network_usage"`
+}
+
+type NetworkUsage struct {
+	Nodes            int `json:"nodes"`
+	Clients          int `json:"clients"`
+	Ingresses        int `json:"ingresses"`
+	Egresses         int `json:"egresses"`
+	Relays           int `json:"relays"`
+	InternetGateways int `json:"internet_gateways"`
+	FailOvers        int `json:"fail_overs"`
+}
+
+// SetDefaults - sets the default values for usage
+func (l *Usage) SetDefaults() {
+	l.Clients = 0
+	l.Servers = 1
+	l.Hosts = 0
+	l.Users = 1
+	l.Networks = 0
+	l.Ingresses = 0
+	l.Egresses = 0
+	l.Relays = 0
+	l.InternetGateways = 0
+	l.NetworkUsage = make(map[string]NetworkUsage)
+}

+ 1 - 0
models/user_mgmt.go

@@ -102,6 +102,7 @@ const (
 	AdminRole      UserRoleID = "admin"
 	ServiceUser    UserRoleID = "service-user"
 	PlatformUser   UserRoleID = "platform-user"
+	Auditor        UserRoleID = "auditor"
 	NetworkAdmin   UserRoleID = "network-admin"
 	NetworkUser    UserRoleID = "network-user"
 )

+ 3 - 0
pro/controllers/users.go

@@ -1399,6 +1399,7 @@ func getRemoteAccessGatewayConf(w http.ResponseWriter, r *http.Request) {
 		Metadata:          node.Metadata,
 		AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
 		NetworkAddresses:  []string{network.AddressRange, network.AddressRange6},
+		ManageDNS:         host.DNS == "yes",
 		DnsAddress:        node.IngressDNS,
 		Addresses:         utils.NoEmptyStringToCsv(node.Address.String(), node.Address6.String()),
 	}
@@ -1550,6 +1551,7 @@ func getUserRemoteAccessGwsV1(w http.ResponseWriter, r *http.Request) {
 			AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
 			NetworkAddresses:  []string{network.AddressRange, network.AddressRange6},
 			Status:            node.Status,
+			ManageDNS:         host.DNS == "yes",
 			DnsAddress:        node.IngressDNS,
 			Addresses:         utils.NoEmptyStringToCsv(node.Address.String(), node.Address6.String()),
 		}
@@ -1601,6 +1603,7 @@ func getUserRemoteAccessGwsV1(w http.ResponseWriter, r *http.Request) {
 			AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
 			NetworkAddresses:  []string{network.AddressRange, network.AddressRange6},
 			Status:            node.Status,
+			ManageDNS:         host.DNS == "yes",
 			DnsAddress:        node.IngressDNS,
 			Addresses:         utils.NoEmptyStringToCsv(node.Address.String(), node.Address6.String()),
 		}

+ 1 - 1
pro/license.go

@@ -85,7 +85,7 @@ func ValidateLicense() (err error) {
 
 	licenseSecret := LicenseSecret{
 		AssociatedID: netmakerTenantID,
-		Usage:        getCurrentServerUsage(),
+		Usage:        logic.GetCurrentServerUsage(),
 	}
 
 	secretData, err := json.Marshal(&licenseSecret)

+ 36 - 4
pro/logic/dns.go

@@ -65,6 +65,15 @@ func ValidateNameserverReq(ns schema.Nameserver) error {
 }
 
 func GetNameserversForNode(node *models.Node) (returnNsLi []models.Nameserver) {
+	filters := make(map[string]bool)
+	if node.Address.IP != nil {
+		filters[node.Address.IP.String()] = true
+	}
+
+	if node.Address6.IP != nil {
+		filters[node.Address6.IP.String()] = true
+	}
+
 	ns := &schema.Nameserver{
 		NetworkID: node.Network,
 	}
@@ -73,11 +82,17 @@ func GetNameserversForNode(node *models.Node) (returnNsLi []models.Nameserver) {
 		if !nsI.Status {
 			continue
 		}
+
+		filteredIps := logic.FilterOutIPs(nsI.Servers, filters)
+		if len(filteredIps) == 0 {
+			continue
+		}
+
 		_, all := nsI.Tags["*"]
 		if all {
 			for _, matchDomain := range nsI.MatchDomains {
 				returnNsLi = append(returnNsLi, models.Nameserver{
-					IPs:         nsI.Servers,
+					IPs:         filteredIps,
 					MatchDomain: matchDomain,
 				})
 			}
@@ -88,7 +103,7 @@ func GetNameserversForNode(node *models.Node) (returnNsLi []models.Nameserver) {
 			if _, ok := nsI.Tags[tagI.String()]; ok {
 				for _, matchDomain := range nsI.MatchDomains {
 					returnNsLi = append(returnNsLi, models.Nameserver{
-						IPs:         nsI.Servers,
+						IPs:         filteredIps,
 						MatchDomain: matchDomain,
 					})
 				}
@@ -126,11 +141,22 @@ func GetNameserversForHost(h *models.Host) (returnNsLi []models.Nameserver) {
 	if h.DNS != "yes" {
 		return
 	}
+
 	for _, nodeID := range h.Nodes {
 		node, err := logic.GetNodeByID(nodeID)
 		if err != nil {
 			continue
 		}
+
+		filters := make(map[string]bool)
+		if node.Address.IP != nil {
+			filters[node.Address.IP.String()] = true
+		}
+
+		if node.Address6.IP != nil {
+			filters[node.Address6.IP.String()] = true
+		}
+
 		ns := &schema.Nameserver{
 			NetworkID: node.Network,
 		}
@@ -139,11 +165,17 @@ func GetNameserversForHost(h *models.Host) (returnNsLi []models.Nameserver) {
 			if !nsI.Status {
 				continue
 			}
+
+			filteredIps := logic.FilterOutIPs(nsI.Servers, filters)
+			if len(filteredIps) == 0 {
+				continue
+			}
+
 			_, all := nsI.Tags["*"]
 			if all {
 				for _, matchDomain := range nsI.MatchDomains {
 					returnNsLi = append(returnNsLi, models.Nameserver{
-						IPs:         nsI.Servers,
+						IPs:         filteredIps,
 						MatchDomain: matchDomain,
 					})
 				}
@@ -154,7 +186,7 @@ func GetNameserversForHost(h *models.Host) (returnNsLi []models.Nameserver) {
 				if _, ok := nsI.Tags[tagI.String()]; ok {
 					for _, matchDomain := range nsI.MatchDomains {
 						returnNsLi = append(returnNsLi, models.Nameserver{
-							IPs:         nsI.Servers,
+							IPs:         filteredIps,
 							MatchDomain: matchDomain,
 						})
 					}

+ 18 - 0
pro/logic/security.go

@@ -44,6 +44,15 @@ func NetworkPermissionsCheck(username string, r *http.Request) error {
 	if userRole.FullAccess {
 		return nil
 	}
+
+	if userRole.ID == models.Auditor {
+		if r.Method == http.MethodGet {
+			return nil
+		} else {
+			return errors.New("access denied")
+		}
+	}
+
 	// get info from header to determine the target rsrc
 	targetRsrc := r.Header.Get("TARGET_RSRC")
 	targetRsrcID := r.Header.Get("TARGET_RSRC_ID")
@@ -160,6 +169,15 @@ func GlobalPermissionsCheck(username string, r *http.Request) error {
 	if userRole.FullAccess {
 		return nil
 	}
+
+	if userRole.ID == models.Auditor {
+		if r.Method == http.MethodGet {
+			return nil
+		} else {
+			return errors.New("access denied")
+		}
+	}
+
 	targetRsrc := r.Header.Get("TARGET_RSRC")
 	targetRsrcID := r.Header.Get("TARGET_RSRC_ID")
 	if targetRsrc == "" {

+ 23 - 1
pro/logic/user_mgmt.go

@@ -43,6 +43,20 @@ var PlatformUserUserPermissionTemplate = models.UserRolePermissionTemplate{
 	},
 }
 
+var AuditorUserPermissionTemplate = models.UserRolePermissionTemplate{
+	ID:                  models.Auditor,
+	Default:             true,
+	DenyDashboardAccess: false,
+	FullAccess:          false,
+	NetworkLevelAccess: map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope{
+		models.NetworkRsrc: {
+			models.AllNetworkRsrcID: models.RsrcPermissionScope{
+				Read: true,
+			},
+		},
+	},
+}
+
 var NetworkAdminAllPermissionTemplate = models.UserRolePermissionTemplate{
 	ID:         globalNetworksAdminRoleID,
 	Name:       "Network Admins",
@@ -122,6 +136,8 @@ func UserRolesInit() {
 	database.Insert(ServiceUserPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
 	d, _ = json.Marshal(PlatformUserUserPermissionTemplate)
 	database.Insert(PlatformUserUserPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
+	d, _ = json.Marshal(AuditorUserPermissionTemplate)
+	database.Insert(AuditorUserPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
 	d, _ = json.Marshal(NetworkAdminAllPermissionTemplate)
 	database.Insert(NetworkAdminAllPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
 	d, _ = json.Marshal(NetworkUserAllPermissionTemplate)
@@ -992,6 +1008,13 @@ func FilterNetworksByRole(allnetworks []models.Network, user models.User) []mode
 	}
 	if !platformRole.FullAccess {
 		allNetworkRoles := make(map[models.NetworkID]struct{})
+		_, ok := platformRole.NetworkLevelAccess[models.NetworkRsrc]
+		if ok {
+			perm, ok := platformRole.NetworkLevelAccess[models.NetworkRsrc][models.AllNetworkRsrcID]
+			if ok && perm.Read {
+				return allnetworks
+			}
+		}
 		if len(user.NetworkRoles) > 0 {
 			for netID := range user.NetworkRoles {
 				if netID == models.AllNetworks {
@@ -1011,7 +1034,6 @@ func FilterNetworksByRole(allnetworks []models.Network, user models.User) []mode
 								return allnetworks
 							}
 							allNetworkRoles[netID] = struct{}{}
-
 						}
 					}
 				}

+ 3 - 29
pro/types.go

@@ -5,6 +5,7 @@ package pro
 
 import (
 	"errors"
+
 	"github.com/gravitl/netmaker/models"
 )
 
@@ -40,35 +41,8 @@ type ValidatedLicense struct {
 
 // LicenseSecret - the encrypted struct for sending user-id
 type LicenseSecret struct {
-	AssociatedID string `json:"associated_id" binding:"required"` // UUID for user foreign key to User table
-	Usage        Usage  `json:"limits"        binding:"required"`
-}
-
-// Usage - struct for license usage
-type Usage struct {
-	Servers          int `json:"servers"`
-	Users            int `json:"users"`
-	Hosts            int `json:"hosts"`
-	Clients          int `json:"clients"`
-	Networks         int `json:"networks"`
-	Ingresses        int `json:"ingresses"`
-	Egresses         int `json:"egresses"`
-	Relays           int `json:"relays"`
-	InternetGateways int `json:"internet_gateways"`
-	FailOvers        int `json:"fail_overs"`
-}
-
-// Usage.SetDefaults - sets the default values for usage
-func (l *Usage) SetDefaults() {
-	l.Clients = 0
-	l.Servers = 1
-	l.Hosts = 0
-	l.Users = 1
-	l.Networks = 0
-	l.Ingresses = 0
-	l.Egresses = 0
-	l.Relays = 0
-	l.InternetGateways = 0
+	AssociatedID string       `json:"associated_id" binding:"required"` // UUID for user foreign key to User table
+	Usage        models.Usage `json:"limits"        binding:"required"`
 }
 
 // ValidateLicenseRequest - used for request to validate license endpoint

+ 0 - 44
pro/util.go

@@ -4,12 +4,7 @@
 package pro
 
 import (
-	"context"
 	"encoding/base64"
-	"github.com/gravitl/netmaker/db"
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/schema"
-	"github.com/gravitl/netmaker/logic"
 )
 
 // base64encode - base64 encode helper function
@@ -26,42 +21,3 @@ func base64decode(input string) []byte {
 
 	return bytes
 }
-
-func getCurrentServerUsage() (limits Usage) {
-	limits.SetDefaults()
-	hosts, hErr := logic.GetAllHostsWithStatus(models.OnlineSt)
-	if hErr == nil {
-		limits.Hosts = len(hosts)
-	}
-	clients, cErr := logic.GetAllExtClientsWithStatus(models.OnlineSt)
-	if cErr == nil {
-		limits.Clients = len(clients)
-	}
-	users, err := logic.GetUsers()
-	if err == nil {
-		limits.Users = len(users)
-	}
-	networks, err := logic.GetNetworks()
-	if err == nil {
-		limits.Networks = len(networks)
-	}
-	// TODO this part bellow can be optimized to get nodes just once
-	ingresses, err := logic.GetAllIngresses()
-	if err == nil {
-		limits.Ingresses = len(ingresses)
-	}
-	limits.Egresses, _ = (&schema.Egress{}).Count(db.WithContext(context.TODO()))
-	relays, err := logic.GetRelays()
-	if err == nil {
-		limits.Relays = len(relays)
-	}
-	gateways, err := logic.GetInternetGateways()
-	if err == nil {
-		limits.InternetGateways = len(gateways)
-	}
-	failovers, err := logic.GetAllFailOvers()
-	if err == nil {
-		limits.FailOvers = len(failovers)
-	}
-	return
-}

+ 26 - 26
scripts/nm-quick.sh

@@ -150,12 +150,12 @@ setup_netclient() {
 # configure_netclient - configures server's netclient as a default host and an ingress gateway
 configure_netclient() {
 	sleep 2
-	NODE_ID=$(sudo cat /etc/netclient/nodes.json | jq -r .netmaker.id)
-	if [ "$NODE_ID" = "" ] || [ "$NODE_ID" = "null" ]; then
-		echo "Error obtaining NODE_ID for the new network"
-		exit 1
-	fi
-	echo "register complete. New node ID: $NODE_ID"
+	# NODE_ID=$(sudo cat /etc/netclient/nodes.json | jq -r .netmaker.id)
+	# if [ "$NODE_ID" = "" ] || [ "$NODE_ID" = "null" ]; then
+	# 	echo "Error obtaining NODE_ID for the new network"
+	# 	exit 1
+	# fi
+	# echo "register complete. New node ID: $NODE_ID"
 	HOST_ID=$(sudo cat /etc/netclient/netclient.json | jq -r .id)
 	if [ "$HOST_ID" = "" ] || [ "$HOST_ID" = "null" ]; then
 		echo "Error obtaining HOST_ID for the new network"
@@ -167,13 +167,13 @@ configure_netclient() {
 	set +e
 	nmctl host update $HOST_ID --default
 	sleep 5
-	nmctl node create_remote_access_gateway netmaker $NODE_ID
-	sleep 2
-	# set failover
-	if [ "$INSTALL_TYPE" = "pro" ]; then
-	    #setup failOver
-		curl --location --request POST "https://api.${NETMAKER_BASE_DOMAIN}/api/v1/node/${NODE_ID}/failover" --header "Authorization: Bearer ${MASTER_KEY}"
-	fi
+	# nmctl node create_remote_access_gateway netmaker $NODE_ID
+	# sleep 2
+	# # set failover
+	# if [ "$INSTALL_TYPE" = "pro" ]; then
+	#     #setup failOver
+	# 	curl --location --request POST "https://api.${NETMAKER_BASE_DOMAIN}/api/v1/node/${NODE_ID}/failover" --header "Authorization: Bearer ${MASTER_KEY}"
+	# fi
 	set -e
 }
 
@@ -694,24 +694,24 @@ test_connection() {
 setup_mesh() {
 
 	wait_seconds 5
-	networks=$(nmctl network list -o json)
-	if [[ ${networks} != "null" ]]; then
-		netmakerNet=$(nmctl network list -o json | jq -r '.[] | .netid' | grep -w "netmaker")
-	fi
-	# create netmaker network
-	if [[ ${netmakerNet} = "" ]]; then
-		echo "Creating netmaker network (100.64.0.0/16)"
-		# TODO causes "Error Status: 400 Response: {"Code":400,"Message":"could not find any records"}"
-		nmctl network create --name netmaker --ipv4_addr 100.64.0.0/16
-	fi
+	# networks=$(nmctl network list -o json)
+	# if [[ ${networks} != "null" ]]; then
+	# 	netmakerNet=$(nmctl network list -o json | jq -r '.[] | .netid' | grep -w "netmaker")
+	# fi
+	# # create netmaker network
+	# if [[ ${netmakerNet} = "" ]]; then
+	# 	echo "Creating netmaker network (100.64.0.0/16)"
+	# 	# TODO causes "Error Status: 400 Response: {"Code":400,"Message":"could not find any records"}"
+	# 	nmctl network create --name netmaker --ipv4_addr 100.64.0.0/16
+	# fi
 	# create enrollment key for netmaker network
-	local netmakerTag=$(nmctl enrollment_key list | jq -r '.[] | .tags[0]' | grep -w "netmaker")
+	local netmakerTag=$(nmctl enrollment_key list | jq -r '.[] | .tags[0]' | grep -w "firstJoinKey")
 	if [[ ${netmakerTag} = "" ]]; then
-		nmctl enrollment_key create --tags netmaker --unlimited --networks netmaker
+		nmctl enrollment_key create --tags firstJoinKey --unlimited
 	fi
 	echo "Obtaining enrollment key..."
 	# key exists already, fetch token
-	TOKEN=$(nmctl enrollment_key list | jq -r '.[] | select(.tags[0]=="netmaker") | .token')
+	TOKEN=$(nmctl enrollment_key list | jq -r '.[] | select(.tags[0]=="firstJoinKey") | .token')
 	wait_seconds 3
 }