Переглянути джерело

Merge branch 'develop' into NET-822

Abhishek K 1 рік тому
батько
коміт
8aca52af79

+ 2 - 2
.github/workflows/deletedroplets.yml

@@ -12,7 +12,7 @@ jobs:
     if: ${{ github.event.workflow_run.conclusion == 'success' }}
     steps:
       - name: get logs
-        uses: dawidd6/action-download-artifact@v2
+        uses: dawidd6/action-download-artifact@v3
         with:
           run_id: ${{ github.event.workflow_run.id}}
           if_no_artifact_found: warn
@@ -60,7 +60,7 @@ jobs:
     if: ${{ github.event.workflow_run.conclusion == 'failure' }}
     steps:
       - name: get logs
-        uses: dawidd6/action-download-artifact@v2
+        uses: dawidd6/action-download-artifact@v3
         with:
           run_id: ${{ github.event.workflow_run.id}}
           if_no_artifact_found: warn

+ 9 - 0
auth/azure-ad.go

@@ -66,6 +66,15 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 	}
+	user, err := logic.GetUser(content.Email)
+	if err != nil {
+		handleOauthUserNotFound(w)
+		return
+	}
+	if !(user.IsSuperAdmin || user.IsAdmin) {
+		handleOauthUserNotAllowed(w)
+		return
+	}
 	var newPass, fetchErr = fetchPassValue("")
 	if fetchErr != nil {
 		return

+ 25 - 0
auth/error.go

@@ -10,6 +10,31 @@ const oauthNotConfigured = `<!DOCTYPE html><html>
 </body>
 </html>`
 
+const userNotAllowed = `<!DOCTYPE html><html>
+<body>
+<h3>Only Admins are allowed to access Dashboard.</h3>
+<p>Non-Admins can access the netmaker networks using <a href="https://docs.netmaker.io/pro/rac.html" target="_blank" rel="noopener">RemoteAccessClient.</a></p>
+</body>
+</html>
+`
+const userNotFound = `<!DOCTYPE html><html>
+<body>
+<h3>User Not Found.</h3>
+</body>
+</html>`
+
+func handleOauthUserNotFound(response http.ResponseWriter) {
+	response.Header().Set("Content-Type", "text/html; charset=utf-8")
+	response.WriteHeader(http.StatusNotFound)
+	response.Write([]byte(userNotFound))
+}
+
+func handleOauthUserNotAllowed(response http.ResponseWriter) {
+	response.Header().Set("Content-Type", "text/html; charset=utf-8")
+	response.WriteHeader(http.StatusForbidden)
+	response.Write([]byte(userNotAllowed))
+}
+
 // handleOauthNotConfigured - returns an appropriate html page when oauth is not configured on netmaker server but an oauth login was attempted
 func handleOauthNotConfigured(response http.ResponseWriter) {
 	response.Header().Set("Content-Type", "text/html; charset=utf-8")

+ 9 - 0
auth/github.go

@@ -66,6 +66,15 @@ func handleGithubCallback(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 	}
+	user, err := logic.GetUser(content.Email)
+	if err != nil {
+		handleOauthUserNotFound(w)
+		return
+	}
+	if !(user.IsSuperAdmin || user.IsAdmin) {
+		handleOauthUserNotAllowed(w)
+		return
+	}
 	var newPass, fetchErr = fetchPassValue("")
 	if fetchErr != nil {
 		return

+ 9 - 0
auth/google.go

@@ -68,6 +68,15 @@ func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 	}
+	user, err := logic.GetUser(content.Email)
+	if err != nil {
+		handleOauthUserNotFound(w)
+		return
+	}
+	if !(user.IsSuperAdmin || user.IsAdmin) {
+		handleOauthUserNotAllowed(w)
+		return
+	}
 	var newPass, fetchErr = fetchPassValue("")
 	if fetchErr != nil {
 		return

+ 9 - 0
auth/oidc.go

@@ -79,6 +79,15 @@ func handleOIDCCallback(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 	}
+	user, err := logic.GetUser(content.Email)
+	if err != nil {
+		handleOauthUserNotFound(w)
+		return
+	}
+	if !(user.IsSuperAdmin || user.IsAdmin) {
+		handleOauthUserNotAllowed(w)
+		return
+	}
 	var newPass, fetchErr = fetchPassValue("")
 	if fetchErr != nil {
 		return

+ 27 - 9
cli/cmd/acl/allow.go

@@ -2,6 +2,7 @@ package acl
 
 import (
 	"fmt"
+	"log"
 
 	"github.com/gravitl/netmaker/cli/functions"
 	"github.com/gravitl/netmaker/logic/acls"
@@ -14,17 +15,34 @@ var aclAllowCmd = &cobra.Command{
 	Short: "Allow access from one node to another",
 	Long:  `Allow access from one node to another`,
 	Run: func(cmd *cobra.Command, args []string) {
+		network := args[0]
 		fromNodeID := args[1]
 		toNodeID := args[2]
-		payload := acls.ACLContainer(map[acls.AclID]acls.ACL{
-			acls.AclID(fromNodeID): map[acls.AclID]byte{
-				acls.AclID(toNodeID): acls.Allowed,
-			},
-			acls.AclID(toNodeID): map[acls.AclID]byte{
-				acls.AclID(fromNodeID): acls.Allowed,
-			},
-		})
-		functions.UpdateACL(args[0], &payload)
+
+		if fromNodeID == toNodeID {
+			log.Fatal("Cannot allow access from a node to itself")
+		}
+
+		// get current acls
+		res := functions.GetACL(network)
+		if res == nil {
+			log.Fatalf("Could not load network ACLs")
+		}
+
+		payload := *res
+
+		if _, ok := payload[acls.AclID(fromNodeID)]; !ok {
+			log.Fatalf("Node %s does not exist", fromNodeID)
+		}
+		if _, ok := payload[acls.AclID(toNodeID)]; !ok {
+			log.Fatalf("Node %s does not exist", toNodeID)
+		}
+
+		// update acls
+		payload[acls.AclID(fromNodeID)][acls.AclID(toNodeID)] = acls.Allowed
+		payload[acls.AclID(toNodeID)][acls.AclID(fromNodeID)] = acls.Allowed
+
+		functions.UpdateACL(network, &payload)
 		fmt.Println("Success")
 	},
 }

+ 27 - 9
cli/cmd/acl/deny.go

@@ -2,6 +2,7 @@ package acl
 
 import (
 	"fmt"
+	"log"
 
 	"github.com/gravitl/netmaker/cli/functions"
 	"github.com/gravitl/netmaker/logic/acls"
@@ -14,17 +15,34 @@ var aclDenyCmd = &cobra.Command{
 	Short: "Deny access from one node to another",
 	Long:  `Deny access from one node to another`,
 	Run: func(cmd *cobra.Command, args []string) {
+		network := args[0]
 		fromNodeID := args[1]
 		toNodeID := args[2]
-		payload := acls.ACLContainer(map[acls.AclID]acls.ACL{
-			acls.AclID(fromNodeID): map[acls.AclID]byte{
-				acls.AclID(toNodeID): acls.NotAllowed,
-			},
-			acls.AclID(toNodeID): map[acls.AclID]byte{
-				acls.AclID(fromNodeID): acls.NotAllowed,
-			},
-		})
-		functions.UpdateACL(args[0], &payload)
+
+		if fromNodeID == toNodeID {
+			log.Fatal("Cannot deny access to self")
+		}
+
+		// get current acls
+		res := functions.GetACL(network)
+		if res == nil {
+			log.Fatalf("Could not load network ACLs")
+		}
+
+		payload := *res
+
+		if _, ok := payload[acls.AclID(fromNodeID)]; !ok {
+			log.Fatalf("Node [%s] does not exist", fromNodeID)
+		}
+		if _, ok := payload[acls.AclID(toNodeID)]; !ok {
+			log.Fatalf("Node [%s] does not exist", toNodeID)
+		}
+
+		// update acls
+		payload[acls.AclID(fromNodeID)][acls.AclID(toNodeID)] = acls.NotAllowed
+		payload[acls.AclID(toNodeID)][acls.AclID(fromNodeID)] = acls.NotAllowed
+
+		functions.UpdateACL(network, &payload)
 		fmt.Println("Success")
 	},
 }

+ 1 - 0
compose/docker-compose.yml

@@ -56,6 +56,7 @@ services:
       - "443:443"
 
   coredns:
+    #network_mode: host
     container_name: coredns
     image: coredns/coredns:1.10.1
     command: -conf /root/dnsconfig/Corefile

+ 1 - 1
controllers/controller.go

@@ -41,7 +41,7 @@ func HandleRESTRequests(wg *sync.WaitGroup, ctx context.Context) {
 
 	// Currently allowed dev origin is all. Should change in prod
 	// should consider analyzing the allowed methods further
-	headersOk := handlers.AllowedHeaders([]string{"Access-Control-Allow-Origin", "X-Requested-With", "Content-Type", "authorization"})
+	headersOk := handlers.AllowedHeaders([]string{"Access-Control-Allow-Origin", "X-Requested-With", "Content-Type", "authorization", "From-Ui"})
 	originsOk := handlers.AllowedOrigins(strings.Split(servercfg.GetAllowedOrigin(), ","))
 	methodsOk := handlers.AllowedMethods([]string{http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete})
 

+ 23 - 33
controllers/dns.go

@@ -2,6 +2,7 @@ package controller
 
 import (
 	"encoding/json"
+	"errors"
 	"fmt"
 	"net/http"
 
@@ -10,7 +11,6 @@ import (
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/mq"
 	"github.com/gravitl/netmaker/servercfg"
 )
 
@@ -170,24 +170,17 @@ func createDNS(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
-	err = logic.SetDNS()
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"),
-			fmt.Sprintf("Failed to set DNS entries on file: %v", err))
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
+	if servercfg.IsDNSMode() {
+		err = logic.SetDNS()
+		if err != nil {
+			logger.Log(0, r.Header.Get("user"),
+				fmt.Sprintf("Failed to set DNS entries on file: %v", err))
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+			return
+		}
 	}
+
 	logger.Log(1, "new DNS record added:", entry.Name)
-	if servercfg.IsMessageQueueBackend() {
-		go func() {
-			if err = mq.PublishPeerUpdate(); err != nil {
-				logger.Log(0, "failed to publish peer update after ACL update on", entry.Network)
-			}
-			if err := mq.PublishCustomDNS(&entry); err != nil {
-				logger.Log(0, "error publishing custom dns", err.Error())
-			}
-		}()
-	}
 	logger.Log(2, r.Header.Get("user"),
 		fmt.Sprintf("DNS entry is set: %+v", entry))
 	w.WriteHeader(http.StatusOK)
@@ -221,23 +214,17 @@ func deleteDNS(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	logger.Log(1, "deleted dns entry: ", entrytext)
-	err = logic.SetDNS()
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"),
-			fmt.Sprintf("Failed to set DNS entries on file: %v", err))
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
+	if servercfg.IsDNSMode() {
+		err = logic.SetDNS()
+		if err != nil {
+			logger.Log(0, r.Header.Get("user"),
+				fmt.Sprintf("Failed to set DNS entries on file: %v", err))
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+			return
+		}
 	}
+
 	json.NewEncoder(w).Encode(entrytext + " deleted.")
-	go func() {
-		dns := models.DNSUpdate{
-			Action: models.DNSDeleteByName,
-			Name:   entrytext,
-		}
-		if err := mq.PublishDNSUpdate(params["network"], dns); err != nil {
-			logger.Log(0, "failed to publish dns update", err.Error())
-		}
-	}()
 
 }
 
@@ -271,7 +258,10 @@ func GetDNSEntry(domain string, network string) (models.DNSEntry, error) {
 func pushDNS(w http.ResponseWriter, r *http.Request) {
 	// Set header
 	w.Header().Set("Content-Type", "application/json")
-
+	if !servercfg.IsDNSMode() {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("DNS Mode is set to off"), "badrequest"))
+		return
+	}
 	err := logic.SetDNS()
 
 	if err != nil {

+ 27 - 28
controllers/ext_client.go

@@ -12,6 +12,7 @@ import (
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/servercfg"
 
 	"github.com/gravitl/netmaker/models"
 
@@ -355,30 +356,28 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 		userName = caller.UserName
-		if !caller.IsAdmin && !caller.IsSuperAdmin {
-			if _, ok := caller.RemoteGwIDs[nodeid]; !ok {
-				err = errors.New("permission denied")
-				slog.Error("failed to create extclient", "error", err)
-				logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
-				return
-			}
-			// check if user has a config already for remote access client
-			extclients, err := logic.GetNetworkExtClients(node.Network)
-			if err != nil {
-				slog.Error("failed to get extclients", "error", err)
-				logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		if _, ok := caller.RemoteGwIDs[nodeid]; (!caller.IsAdmin && !caller.IsSuperAdmin) && !ok {
+			err = errors.New("permission denied")
+			slog.Error("failed to create extclient", "error", err)
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
+			return
+		}
+		// check if user has a config already for remote access client
+		extclients, err := logic.GetNetworkExtClients(node.Network)
+		if err != nil {
+			slog.Error("failed to get extclients", "error", err)
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+			return
+		}
+		for _, extclient := range extclients {
+			if extclient.RemoteAccessClientID != "" &&
+				extclient.RemoteAccessClientID == customExtClient.RemoteAccessClientID && nodeid == extclient.IngressGatewayID {
+				// extclient on the gw already exists for the remote access client
+				err = errors.New("remote client config already exists on the gateway. it may have been created by another user with this same remote client machine")
+				slog.Error("failed to create extclient", "user", userName, "error", err)
+				logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 				return
 			}
-			for _, extclient := range extclients {
-				if extclient.RemoteAccessClientID != "" &&
-					extclient.RemoteAccessClientID == customExtClient.RemoteAccessClientID && nodeid == extclient.IngressGatewayID {
-					// extclient on the gw already exists for the remote access client
-					err = errors.New("remote client config already exists on the gateway")
-					slog.Error("failed to create extclient", "user", userName, "error", err)
-					logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-					return
-				}
-			}
 		}
 	}
 
@@ -426,8 +425,8 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 		if err := mq.PublishPeerUpdate(); err != nil {
 			logger.Log(1, "error setting ext peers on "+nodeid+": "+err.Error())
 		}
-		if err := mq.PublishExtClientDNS(&extclient); err != nil {
-			logger.Log(1, "error publishing extclient dns", err.Error())
+		if servercfg.IsDNSMode() {
+			logic.SetDNS()
 		}
 	}()
 }
@@ -522,8 +521,8 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(newclient)
 	if changedID {
 		go func() {
-			if err := mq.PublishExtClientDNSUpdate(oldExtClient, newclient, oldExtClient.Network); err != nil {
-				logger.Log(1, "error pubishing dns update for extcient update", err.Error())
+			if servercfg.IsDNSMode() {
+				logic.SetDNS()
 			}
 		}()
 	}
@@ -583,8 +582,8 @@ func deleteExtClient(w http.ResponseWriter, r *http.Request) {
 		if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
 			logger.Log(1, "error setting ext peers on "+ingressnode.ID.String()+": "+err.Error())
 		}
-		if err = mq.PublishDeleteExtClientDNS(&extclient); err != nil {
-			logger.Log(1, "error publishing dns update for extclient deletion", err.Error())
+		if servercfg.IsDNSMode() {
+			logic.SetDNS()
 		}
 	}()
 

+ 66 - 13
controllers/hosts.go

@@ -31,6 +31,7 @@ func hostHandlers(r *mux.Router) {
 	r.HandleFunc("/api/hosts/adm/authenticate", authenticateHost).Methods(http.MethodPost)
 	r.HandleFunc("/api/v1/host", Authorize(true, false, "host", http.HandlerFunc(pull))).Methods(http.MethodGet)
 	r.HandleFunc("/api/v1/host/{hostid}/signalpeer", Authorize(true, false, "host", http.HandlerFunc(signalPeer))).Methods(http.MethodPost)
+	r.HandleFunc("/api/v1/fallback/host/{hostid}", Authorize(true, false, "host", http.HandlerFunc(hostUpdateFallback))).Methods(http.MethodPut)
 	r.HandleFunc("/api/v1/auth-register/host", socketHandler)
 }
 
@@ -141,6 +142,8 @@ func pull(w http.ResponseWriter, r *http.Request) {
 		Peers:           hPU.Peers,
 		PeerIDs:         hPU.PeerIDs,
 		HostNetworkInfo: hPU.HostNetworkInfo,
+		EgressRoutes:    hPU.EgressRoutes,
+		FwUpdate:        hPU.FwUpdate,
 	}
 
 	logger.Log(1, hostID, "completed a pull")
@@ -196,16 +199,8 @@ func updateHost(w http.ResponseWriter, r *http.Request) {
 			logger.Log(0, "fail to publish peer update: ", err.Error())
 		}
 		if newHost.Name != currHost.Name {
-			networks := logic.GetHostNetworks(currHost.ID.String())
-			if err := mq.PublishHostDNSUpdate(currHost, newHost, networks); err != nil {
-				var dnsError *models.DNSError
-				if errors.Is(err, dnsError) {
-					for _, message := range err.(models.DNSError).ErrorStrings {
-						logger.Log(0, message)
-					}
-				} else {
-					logger.Log(0, err.Error())
-				}
+			if servercfg.IsDNSMode() {
+				logic.SetDNS()
 			}
 		}
 	}()
@@ -216,6 +211,51 @@ func updateHost(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(apiHostData)
 }
 
+// swagger:route PUT /api/v1/fallback/host/{hostid} hosts hostUpdateFallback
+//
+// Updates a Netclient host on Netmaker server.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: apiHostResponse
+func hostUpdateFallback(w http.ResponseWriter, r *http.Request) {
+	var params = mux.Vars(r)
+	hostid := params["hostid"]
+	currentHost, err := logic.GetHost(hostid)
+	if err != nil {
+		slog.Error("error getting host", "id", hostid, "error", err)
+		return
+	}
+
+	var hostUpdate models.HostUpdate
+	err = json.NewDecoder(r.Body).Decode(&hostUpdate)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to update a host:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	slog.Info("recieved host update", "name", hostUpdate.Host.Name, "id", hostUpdate.Host.ID)
+	switch hostUpdate.Action {
+	case models.CheckIn:
+		_ = mq.HandleHostCheckin(&hostUpdate.Host, currentHost)
+
+	case models.UpdateHost:
+
+		_ = logic.UpdateHostFromClient(&hostUpdate.Host, currentHost)
+		err := logic.UpsertHost(currentHost)
+		if err != nil {
+			slog.Error("failed to update host", "id", currentHost.ID, "error", err)
+			return
+		}
+
+	}
+
+}
+
 // swagger:route DELETE /api/hosts/{hostid} hosts deleteHost
 //
 // Deletes a Netclient host from Netmaker server.
@@ -252,6 +292,12 @@ func deleteHost(w http.ResponseWriter, r *http.Request) {
 		go mq.PublishMqUpdatesForDeletedNode(node, false, gwClients)
 
 	}
+	if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
+		// delete EMQX credentials for host
+		if err := mq.DeleteEmqxUser(currHost.ID.String()); err != nil {
+			slog.Error("failed to remove host credentials from EMQX", "id", currHost.ID, "error", err)
+		}
+	}
 	if err = logic.RemoveHost(currHost, forceDelete); err != nil {
 		logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
@@ -311,7 +357,9 @@ func addHostToNetwork(w http.ResponseWriter, r *http.Request) {
 			Node:   *newNode,
 		})
 		mq.PublishPeerUpdate()
-		mq.HandleNewNodeDNS(currHost, newNode)
+		if servercfg.IsDNSMode() {
+			logic.SetDNS()
+		}
 	}()
 	logger.Log(2, r.Header.Get("user"), fmt.Sprintf("added host %s to network %s", currHost.Name, network))
 	w.WriteHeader(http.StatusOK)
@@ -396,7 +444,12 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal"))
 		return
 	}
-	go mq.PublishMqUpdatesForDeletedNode(*node, true, gwClients)
+	go func() {
+		mq.PublishMqUpdatesForDeletedNode(*node, true, gwClients)
+		if servercfg.IsDNSMode() {
+			logic.SetDNS()
+		}
+	}()
 	logger.Log(2, r.Header.Get("user"), fmt.Sprintf("removed host %s from network %s", currHost.Name, network))
 	w.WriteHeader(http.StatusOK)
 }
@@ -492,7 +545,7 @@ func authenticateHost(response http.ResponseWriter, request *http.Request) {
 
 	// Create EMQX creds and ACLs if not found
 	if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
-		if err := mq.CreateEmqxUser(host.ID.String(), host.HostPass, false); err != nil {
+		if err := mq.CreateEmqxUser(host.ID.String(), authRequest.Password, false); err != nil {
 			slog.Error("failed to create host credentials for EMQX: ", err.Error())
 		} else {
 			if err := mq.CreateHostACL(host.ID.String(), servercfg.GetServerInfo().Server); err != nil {

+ 6 - 7
controllers/node.go

@@ -596,7 +596,9 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 				if err := mq.NodeUpdate(&node); err != nil {
 					slog.Error("error publishing node update to node", "node", node.ID, "error", err)
 				}
-				mq.PublishDeleteAllExtclientsDNS(node.Network, removedClients)
+				if servercfg.IsDNSMode() {
+					logic.SetDNS()
+				}
 			}()
 		}
 	}
@@ -635,7 +637,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 	}
 	newNode := newData.ConvertToServerNode(&currentNode)
 	relayUpdate := logic.RelayUpdates(&currentNode, newNode)
-	host, err := logic.GetHost(newNode.HostID.String())
+	_, err = logic.GetHost(newNode.HostID.String())
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 			fmt.Sprintf("failed to get host for node  [ %s ] info: %v", nodeid, err))
@@ -655,9 +657,6 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 	if relayUpdate {
 		logic.UpdateRelayed(&currentNode, newNode)
 	}
-	if servercfg.IsDNSMode() {
-		logic.SetDNS()
-	}
 
 	apiNode := newNode.ConvertToAPINode()
 	logger.Log(1, r.Header.Get("user"), "updated node", currentNode.ID.String(), "on network", currentNode.Network)
@@ -672,8 +671,8 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 				logger.Log(0, "error during node ACL update for node", newNode.ID.String())
 			}
 		}
-		if err := mq.PublishReplaceDNS(&currentNode, newNode, host); err != nil {
-			logger.Log(1, "failed to publish dns update", err.Error())
+		if servercfg.IsDNSMode() {
+			logic.SetDNS()
 		}
 	}(aclUpdate, relayUpdate, newNode)
 }

+ 18 - 1
controllers/user.go

@@ -71,6 +71,20 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
 		logic.ReturnErrorResponse(response, request, errorResponse)
 		return
 	}
+	if val := request.Header.Get("From-Ui"); val == "true" {
+		// request came from UI, if normal user block Login
+		user, err := logic.GetUser(authRequest.UserName)
+		if err != nil {
+			logger.Log(0, authRequest.UserName, "user validation failed: ",
+				err.Error())
+			logic.ReturnErrorResponse(response, request, logic.FormatError(err, "unauthorized"))
+			return
+		}
+		if !(user.IsAdmin || user.IsSuperAdmin) {
+			logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("only admins can access dashboard"), "unauthorized"))
+			return
+		}
+	}
 	username := authRequest.UserName
 	jwt, err := logic.VerifyAuthRequest(authRequest)
 	if err != nil {
@@ -119,7 +133,7 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
 				if client.OwnerID == username && !client.Enabled {
 					slog.Info(fmt.Sprintf("enabling ext client %s for user %s due to RAC autodisabling feature", client.ClientID, client.OwnerID))
 					if newClient, err := logic.ToggleExtClientConnectivity(&client, true); err != nil {
-						slog.Error("error disabling ext client in RAC autodisable hook", "error", err)
+						slog.Error("error enabling ext client in RAC autodisable hook", "error", err)
 						continue // dont return but try for other clients
 					} else {
 						// publish peer update to ingress gateway
@@ -545,6 +559,9 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
 				}
 			}
 		}
+		if servercfg.IsDNSMode() {
+			logic.SetDNS()
+		}
 	}()
 	logger.Log(1, username, "was deleted")
 	json.NewEncoder(w).Encode(params["username"] + " deleted.")

+ 3 - 4
go.mod

@@ -6,16 +6,16 @@ require (
 	github.com/eclipse/paho.mqtt.golang v1.4.3
 	github.com/go-playground/validator/v10 v10.16.0
 	github.com/golang-jwt/jwt/v4 v4.5.0
-	github.com/google/uuid v1.4.0
+	github.com/google/uuid v1.5.0
 	github.com/gorilla/handlers v1.5.2
 	github.com/gorilla/mux v1.8.1
 	github.com/lib/pq v1.10.9
-	github.com/mattn/go-sqlite3 v1.14.18
+	github.com/mattn/go-sqlite3 v1.14.19
 	github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 	github.com/stretchr/testify v1.8.4
 	github.com/txn2/txeh v1.5.5
-	golang.org/x/crypto v0.16.0
+	golang.org/x/crypto v0.17.0
 	golang.org/x/net v0.19.0 // indirect
 	golang.org/x/oauth2 v0.15.0
 	golang.org/x/sys v0.15.0 // indirect
@@ -38,7 +38,6 @@ require (
 )
 
 require (
-	github.com/devilcove/httpclient v0.6.0
 	github.com/go-jose/go-jose/v3 v3.0.1
 	github.com/guumaster/tablewriter v0.0.10
 	github.com/matryer/is v1.4.1

+ 6 - 8
go.sum

@@ -14,8 +14,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/devilcove/httpclient v0.6.0 h1:M5YAfHeNbu+0QxCiOCo/fKN+Hf0BtF/6aovu3NNgcKk=
-github.com/devilcove/httpclient v0.6.0/go.mod h1:ctrAO2gRgTT+GxtRdWBp2SMQ+vacuxXlbhmlM4oWhs8=
 github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik=
 github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE=
 github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
@@ -40,8 +38,8 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
 github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
-github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
+github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
 github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
 github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
@@ -64,8 +62,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
 github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
 github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI=
-github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
+github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
+github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
 github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -104,8 +102,8 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
-golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
+golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=

+ 29 - 2
logic/dns.go

@@ -11,6 +11,7 @@ import (
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/servercfg"
 	"github.com/txn2/txeh"
 )
 
@@ -36,6 +37,10 @@ func SetDNS() error {
 			hostfile.AddHost(entry.Address, entry.Name)
 		}
 	}
+	dns := GetExtclientDNS()
+	for _, entry := range dns {
+		hostfile.AddHost(entry.Address, entry.Name)
+	}
 	if corefilestring == "" {
 		corefilestring = "example.com"
 	}
@@ -69,6 +74,28 @@ func GetDNS(network string) ([]models.DNSEntry, error) {
 	return dns, nil
 }
 
+// GetExtclientDNS - gets all extclients dns entries
+func GetExtclientDNS() []models.DNSEntry {
+	extclients, err := GetAllExtClients()
+	if err != nil {
+		return []models.DNSEntry{}
+	}
+	var dns []models.DNSEntry
+	for _, extclient := range extclients {
+		var entry = models.DNSEntry{}
+		entry.Name = fmt.Sprintf("%s.%s", extclient.ClientID, extclient.Network)
+		entry.Network = extclient.Network
+		if extclient.Address != "" {
+			entry.Address = extclient.Address
+		}
+		if extclient.Address6 != "" {
+			entry.Address6 = extclient.Address6
+		}
+		dns = append(dns, entry)
+	}
+	return dns
+}
+
 // GetNodeDNS - gets the DNS of a network node
 func GetNodeDNS(network string) ([]models.DNSEntry, error) {
 
@@ -142,6 +169,7 @@ func SetCorefile(domains string) error {
 	}
 
 	corefile := domains + ` {
+	bind %s
     reload 15s
     hosts /root/dnsconfig/netmaker.hosts {
 	fallthrough	
@@ -150,8 +178,7 @@ func SetCorefile(domains string) error {
     log
 }
 `
-	corebytes := []byte(corefile)
-
+	corebytes := []byte(fmt.Sprintf(corefile, servercfg.GetCoreDNSAddr()))
 	err = os.WriteFile(dir+"/config/dnsconfig/Corefile", corebytes, 0644)
 	if err != nil {
 		return err

+ 5 - 0
logic/hosts.go

@@ -309,6 +309,11 @@ func RemoveHost(h *models.Host, forceDelete bool) error {
 	if servercfg.CacheEnabled() {
 		deleteHostFromCache(h.ID.String())
 	}
+	go func() {
+		if servercfg.IsDNSMode() {
+			SetDNS()
+		}
+	}()
 
 	return nil
 }

+ 2 - 2
models/extclient.go

@@ -17,7 +17,7 @@ type ExtClient struct {
 	Enabled                bool                `json:"enabled" bson:"enabled"`
 	OwnerID                string              `json:"ownerid" bson:"ownerid"`
 	DeniedACLs             map[string]struct{} `json:"deniednodeacls" bson:"acls,omitempty"`
-	RemoteAccessClientID   string              `json:"remote_access_client_id"`
+	RemoteAccessClientID   string              `json:"remote_access_client_id"` // unique ID (MAC address) of RAC machine
 }
 
 // CustomExtClient - struct for CustomExtClient params
@@ -28,5 +28,5 @@ type CustomExtClient struct {
 	ExtraAllowedIPs      []string            `json:"extraallowedips,omitempty"`
 	Enabled              bool                `json:"enabled,omitempty"`
 	DeniedACLs           map[string]struct{} `json:"deniednodeacls" bson:"acls,omitempty"`
-	RemoteAccessClientID string              `json:"remote_access_client_id"`
+	RemoteAccessClientID string              `json:"remote_access_client_id"` // unique ID (MAC address) of RAC machine
 }

+ 9 - 6
models/structs.go

@@ -225,12 +225,14 @@ type TrafficKeys struct {
 
 // HostPull - response of a host's pull
 type HostPull struct {
-	Host            Host                 `json:"host" yaml:"host"`
-	Nodes           []Node               `json:"nodes" yaml:"nodes"`
-	Peers           []wgtypes.PeerConfig `json:"peers" yaml:"peers"`
-	ServerConfig    ServerConfig         `json:"server_config" yaml:"server_config"`
-	PeerIDs         PeerMap              `json:"peer_ids,omitempty" yaml:"peer_ids,omitempty"`
-	HostNetworkInfo HostInfoMap          `json:"host_network_info,omitempty"  yaml:"host_network_info,omitempty"`
+	Host            Host                  `json:"host" yaml:"host"`
+	Nodes           []Node                `json:"nodes" yaml:"nodes"`
+	Peers           []wgtypes.PeerConfig  `json:"peers" yaml:"peers"`
+	ServerConfig    ServerConfig          `json:"server_config" yaml:"server_config"`
+	PeerIDs         PeerMap               `json:"peer_ids,omitempty" yaml:"peer_ids,omitempty"`
+	HostNetworkInfo HostInfoMap           `json:"host_network_info,omitempty"  yaml:"host_network_info,omitempty"`
+	EgressRoutes    []EgressNetworkRoutes `json:"egress_network_routes"`
+	FwUpdate        FwUpdate              `json:"fw_update"`
 }
 
 // NodeGet - struct for a single node get response
@@ -261,6 +263,7 @@ type ServerConfig struct {
 	MQPort      string `yaml:"mqport"`
 	MQUserName  string `yaml:"mq_username"`
 	MQPassword  string `yaml:"mq_password"`
+	BrokerType  string `yaml:"broker_type"`
 	Server      string `yaml:"server"`
 	Broker      string `yaml:"broker"`
 	IsPro       bool   `yaml:"isee" json:"Is_EE"`

+ 0 - 10
mq/emqx.go

@@ -286,16 +286,6 @@ func CreateHostACL(hostID, serverName string) error {
 				Permission: "allow",
 				Action:     "all",
 			},
-			{
-				Topic:      fmt.Sprintf("dns/all/%s/%s", hostID, serverName),
-				Permission: "allow",
-				Action:     "all",
-			},
-			{
-				Topic:      fmt.Sprintf("dns/update/%s/%s", hostID, serverName),
-				Permission: "allow",
-				Action:     "all",
-			},
 			{
 				Topic:      fmt.Sprintf("host/serverupdate/%s/%s", serverName, hostID),
 				Permission: "allow",

+ 5 - 29
mq/handlers.go

@@ -104,7 +104,7 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
 	var sendPeerUpdate bool
 	switch hostUpdate.Action {
 	case models.CheckIn:
-		sendPeerUpdate = handleHostCheckin(&hostUpdate.Host, currentHost)
+		sendPeerUpdate = HandleHostCheckin(&hostUpdate.Host, currentHost)
 	case models.Acknowledgement:
 		hu := hostactions.GetAction(currentHost.ID.String())
 		if hu != nil {
@@ -126,10 +126,6 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
 					slog.Error("failed peers publish after join acknowledged", "name", hostUpdate.Host.Name, "id", currentHost.ID, "error", err)
 					return
 				}
-				if err = HandleNewNodeDNS(&hu.Host, &hu.Node); err != nil {
-					slog.Error("failed to send dns update after node added to host", "name", hostUpdate.Host.Name, "id", currentHost.ID, "error", err)
-					return
-				}
 			}
 		}
 	case models.UpdateHost:
@@ -166,7 +162,6 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
 			// delete EMQX credentials for host
 			if err := DeleteEmqxUser(currentHost.ID.String()); err != nil {
 				slog.Error("failed to remove host credentials from EMQX", "id", currentHost.ID, "error", err)
-				return
 			}
 		}
 
@@ -193,6 +188,9 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
 			slog.Error("failed to delete host", "id", currentHost.ID, "error", err)
 			return
 		}
+		if servercfg.IsDNSMode() {
+			logic.SetDNS()
+		}
 		sendPeerUpdate = true
 	case models.SignalHost:
 		signalPeer(hostUpdate.Signal)
@@ -260,29 +258,7 @@ func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
 	slog.Info("sent peer updates after signal received from", "id", id)
 }
 
-func HandleNewNodeDNS(host *models.Host, node *models.Node) error {
-	dns := models.DNSUpdate{
-		Action: models.DNSInsert,
-		Name:   host.Name + "." + node.Network,
-	}
-	if node.Address.IP != nil {
-		dns.Address = node.Address.IP.String()
-		if err := PublishDNSUpdate(node.Network, dns); err != nil {
-			return err
-		}
-	} else if node.Address6.IP != nil {
-		dns.Address = node.Address6.IP.String()
-		if err := PublishDNSUpdate(node.Network, dns); err != nil {
-			return err
-		}
-	}
-	if err := PublishAllDNS(node); err != nil {
-		return err
-	}
-	return nil
-}
-
-func handleHostCheckin(h, currentHost *models.Host) bool {
+func HandleHostCheckin(h, currentHost *models.Host) bool {
 	if h == nil {
 		return false
 	}

+ 2 - 286
mq/publishers.go

@@ -166,73 +166,6 @@ func ServerStartNotify() error {
 	return nil
 }
 
-// PublishDNSUpdatev1 - published dns updates to all nodes passed
-func PublishDNSUpdatev1(network string, dns models.DNSUpdate, nodes []models.Node) error {
-	for _, node := range nodes {
-		host, err := logic.GetHost(node.HostID.String())
-		if err != nil {
-			logger.Log(0, "error retrieving host for dns update", node.HostID.String(), err.Error())
-			continue
-		}
-		data, err := json.Marshal(dns)
-		if err != nil {
-			logger.Log(0, "failed to encode dns data for node", node.ID.String(), err.Error())
-		}
-		if err := publish(host, "dns/update/"+host.ID.String()+"/"+servercfg.GetServer(), data); err != nil {
-			logger.Log(0, "error publishing dns update to host", host.ID.String(), err.Error())
-			continue
-		}
-		logger.Log(3, "published dns update to host", host.ID.String())
-	}
-	return nil
-}
-
-// PublishDNSUpdate publishes a dns update to all nodes on a network
-func PublishDNSUpdate(network string, dns models.DNSUpdate) error {
-	nodes, err := logic.GetNetworkNodes(network)
-	if err != nil {
-		return err
-	}
-	for _, node := range nodes {
-		host, err := logic.GetHost(node.HostID.String())
-		if err != nil {
-			logger.Log(0, "error retrieving host for dns update", node.HostID.String(), err.Error())
-			continue
-		}
-		data, err := json.Marshal(dns)
-		if err != nil {
-			logger.Log(0, "failed to encode dns data for node", node.ID.String(), err.Error())
-		}
-		if err := publish(host, "dns/update/"+host.ID.String()+"/"+servercfg.GetServer(), data); err != nil {
-			logger.Log(0, "error publishing dns update to host", host.ID.String(), err.Error())
-			continue
-		}
-		logger.Log(3, "published dns update to host", host.ID.String())
-	}
-	return nil
-}
-
-// PublishAllDNS publishes an array of dns updates (ip / host.network) for each peer to a node joining a network
-func PublishAllDNS(newnode *models.Node) error {
-	alldns := []models.DNSUpdate{}
-	newnodeHost, err := logic.GetHost(newnode.HostID.String())
-	if err != nil {
-		return fmt.Errorf("error retrieving host for dns update %w", err)
-	}
-	alldns = append(alldns, getNodeDNS(newnode.Network)...)
-	alldns = append(alldns, getExtClientDNS(newnode.Network)...)
-	alldns = append(alldns, getCustomDNS(newnode.Network)...)
-	data, err := json.Marshal(alldns)
-	if err != nil {
-		return fmt.Errorf("error encoding dns data %w", err)
-	}
-	if err := publish(newnodeHost, "dns/all/"+newnodeHost.ID.String()+"/"+servercfg.GetServer(), data); err != nil {
-		return fmt.Errorf("error publishing full dns update to %s, %w", newnodeHost.ID.String(), err)
-	}
-	logger.Log(3, "published full dns update to %s", newnodeHost.ID.String())
-	return nil
-}
-
 // PublishMqUpdatesForDeletedNode - published all the required updates for deleted node
 func PublishMqUpdatesForDeletedNode(node models.Node, sendNodeUpdate bool, gwClients []models.ExtClient) {
 	// notify of peer change
@@ -246,162 +179,10 @@ func PublishMqUpdatesForDeletedNode(node models.Node, sendNodeUpdate bool, gwCli
 	if err := PublishDeletedNodePeerUpdate(&node); err != nil {
 		logger.Log(1, "error publishing peer update ", err.Error())
 	}
-	host, err := logic.GetHost(node.HostID.String())
-	if err != nil {
-		logger.Log(1, "failed to retrieve host for node", node.ID.String(), err.Error())
+	if servercfg.IsDNSMode() {
+		logic.SetDNS()
 	}
-	if err := PublishDNSDelete(&node, host); err != nil {
-		logger.Log(1, "error publishing dns update", err.Error())
-	}
-	if err := PublishDeleteAllExtclientsDNS(node.Network, gwClients); err != nil {
-		logger.Log(1, "error publishing ext dns update", err.Error())
-	}
-
-}
 
-// PublishDNSDelete publish a dns update deleting a node to all hosts on a network
-func PublishDNSDelete(node *models.Node, host *models.Host) error {
-	dns := models.DNSUpdate{
-		Action: models.DNSDeleteByIP,
-		Name:   host.Name + "." + node.Network,
-	}
-	if node.Address.IP != nil {
-		dns.Address = node.Address.IP.String()
-		if err := PublishDNSUpdate(node.Network, dns); err != nil {
-			return fmt.Errorf("dns update node deletion %w", err)
-		}
-	}
-	if node.Address6.IP != nil {
-		dns.Address = node.Address6.IP.String()
-		if err := PublishDNSUpdate(node.Network, dns); err != nil {
-			return fmt.Errorf("dns update node deletion %w", err)
-		}
-	}
-	return nil
-}
-
-// PublishReplaceDNS publish a dns update to replace a dns entry on all hosts in network
-func PublishReplaceDNS(oldNode, newNode *models.Node, host *models.Host) error {
-	dns := models.DNSUpdate{
-		Action: models.DNSReplaceIP,
-		Name:   host.Name + "." + oldNode.Network,
-	}
-	if !oldNode.Address.IP.Equal(newNode.Address.IP) {
-		dns.Address = oldNode.Address.IP.String()
-		dns.NewAddress = newNode.Address.IP.String()
-		if err := PublishDNSUpdate(oldNode.Network, dns); err != nil {
-			return err
-		}
-	}
-	if !oldNode.Address6.IP.Equal(newNode.Address6.IP) {
-		dns.Address = oldNode.Address6.IP.String()
-		dns.NewAddress = newNode.Address6.IP.String()
-		if err := PublishDNSUpdate(oldNode.Network, dns); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-// PublishExtClientDNS publish dns update for new extclient
-func PublishExtClientDNS(client *models.ExtClient) error {
-	errMsgs := models.DNSError{}
-	dns := models.DNSUpdate{
-		Action:  models.DNSInsert,
-		Name:    client.ClientID + "." + client.Network,
-		Address: client.Address,
-	}
-	if client.Address != "" {
-		dns.Address = client.Address
-		if err := PublishDNSUpdate(client.Network, dns); err != nil {
-			errMsgs.ErrorStrings = append(errMsgs.ErrorStrings, err.Error())
-		}
-
-	}
-	if client.Address6 != "" {
-		dns.Address = client.Address6
-		if err := PublishDNSUpdate(client.Network, dns); err != nil {
-			errMsgs.ErrorStrings = append(errMsgs.ErrorStrings, err.Error())
-		}
-	}
-	if len(errMsgs.ErrorStrings) > 0 {
-		return errMsgs
-	}
-	return nil
-}
-
-// PublishExtClientDNSUpdate update for extclient name change
-func PublishExtClientDNSUpdate(old, new models.ExtClient, network string) error {
-	dns := models.DNSUpdate{
-		Action:  models.DNSReplaceName,
-		Name:    old.ClientID + "." + network,
-		NewName: new.ClientID + "." + network,
-	}
-	if err := PublishDNSUpdate(network, dns); err != nil {
-		return err
-	}
-	return nil
-}
-
-// PublishDeleteAllExtclientsDNS - publish to delete all passed ext clients dns entries
-func PublishDeleteAllExtclientsDNS(network string, clients []models.ExtClient) error {
-	nodes, err := logic.GetNetworkNodes(network)
-	if err != nil {
-		return err
-	}
-	for _, client := range clients {
-		dns := models.DNSUpdate{
-			Action: models.DNSDeleteByName,
-			Name:   client.ClientID + "." + client.Network,
-		}
-		go PublishDNSUpdatev1(client.Network, dns, nodes)
-	}
-	return nil
-}
-
-// PublishDeleteExtClientDNS publish dns update to delete extclient entry
-func PublishDeleteExtClientDNS(client *models.ExtClient) error {
-	dns := models.DNSUpdate{
-		Action: models.DNSDeleteByName,
-		Name:   client.ClientID + "." + client.Network,
-	}
-	if err := PublishDNSUpdate(client.Network, dns); err != nil {
-		return err
-	}
-	return nil
-}
-
-// PublishCustomDNS publish dns update for new custom dns entry
-func PublishCustomDNS(entry *models.DNSEntry) error {
-	dns := models.DNSUpdate{
-		Action: models.DNSInsert,
-		Name:   entry.Name,
-		//entry.Address6 is never used
-		Address: entry.Address,
-	}
-	if err := PublishDNSUpdate(entry.Network, dns); err != nil {
-		return err
-	}
-	return nil
-}
-
-// PublishHostDNSUpdate publishes dns update on host name change
-func PublishHostDNSUpdate(old, new *models.Host, networks []string) error {
-	errMsgs := models.DNSError{}
-	for _, network := range networks {
-		dns := models.DNSUpdate{
-			Action:  models.DNSReplaceName,
-			Name:    old.Name + "." + network,
-			NewName: new.Name + "." + network,
-		}
-		if err := PublishDNSUpdate(network, dns); err != nil {
-			errMsgs.ErrorStrings = append(errMsgs.ErrorStrings, err.Error())
-		}
-	}
-	if len(errMsgs.ErrorStrings) > 0 {
-		return errMsgs
-	}
-	return nil
 }
 
 func PushMetricsToExporter(metrics models.Metrics) error {
@@ -422,71 +203,6 @@ func PushMetricsToExporter(metrics models.Metrics) error {
 	return nil
 }
 
-func getNodeDNS(network string) []models.DNSUpdate {
-	alldns := []models.DNSUpdate{}
-	dns := models.DNSUpdate{}
-	nodes, err := logic.GetNetworkNodes(network)
-	if err != nil {
-		logger.Log(0, "error retreiving network nodes for network", network, err.Error())
-	}
-	for _, node := range nodes {
-		host, err := logic.GetHost(node.HostID.String())
-		if err != nil {
-			logger.Log(0, "error retrieving host for dns update", node.HostID.String(), err.Error())
-			continue
-		}
-		dns.Action = models.DNSInsert
-		dns.Name = host.Name + "." + node.Network
-		if node.Address.IP != nil {
-			dns.Address = node.Address.IP.String()
-			alldns = append(alldns, dns)
-		}
-		if node.Address6.IP != nil {
-			dns.Address = node.Address6.IP.String()
-			alldns = append(alldns, dns)
-		}
-	}
-	return alldns
-}
-
-func getExtClientDNS(network string) []models.DNSUpdate {
-	alldns := []models.DNSUpdate{}
-	dns := models.DNSUpdate{}
-	clients, err := logic.GetNetworkExtClients(network)
-	if err != nil {
-		logger.Log(0, "error retrieving extclients", err.Error())
-	}
-	for _, client := range clients {
-		dns.Action = models.DNSInsert
-		dns.Name = client.ClientID + "." + client.Network
-		if client.Address != "" {
-			dns.Address = client.Address
-			alldns = append(alldns, dns)
-		}
-		if client.Address6 != "" {
-			dns.Address = client.Address
-			alldns = append(alldns, dns)
-		}
-	}
-	return alldns
-}
-
-func getCustomDNS(network string) []models.DNSUpdate {
-	alldns := []models.DNSUpdate{}
-	dns := models.DNSUpdate{}
-	customdns, err := logic.GetCustomDNS(network)
-	if err != nil {
-		logger.Log(0, "error retrieving custom dns entries", err.Error())
-	}
-	for _, custom := range customdns {
-		dns.Action = models.DNSInsert
-		dns.Address = custom.Address
-		dns.Name = custom.Name + "." + custom.Network
-		alldns = append(alldns, dns)
-	}
-	return alldns
-}
-
 // sendPeers - retrieve networks, send peer ports to all peers
 func sendPeers() {
 

+ 1 - 1
mq/util.go

@@ -78,7 +78,7 @@ func publish(host *models.Host, dest string, msg []byte) error {
 	if encryptErr != nil {
 		return encryptErr
 	}
-	if mqclient == nil {
+	if mqclient == nil || !mqclient.IsConnectionOpen() {
 		return errors.New("cannot publish ... mqclient not connected")
 	}
 

+ 71 - 29
pro/controllers/users.go

@@ -10,6 +10,7 @@ import (
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/servercfg"
 	"golang.org/x/exp/slog"
 )
 
@@ -116,6 +117,9 @@ func removeUserFromRemoteAccessGW(w http.ResponseWriter, r *http.Request) {
 				logic.DeleteExtClient(extclient.Network, extclient.ClientID)
 			}
 		}
+		if servercfg.IsDNSMode() {
+			logic.SetDNS()
+		}
 	}(*user, remoteGwID)
 
 	err = logic.UpsertUser(*user)
@@ -172,16 +176,13 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch user %s, error: %v", username, err), "badrequest"))
 		return
 	}
-	if user.IsAdmin || user.IsSuperAdmin {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("admins can visit dashboard to create remote clients"), "badrequest"))
-		return
-	}
 	allextClients, err := logic.GetAllExtClients()
 	if err != nil {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
 
+	processedAdminNodeIds := make(map[string]struct{})
 	for _, extClient := range allextClients {
 		if extClient.RemoteAccessClientID == req.RemoteAccessClientID && extClient.OwnerID == username {
 			node, err := logic.GetNodeByID(extClient.IngressGatewayID)
@@ -199,7 +200,7 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
 				continue
 			}
 
-			if _, ok := user.RemoteGwIDs[node.ID.String()]; ok {
+			if _, ok := user.RemoteGwIDs[node.ID.String()]; (!user.IsAdmin && !user.IsSuperAdmin) && ok {
 				gws := userGws[node.Network]
 				extClient.AllowedIPs = logic.GetExtclientAllowedIPs(extClient)
 				gws = append(gws, models.UserRemoteGws{
@@ -213,40 +214,81 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
 				})
 				userGws[node.Network] = gws
 				delete(user.RemoteGwIDs, node.ID.String())
-
+			} else {
+				gws := userGws[node.Network]
+				extClient.AllowedIPs = logic.GetExtclientAllowedIPs(extClient)
+				gws = append(gws, models.UserRemoteGws{
+					GwID:              node.ID.String(),
+					GWName:            host.Name,
+					Network:           node.Network,
+					GwClient:          extClient,
+					Connected:         true,
+					IsInternetGateway: node.IsInternetGateway,
+					GwPeerPublicKey:   host.PublicKey.String(),
+				})
+				userGws[node.Network] = gws
+				processedAdminNodeIds[node.ID.String()] = struct{}{}
 			}
 		}
-
 	}
 
 	// add remaining gw nodes to resp
-	for gwID := range user.RemoteGwIDs {
-		node, err := logic.GetNodeByID(gwID)
-		if err != nil {
-			continue
-		}
-		if !node.IsIngressGateway {
-			continue
-		}
-		if node.PendingDelete {
-			continue
+	if !user.IsAdmin && !user.IsSuperAdmin {
+		for gwID := range user.RemoteGwIDs {
+			node, err := logic.GetNodeByID(gwID)
+			if err != nil {
+				continue
+			}
+			if !node.IsIngressGateway {
+				continue
+			}
+			if node.PendingDelete {
+				continue
+			}
+			host, err := logic.GetHost(node.HostID.String())
+			if err != nil {
+				continue
+			}
+			gws := userGws[node.Network]
+
+			gws = append(gws, models.UserRemoteGws{
+				GwID:              node.ID.String(),
+				GWName:            host.Name,
+				Network:           node.Network,
+				IsInternetGateway: node.IsInternetGateway,
+				GwPeerPublicKey:   host.PublicKey.String(),
+			})
+			userGws[node.Network] = gws
 		}
-		host, err := logic.GetHost(node.HostID.String())
+	} else {
+		allNodes, err := logic.GetAllNodes()
 		if err != nil {
-			continue
+			slog.Error("failed to fetch all nodes", "error", err)
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+			return
 		}
-		gws := userGws[node.Network]
+		for _, node := range allNodes {
+			_, ok := processedAdminNodeIds[node.ID.String()]
+			if node.IsIngressGateway && !node.PendingDelete && !ok {
+				host, err := logic.GetHost(node.HostID.String())
+				if err != nil {
+					slog.Error("failed to fetch host", "error", err)
+					continue
+				}
+				gws := userGws[node.Network]
 
-		gws = append(gws, models.UserRemoteGws{
-			GwID:              node.ID.String(),
-			GWName:            host.Name,
-			Network:           node.Network,
-			IsInternetGateway: node.IsInternetGateway,
-			GwPeerPublicKey:   host.PublicKey.String(),
-		})
-		userGws[node.Network] = gws
+				gws = append(gws, models.UserRemoteGws{
+					GwID:              node.ID.String(),
+					GWName:            host.Name,
+					Network:           node.Network,
+					IsInternetGateway: node.IsInternetGateway,
+					GwPeerPublicKey:   host.PublicKey.String(),
+				})
+				userGws[node.Network] = gws
+			}
+		}
 	}
-
+	slog.Debug("returned user gws", "user", username, "gws", userGws)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(userGws)
 }

+ 1 - 0
servercfg/serverconf.go

@@ -119,6 +119,7 @@ func GetServerInfo() models.ServerConfig {
 	cfg.APIPort = GetAPIPort()
 	cfg.DNSMode = "off"
 	cfg.Broker = GetPublicBrokerEndpoint()
+	cfg.BrokerType = GetBrokerType()
 	if IsDNSMode() {
 		cfg.DNSMode = "on"
 	}