Browse Source

merge conflicts resolved

Abhishek Kondur 2 years ago
parent
commit
433b61417b

+ 4 - 4
.github/workflows/publish-docker.yml

@@ -19,8 +19,8 @@ jobs:
       -
         name: Set tag
         run: |
-            if [[ -n "${{ github.event.inputs.tag }}" ]]; then
-              TAG=${{ github.event.inputs.tag }}
+            if [[ -n "${{ inputs.tag }}" ]]; then
+              TAG=${{ inputs.tag }}
             elif [[ "${{ github.ref_name }}" == 'master' ]]; then
               TAG="latest"
             else
@@ -59,8 +59,8 @@ jobs:
       -
         name: Set tag
         run: |
-            if [[ -n "${{ github.event.inputs.tag }}" ]]; then
-              TAG=${{ github.event.inputs.tag }}
+            if [[ -n "${{ inputs.tag }}" ]]; then
+              TAG=${{ inputs.tag }}
             elif [[ "${{ github.ref_name }}" == 'master' ]]; then
               TAG="latest"
             else

+ 1 - 1
.github/workflows/release-assets.yml

@@ -31,7 +31,7 @@ jobs:
         run: |
           git fetch --force --tags
       - name: Setup go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4
         with:
           go-version: 1.19
       - name: GoReleaser (full release)

+ 1 - 1
.github/workflows/release-branch.yml

@@ -21,7 +21,7 @@ jobs:
         with:
           ref: develop
       - name: setup go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4
         with:
           go-version: 1.19
       - name: setup git

+ 6 - 0
.github/workflows/release.yml

@@ -20,6 +20,8 @@ jobs:
     uses: ./.github/workflows/release-branch.yml
     with:
       version: ${{ github.event.inputs.version }}
+    secrets: inherit
+
   
   release-assets:
     needs: release-branch
@@ -27,12 +29,14 @@ jobs:
     with:
       version: ${{ github.event.inputs.version }}
       prerelease: ${{ github.event.inputs.prerelease == 'true' }}
+    secrets: inherit
 
   docker:
     needs: release-branch
     uses: ./.github/workflows/publish-docker.yml
     with:
       tag: ${{ github.event.inputs.version }}
+    secrets: inherit
 
   packages:
     if: ${{ github.event.inputs.prerelease == 'false' }}
@@ -40,6 +44,7 @@ jobs:
     uses: ./.github/workflows/packages.yml
     with:
       version: ${{ github.event.inputs.version }}
+    secrets: inherit
 
   pull-request:
     if: ${{ github.event.inputs.prerelease == 'false' }}
@@ -47,3 +52,4 @@ jobs:
     uses: ./.github/workflows/pull-request.yml
     with:
       version: ${{ github.event.inputs.version }}
+    secrets: inherit

+ 4 - 4
.github/workflows/test.yml

@@ -13,7 +13,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v3
       - name: Setup Go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4
         with:
           go-version: 1.19
       - name: Build
@@ -27,7 +27,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v3
       - name: Setup go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4
         with:
           go-version: 1.19
       - name: Build
@@ -44,7 +44,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v3
       - name: Setup Go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4
         with:
           go-version: 1.19
       - name: run tests
@@ -63,7 +63,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v3
       - name: Setup Go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4
         with:
           go-version: 1.19
       - name: run static checks

+ 1 - 1
.github/workflows/upgraderelease.yml

@@ -20,7 +20,7 @@ jobs:
       - run: |
           git fetch --force --tags
       - name: Setup go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4
         with:
           go-version: 1.19
       - name: goreleaser

+ 1 - 1
.goreleaser.prerelease.yaml

@@ -28,7 +28,7 @@ builds:
     binary: 'nmctl'
 archives:
   - format: binary
-    name_template: '{{ .Binary }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}'
+    name_template: '{{ .Binary }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}-{{ . }}{{ end }}'
 release:
   prerelease: true
     

+ 1 - 1
.goreleaser.yaml

@@ -28,7 +28,7 @@ builds:
     binary: 'nmctl'
 archives:
   - format: binary
-    name_template: '{{ .Binary }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}'
+    name_template: '{{ .Binary }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}-{{ . }}{{ end }}'
 release:
   prerelease: false
     

+ 3 - 3
auth/auth.go

@@ -114,10 +114,10 @@ func HandleAuthCallback(w http.ResponseWriter, r *http.Request) {
 	if err == nil || errors.Is(err, netcache.ErrExpired) {
 		switch len(state) {
 		case node_signin_length:
-			logger.Log(0, "proceeding with node SSO callback")
-			HandleNodeSSOCallback(w, r)
+			logger.Log(1, "proceeding with host SSO callback")
+			HandleHostSSOCallback(w, r)
 		case headless_signin_length:
-			logger.Log(0, "proceeding with headless SSO callback")
+			logger.Log(1, "proceeding with headless SSO callback")
 			HandleHeadlessSSOCallback(w, r)
 		default:
 			logger.Log(1, "invalid state length: ", fmt.Sprintf("%d", len(state)))

+ 258 - 0
auth/host_session.go

@@ -0,0 +1,258 @@
+package auth
+
+import (
+	"encoding/json"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/google/uuid"
+	"github.com/gorilla/websocket"
+	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/logic/hostactions"
+	"github.com/gravitl/netmaker/logic/pro/netcache"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mq"
+	"github.com/gravitl/netmaker/servercfg"
+)
+
+// SessionHandler - called by the HTTP router when user
+// is calling netclient with join/register -s parameter in order to authenticate
+// via SSO mechanism by OAuth2 protocol flow.
+// This triggers a session start and it is managed by the flow implemented here and callback
+// When this method finishes - the auth flow has finished either OK or by timeout or any other error occured
+func SessionHandler(conn *websocket.Conn) {
+	defer conn.Close()
+	// If reached here we have a session from user to handle...
+	messageType, message, err := conn.ReadMessage()
+	if err != nil {
+		logger.Log(0, "Error during message reading:", err.Error())
+		return
+	}
+
+	var registerMessage models.RegisterMsg
+	if err = json.Unmarshal(message, &registerMessage); err != nil {
+		logger.Log(0, "Failed to unmarshall data err=", err.Error())
+		return
+	}
+	if registerMessage.RegisterHost.ID == uuid.Nil {
+		logger.Log(0, "invalid host registration attempted")
+		return
+	}
+
+	req := new(netcache.CValue)
+	req.Value = string(registerMessage.RegisterHost.ID.String())
+	req.Network = registerMessage.Network
+	req.Host = registerMessage.RegisterHost
+	req.ALL = registerMessage.JoinAll
+	req.Pass = ""
+	req.User = registerMessage.User
+	if len(req.User) > 0 && len(registerMessage.Password) == 0 {
+		logger.Log(0, "invalid host registration attempted")
+		return
+	}
+	// Add any extra parameter provided in the configuration to the Authorize Endpoint request??
+	stateStr := logic.RandomString(node_signin_length)
+	if err := netcache.Set(stateStr, req); err != nil {
+		logger.Log(0, "Failed to process sso request -", err.Error())
+		return
+	}
+	// Wait for the user to finish his auth flow...
+	timeout := make(chan bool, 1)
+	answer := make(chan netcache.CValue, 1)
+	defer close(answer)
+	defer close(timeout)
+
+	if len(registerMessage.User) > 0 { // handle basic auth
+		logger.Log(0, "user registration attempted with host:", registerMessage.RegisterHost.Name, "user:", registerMessage.User)
+
+		if !servercfg.IsBasicAuthEnabled() {
+			err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
+			if err != nil {
+				logger.Log(0, "error during message writing:", err.Error())
+			}
+		}
+		_, err := logic.VerifyAuthRequest(models.UserAuthParams{
+			UserName: registerMessage.User,
+			Password: registerMessage.Password,
+		})
+		if err != nil {
+			err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
+			if err != nil {
+				logger.Log(0, "error during message writing:", err.Error())
+			}
+			return
+		}
+		req.Pass = req.Host.ID.String()
+
+		if err = netcache.Set(stateStr, req); err != nil { // give the user's host access in the DB
+			logger.Log(0, "machine failed to complete join on network,", registerMessage.Network, "-", err.Error())
+			return
+		}
+	} else { // handle SSO / OAuth
+		if auth_provider == nil {
+			err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
+			if err != nil {
+				logger.Log(0, "error during message writing:", err.Error())
+			}
+			return
+		}
+		logger.Log(0, "user registration attempted with host:", registerMessage.RegisterHost.Name, "via SSO")
+		redirectUrl = fmt.Sprintf("https://%s/api/oauth/register/%s", servercfg.GetAPIConnString(), stateStr)
+		err = conn.WriteMessage(messageType, []byte(redirectUrl))
+		if err != nil {
+			logger.Log(0, "error during message writing:", err.Error())
+		}
+	}
+
+	go func() {
+		for {
+			cachedReq, err := netcache.Get(stateStr)
+			if err != nil {
+				if strings.Contains(err.Error(), "expired") {
+					logger.Log(1, "timeout occurred while waiting for SSO registration")
+					timeout <- true
+					break
+				}
+				continue
+			} else if len(cachedReq.User) > 0 {
+				logger.Log(0, "host SSO process completed for user", cachedReq.User)
+				answer <- *cachedReq
+				break
+			}
+			time.Sleep(500) // try it 2 times per second to see if auth is completed
+		}
+	}()
+
+	select {
+	case result := <-answer: // a read from req.answerCh has occurred
+		// add the host, if not exists, handle like enrollment registration
+		hostPass := result.Host.HostPass
+		if !logic.HostExists(&result.Host) { // check if host already exists, add if not
+			if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
+				if err := mq.CreateEmqxUser(result.Host.ID.String(), result.Host.HostPass, false); err != nil {
+					logger.Log(0, "failed to create host credentials for EMQX: ", err.Error())
+					return
+				}
+				if err := mq.CreateHostACL(result.Host.ID.String(), servercfg.GetServerInfo().Server); err != nil {
+					logger.Log(0, "failed to add host ACL rules to EMQX: ", err.Error())
+					return
+				}
+			}
+			logic.CheckHostPorts(&result.Host)
+			if err := logic.CreateHost(&result.Host); err != nil {
+				handleHostRegErr(conn, err)
+				return
+			}
+		}
+		key, keyErr := logic.RetrievePublicTrafficKey()
+		if keyErr != nil {
+			handleHostRegErr(conn, err)
+			return
+		}
+		currHost, err := logic.GetHost(result.Host.ID.String())
+		if err != nil {
+			handleHostRegErr(conn, err)
+			return
+		}
+		var currentNetworks = []string{}
+		if result.ALL {
+			currentNets, err := logic.GetNetworks()
+			if err == nil && len(currentNets) > 0 {
+				for i := range currentNets {
+					currentNetworks = append(currentNetworks, currentNets[i].NetID)
+				}
+			}
+		} else if len(result.Network) > 0 {
+			currentNetworks = append(currentNetworks, result.Network)
+		}
+		var netsToAdd = []string{} // track the networks not currently owned by host
+		hostNets := logic.GetHostNetworks(currHost.ID.String())
+		for _, newNet := range currentNetworks {
+			if !logic.StringSliceContains(hostNets, newNet) {
+				if len(result.User) > 0 {
+					_, err := isUserIsAllowed(result.User, newNet, false)
+					if err != nil {
+						logger.Log(0, "unauthorized user", result.User, "attempted to register to network", newNet)
+						handleHostRegErr(conn, err)
+						return
+					}
+				}
+				netsToAdd = append(netsToAdd, newNet)
+			}
+		}
+		server := servercfg.GetServerInfo()
+		server.TrafficKey = key
+		if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
+			// set MQ username and password for EMQX clients
+			server.MQUserName = result.Host.ID.String()
+			server.MQPassword = hostPass
+		}
+		result.Host.HostPass = ""
+		response := models.RegisterResponse{
+			ServerConf:    server,
+			RequestedHost: result.Host,
+		}
+		reponseData, err := json.Marshal(&response)
+		if err != nil {
+			handleHostRegErr(conn, err)
+			return
+		}
+		if err = conn.WriteMessage(messageType, reponseData); err != nil {
+			logger.Log(0, "error during message writing:", err.Error())
+		}
+		go CheckNetRegAndHostUpdate(netsToAdd[:], &result.Host)
+	case <-timeout: // the read from req.answerCh has timed out
+		if err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")); err != nil {
+			logger.Log(0, "error during timeout message writing:", err.Error())
+		}
+	}
+	// The entry is not needed anymore, but we will let the producer to close it to avoid panic cases
+	if err = netcache.Del(stateStr); err != nil {
+		logger.Log(0, "failed to remove node SSO cache entry", err.Error())
+	}
+	// Cleanly close the connection by sending a close message and then
+	// waiting (with timeout) for the server to close the connection.
+	if err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")); err != nil {
+		logger.Log(0, "write close:", err.Error())
+		return
+	}
+}
+
+// CheckNetRegAndHostUpdate - run through networks and send a host update
+func CheckNetRegAndHostUpdate(networks []string, h *models.Host) {
+	// publish host update through MQ
+	for i := range networks {
+		network := networks[i]
+		if ok, _ := logic.NetworkExists(network); ok {
+			newNode, err := logic.UpdateHostNetwork(h, network, true)
+			if err != nil {
+				logger.Log(0, "failed to add host to network:", h.ID.String(), h.Name, network, err.Error())
+				continue
+			}
+			logger.Log(1, "added new node", newNode.ID.String(), "to host", h.Name)
+			hostactions.AddAction(models.HostUpdate{
+				Action: models.JoinHostToNetwork,
+				Host:   *h,
+				Node:   *newNode,
+			})
+		}
+	}
+	if servercfg.IsMessageQueueBackend() {
+		mq.HostUpdate(&models.HostUpdate{
+			Action: models.RequestAck,
+			Host:   *h,
+		})
+		if err := mq.PublishPeerUpdate(); err != nil {
+			logger.Log(0, "failed to publish peer update during registration -", err.Error())
+		}
+	}
+}
+
+func handleHostRegErr(conn *websocket.Conn, err error) {
+	_ = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
+	if err != nil {
+		logger.Log(0, "error during host registration via auth:", err.Error())
+	}
+}

+ 0 - 162
auth/nodesession.go

@@ -1,162 +0,0 @@
-package auth
-
-import (
-	"encoding/json"
-	"fmt"
-	"strings"
-	"time"
-
-	"github.com/gorilla/websocket"
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/logic/pro/netcache"
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
-	"github.com/gravitl/netmaker/servercfg"
-)
-
-// SessionHandler - called by the HTTP router when user
-// is calling netclient with --login-server parameter in order to authenticate
-// via SSO mechanism by OAuth2 protocol flow.
-// This triggers a session start and it is managed by the flow implemented here and callback
-// When this method finishes - the auth flow has finished either OK or by timeout or any other error occured
-func SessionHandler(conn *websocket.Conn) {
-	defer conn.Close()
-
-	// If reached here we have a session from user to handle...
-	messageType, message, err := conn.ReadMessage()
-	if err != nil {
-		logger.Log(0, "Error during message reading:", err.Error())
-		return
-	}
-	var loginMessage promodels.LoginMsg
-
-	err = json.Unmarshal(message, &loginMessage)
-	if err != nil {
-		logger.Log(0, "Failed to unmarshall data err=", err.Error())
-		return
-	}
-	logger.Log(1, "SSO node join attempted with info network:", loginMessage.Network, "node identifier:", loginMessage.Mac, "user:", loginMessage.User)
-
-	req := new(netcache.CValue)
-	req.Value = string(loginMessage.Mac)
-	req.Network = loginMessage.Network
-	req.Pass = ""
-	req.User = ""
-	// Add any extra parameter provided in the configuration to the Authorize Endpoint request??
-	stateStr := logic.RandomString(node_signin_length)
-	if err := netcache.Set(stateStr, req); err != nil {
-		logger.Log(0, "Failed to process sso request -", err.Error())
-		return
-	}
-	// Wait for the user to finish his auth flow...
-	// TBD: what should be the timeout here ?
-	timeout := make(chan bool, 1)
-	answer := make(chan string, 1)
-	defer close(answer)
-	defer close(timeout)
-
-	if _, err = logic.GetNetwork(loginMessage.Network); err != nil {
-		err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
-		if err != nil {
-			logger.Log(0, "error during message writing:", err.Error())
-		}
-		return
-	}
-
-	if loginMessage.User != "" { // handle basic auth
-		// verify that server supports basic auth, then authorize the request with given credentials
-		// check if user is allowed to join via node sso
-		// i.e. user is admin or user has network permissions
-		if !servercfg.IsBasicAuthEnabled() {
-			err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
-			if err != nil {
-				logger.Log(0, "error during message writing:", err.Error())
-			}
-		}
-		_, err := logic.VerifyAuthRequest(models.UserAuthParams{
-			UserName: loginMessage.User,
-			Password: loginMessage.Password,
-		})
-		if err != nil {
-			err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
-			if err != nil {
-				logger.Log(0, "error during message writing:", err.Error())
-			}
-			return
-		}
-		_, err = isUserIsAllowed(loginMessage.User, loginMessage.Network, false)
-		if err != nil {
-			err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
-			if err != nil {
-				logger.Log(0, "error during message writing:", err.Error())
-			}
-			return
-		}
-
-		// Give the user the access token via Pass in the DB
-		if err = netcache.Set(stateStr, req); err != nil {
-			logger.Log(0, "machine failed to complete join on network,", loginMessage.Network, "-", err.Error())
-			return
-		}
-	} else { // handle SSO / OAuth
-		if auth_provider == nil {
-			err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
-			if err != nil {
-				logger.Log(0, "error during message writing:", err.Error())
-			}
-			return
-		}
-		redirectUrl = fmt.Sprintf("https://%s/api/oauth/register/%s", servercfg.GetAPIConnString(), stateStr)
-		err = conn.WriteMessage(messageType, []byte(redirectUrl))
-		if err != nil {
-			logger.Log(0, "error during message writing:", err.Error())
-		}
-	}
-
-	go func() {
-		for {
-			cachedReq, err := netcache.Get(stateStr)
-			if err != nil {
-				if strings.Contains(err.Error(), "expired") {
-					logger.Log(0, "timeout occurred while waiting for SSO on network", loginMessage.Network)
-					timeout <- true
-					break
-				}
-				continue
-			} else if cachedReq.Pass != "" {
-				logger.Log(0, "node SSO process completed for user", cachedReq.User, "on network", loginMessage.Network)
-				answer <- cachedReq.Pass
-				break
-			}
-			time.Sleep(500) // try it 2 times per second to see if auth is completed
-		}
-	}()
-
-	select {
-	case result := <-answer:
-		// a read from req.answerCh has occurred
-		err = conn.WriteMessage(messageType, []byte(result))
-		if err != nil {
-			logger.Log(0, "Error during message writing:", err.Error())
-		}
-	case <-timeout:
-		logger.Log(0, "Authentication server time out for a node on network", loginMessage.Network)
-		// the read from req.answerCh has timed out
-		err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
-		if err != nil {
-			logger.Log(0, "Error during message writing:", err.Error())
-		}
-	}
-	// The entry is not needed anymore, but we will let the producer to close it to avoid panic cases
-	if err = netcache.Del(stateStr); err != nil {
-		logger.Log(0, "failed to remove node SSO cache entry", err.Error())
-	}
-	// Cleanly close the connection by sending a close message and then
-	// waiting (with timeout) for the server to close the connection.
-	err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
-	if err != nil {
-		logger.Log(0, "write close:", err.Error())
-		return
-	}
-}

+ 8 - 81
auth/nodecallback.go → auth/register_callback.go

@@ -19,13 +19,13 @@ var (
 	redirectUrl string
 )
 
-// HandleNodeSSOCallback handles the callback from the sso endpoint
+// HandleHostSSOCallback handles the callback from the sso endpoint
 // It is the analogue of auth.handleNodeSSOCallback but takes care of the end point flow
 // Retrieves the mkey from the state cache and adds the machine to the users email namespace
 // TODO: A confirmation page for new machines should be added to avoid phishing vulnerabilities
 // TODO: Add groups information from OIDC tokens into machine HostInfo
 // Listens in /oidc/callback.
-func HandleNodeSSOCallback(w http.ResponseWriter, r *http.Request) {
+func HandleHostSSOCallback(w http.ResponseWriter, r *http.Request) {
 
 	var functions = getCurrentAuthFunctions()
 	if functions == nil {
@@ -71,16 +71,7 @@ func HandleNodeSSOCallback(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	user, err := isUserIsAllowed(userClaims.getUserName(), reqKeyIf.Network, true)
-	if err != nil {
-		logger.Log(0, "error occurred during SSO node join for user", userClaims.getUserName(), "on network", reqKeyIf.Network, "-", err.Error())
-		response := returnErrTemplate(user.UserName, err.Error(), state, reqKeyIf)
-		w.WriteHeader(http.StatusNotAcceptable)
-		w.Write(response)
-		return
-	}
-
-	logger.Log(1, "registering new node for user:", user.UserName, "on network", reqKeyIf.Network)
+	logger.Log(1, "registering host for user:", userClaims.getUserName(), reqKeyIf.Host.Name, reqKeyIf.Host.ID.String())
 
 	// Send OK to user in the browser
 	var response bytes.Buffer
@@ -89,32 +80,15 @@ func HandleNodeSSOCallback(w http.ResponseWriter, r *http.Request) {
 		Verb: "Authenticated",
 	}); err != nil {
 		logger.Log(0, "Could not render SSO callback template ", err.Error())
-		response := returnErrTemplate(user.UserName, "Could not render SSO callback template", state, reqKeyIf)
+		response := returnErrTemplate(reqKeyIf.User, "Could not render SSO callback template", state, reqKeyIf)
 		w.WriteHeader(http.StatusInternalServerError)
 		w.Write(response)
-
 	} else {
 		w.WriteHeader(http.StatusOK)
 		w.Write(response.Bytes())
 	}
 
-	// Need to send access key to the client
-	logger.Log(1, "Handling new machine addition to network",
-		reqKeyIf.Network, "with key",
-		reqKeyIf.Value, " identity:", userClaims.getUserName(), "claims:", fmt.Sprintf("%+v", userClaims))
-
-	var answer string
-	// The registation logic is starting here:
-	// we request access key with 1 use for the required network
-	// accessToken, err := requestAccessKey(reqKeyIf.Network, 1, userClaims.getUserName())
-	// if err != nil {
-	// 	answer = fmt.Sprintf("Error from the netmaker controller %s", err.Error())
-	// } else {
-	// 	answer = fmt.Sprintf("AccessToken: %s", accessToken)
-	// }
-	logger.Log(0, "Updating the token for the client request ... ")
-	// Give the user the access token via Pass in the DB
-	reqKeyIf.Pass = answer
+	reqKeyIf.User = userClaims.getUserName() // set the cached registering hosts' user
 	if err = netcache.Set(state, reqKeyIf); err != nil {
 		logger.Log(0, "machine failed to complete join on network,", reqKeyIf.Network, "-", err.Error())
 		return
@@ -151,10 +125,10 @@ func returnErrTemplate(uname, message, state string, ncache *netcache.CValue) []
 	return response.Bytes()
 }
 
-// RegisterNodeSSO redirects to the IDP for authentication
+// RegisterHostSSO redirects to the IDP for authentication
 // Puts machine key in cache so the callback can retrieve it using the oidc state param
 // Listens in /oidc/register/:regKey.
-func RegisterNodeSSO(w http.ResponseWriter, r *http.Request) {
+func RegisterHostSSO(w http.ResponseWriter, r *http.Request) {
 
 	if auth_provider == nil {
 		w.WriteHeader(http.StatusBadRequest)
@@ -165,63 +139,16 @@ func RegisterNodeSSO(w http.ResponseWriter, r *http.Request) {
 
 	// machineKeyStr this is not key but state
 	machineKeyStr := vars["regKey"]
-	logger.Log(1, "requested key:", machineKeyStr)
-
 	if machineKeyStr == "" {
 		w.WriteHeader(http.StatusBadRequest)
 		w.Write([]byte("invalid login attempt"))
 		return
 	}
 
-	// machineKeyStr this not key but state
-	authURL := auth_provider.AuthCodeURL(machineKeyStr)
-	//authURL = authURL + "&connector_id=" + "google"
-	logger.Log(0, "Redirecting to ", authURL, " for authentication")
-
-	http.Redirect(w, r, authURL, http.StatusSeeOther)
-
+	http.Redirect(w, r, auth_provider.AuthCodeURL(machineKeyStr), http.StatusSeeOther)
 }
 
 // == private ==
-// API to create an access key for a given network with a given name
-// func requestAccessKey(network string, uses int, name string) (accessKey string, err error) {
-
-// 	var sAccessKey models.AccessKey
-// 	var sNetwork models.Network
-
-// 	sNetwork, err = logic.GetParentNetwork(network)
-// 	if err != nil {
-// 		logger.Log(0, "err calling GetParentNetwork API=%s", err.Error())
-// 		return "", fmt.Errorf("internal controller error %s", err.Error())
-// 	}
-// 	// If a key already exists, we recreate it.
-// 	// @TODO Is that a preferred handling ? We could also trying to re-use.
-// 	// can happen if user started log in but did not finish
-// 	for _, currentkey := range sNetwork.AccessKeys {
-// 		if currentkey.Name == name {
-// 			logger.Log(0, "erasing existing AccessKey for: ", name)
-// 			err = logic.DeleteKey(currentkey.Name, network)
-// 			if err != nil {
-// 				logger.Log(0, "err calling CreateAccessKey API ", err.Error())
-// 				return "", fmt.Errorf("key already exists. Contact admin to resolve")
-// 			}
-// 			break
-// 		}
-// 	}
-// 	// Only one usage is needed - for the next time new access key will be required
-// 	// it will be created next time after another IdP approval
-// 	sAccessKey.Uses = 1
-// 	sAccessKey.Name = name
-
-// 	accessToken, err := logic.CreateAccessKey(sAccessKey, sNetwork)
-// 	if err != nil {
-// 		logger.Log(0, "err calling CreateAccessKey API ", err.Error())
-// 		return "", fmt.Errorf("error from the netmaker controller %s", err.Error())
-// 	} else {
-// 		logger.Log(1, "created access key", sAccessKey.Name, "on", network)
-// 	}
-// 	return accessToken.AccessString, nil
-// }
 
 func isUserIsAllowed(username, network string, shouldAddUser bool) (*models.User, error) {
 

+ 21 - 0
cli/cmd/host/refresh_keys.go

@@ -0,0 +1,21 @@
+package host
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var hostRefreshKeysCmd = &cobra.Command{
+	Use:   "refresh_keys [HOST ID] ",
+	Args:  cobra.MaximumNArgs(1),
+	Short: "Refresh wireguard keys on host",
+	Long: `Refresh wireguard keys on specified or all hosts
+	If HOSTID is not specified, all hosts will be updated`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.RefreshKeys(args[0]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(hostRefreshKeysCmd)
+}

+ 0 - 20
cli/cmd/network/refresh_keys.go

@@ -1,20 +0,0 @@
-package network
-
-import (
-	"github.com/gravitl/netmaker/cli/functions"
-	"github.com/spf13/cobra"
-)
-
-var networkRefreshKeysCmd = &cobra.Command{
-	Use:   "refresh_keys [NETWORK NAME]",
-	Short: "Refresh public and private key pairs of a network",
-	Long:  `Refresh public and private key pairs of a network`,
-	Args:  cobra.ExactArgs(1),
-	Run: func(cmd *cobra.Command, args []string) {
-		functions.PrettyPrint(functions.RefreshKeys(args[0]))
-	},
-}
-
-func init() {
-	rootCmd.AddCommand(networkRefreshKeysCmd)
-}

+ 0 - 72
cli/cmd/network/update.go

@@ -1,72 +0,0 @@
-package network
-
-import (
-	"encoding/json"
-	"log"
-	"os"
-
-	"github.com/gravitl/netmaker/cli/functions"
-	"github.com/gravitl/netmaker/models"
-	"github.com/spf13/cobra"
-)
-
-var networkUpdateCmd = &cobra.Command{
-	Use:   "update [NETWORK NAME]",
-	Short: "Update a Network",
-	Long:  `Update a Network`,
-	Args:  cobra.ExactArgs(1),
-	Run: func(cmd *cobra.Command, args []string) {
-		var (
-			networkName = args[0]
-			network     = &models.Network{}
-		)
-		if networkDefinitionFilePath != "" {
-			content, err := os.ReadFile(networkDefinitionFilePath)
-			if err != nil {
-				log.Fatal("Error when opening file: ", err)
-			}
-			if err := json.Unmarshal(content, network); err != nil {
-				log.Fatal(err)
-			}
-		} else {
-			network.NetID = networkName
-			network.AddressRange = address
-			if address6 != "" {
-				network.AddressRange6 = address6
-				network.IsIPv6 = "yes"
-			}
-			if udpHolePunch {
-				network.DefaultUDPHolePunch = "yes"
-			}
-			if defaultACL {
-				network.DefaultACL = "yes"
-			}
-			network.DefaultInterface = defaultInterface
-			network.DefaultListenPort = int32(defaultListenPort)
-			network.NodeLimit = int32(nodeLimit)
-			network.DefaultKeepalive = int32(defaultKeepalive)
-			if allowManualSignUp {
-				network.AllowManualSignUp = "yes"
-			}
-			network.DefaultExtClientDNS = defaultExtClientDNS
-			network.DefaultMTU = int32(defaultMTU)
-		}
-		functions.PrettyPrint(functions.UpdateNetwork(networkName, network))
-	},
-}
-
-func init() {
-	networkUpdateCmd.Flags().StringVar(&networkDefinitionFilePath, "file", "", "Path to network_definition.json")
-	networkUpdateCmd.Flags().StringVar(&address, "ipv4_addr", "", "IPv4 address of the network")
-	networkUpdateCmd.Flags().StringVar(&address6, "ipv6_addr", "", "IPv6 address of the network")
-	networkUpdateCmd.Flags().BoolVar(&udpHolePunch, "udp_hole_punch", false, "Enable UDP Hole Punching ?")
-	networkUpdateCmd.Flags().BoolVar(&defaultACL, "default_acl", false, "Enable default Access Control List ?")
-	networkUpdateCmd.Flags().StringVar(&defaultInterface, "interface", "", "Name of the network interface")
-	networkUpdateCmd.Flags().StringVar(&defaultExtClientDNS, "ext_client_dns", "", "IPv4 address of DNS server to be used by external clients")
-	networkUpdateCmd.Flags().IntVar(&defaultListenPort, "listen_port", 0, "Default wireguard port each node will attempt to use")
-	networkUpdateCmd.Flags().IntVar(&nodeLimit, "node_limit", 0, "Maximum number of nodes that can be associated with this network")
-	networkUpdateCmd.Flags().IntVar(&defaultKeepalive, "keep_alive", 0, "Keep Alive in seconds")
-	networkUpdateCmd.Flags().IntVar(&defaultMTU, "mtu", 0, "MTU size")
-	networkUpdateCmd.Flags().BoolVar(&allowManualSignUp, "manual_signup", false, "Allow manual signup ?")
-	rootCmd.AddCommand(networkUpdateCmd)
-}

+ 9 - 0
cli/functions/host.go

@@ -48,3 +48,12 @@ func CreateRelay(hostID string, relayedHosts []string) *models.ApiHost {
 func DeleteRelay(hostID string) *models.ApiHost {
 	return request[models.ApiHost](http.MethodDelete, fmt.Sprintf("/api/hosts/%s/relay", hostID), nil)
 }
+
+// RefreshKeys - refresh wireguard keys
+func RefreshKeys(hostID string) any {
+	if hostID == "" {
+		return request[any](http.MethodPut, "/api/hosts/keys", nil)
+	}
+	return request[any](http.MethodPut, fmt.Sprintf("/api/hosts/%s/keys", hostID), nil)
+
+}

+ 0 - 5
cli/functions/network.go

@@ -38,8 +38,3 @@ func GetNetwork(name string) *models.Network {
 func DeleteNetwork(name string) *string {
 	return request[string](http.MethodDelete, "/api/networks/"+name, nil)
 }
-
-// RefreshKeys - refresh public and private key pairs for a network
-func RefreshKeys(networkName string) *models.Network {
-	return request[models.Network](http.MethodPost, fmt.Sprintf("/api/networks/%s/keyupdate", networkName), nil)
-}

+ 1 - 1
compose/docker-compose-emqx.yml

@@ -65,7 +65,7 @@ services:
       - dnsconfig:/root/dnsconfig
   mq:
     container_name: mq
-    image: emqx/emqx:5.0.17
+    image: emqx/emqx:5.0.9
     restart: unless-stopped
     environment:
       EMQX_NAME: "emqx"

+ 14 - 15
compose/docker-compose.ee.yml

@@ -9,9 +9,7 @@ services:
       - dnsconfig:/root/config/dnsconfig
       - sqldata:/root/data
     environment:
-      BROKER_ENDPOINT: "wss://broker.NETMAKER_BASE_DOMAIN/mqtt"
-      BROKER_TYPE: "emqx"
-      EMQX_REST_ENDPOINT: "http://mq:18083"
+      BROKER_ENDPOINT: "wss://broker.NETMAKER_BASE_DOMAIN"
       SERVER_NAME: "NETMAKER_BASE_DOMAIN"
       STUN_LIST: "stun.NETMAKER_BASE_DOMAIN:3478,stun1.netmaker.io:3478,stun2.netmaker.io:3478,stun1.l.google.com:19302,stun2.l.google.com:19302"
       SERVER_HOST: "SERVER_PUBLIC_IP"
@@ -25,7 +23,7 @@ services:
       DISPLAY_KEYS: "on"
       DATABASE: "sqlite"
       NODE_ID: "netmaker-server-1"
-      SERVER_BROKER_ENDPOINT: "ws://mq:8083/mqtt"
+      SERVER_BROKER_ENDPOINT: "ws://mq:1883"
       MQ_USERNAME: "REPLACE_MQ_USERNAME"
       MQ_PASSWORD: "REPLACE_MQ_PASSWORD"
       STUN_PORT: "3478"
@@ -68,17 +66,18 @@ services:
       - dnsconfig:/root/dnsconfig
   mq:
     container_name: mq
-    image: emqx/emqx:5.0.17
+    image: eclipse-mosquitto:2.0.15-openssl
+    depends_on:
+      - netmaker
     restart: unless-stopped
+    command: ["/mosquitto/config/wait.sh"]
     environment:
-      EMQX_NAME: "emqx"
-      EMQX_DASHBOARD__DEFAULT_PASSWORD: "REPLACE_MQ_PASSWORD"
-      EMQX_DASHBOARD__DEFAULT_USERNAME: "REPLACE_MQ_USERNAME"
-    ports:
-      - "1883:1883" # MQTT
-      - "8883:8883" # SSL MQTT
-      - "8083:8083" # Websockets
-      - "18083:18083" # Dashboard/REST_API
+      MQ_PASSWORD: "REPLACE_MQ_PASSWORD"
+      MQ_USERNAME: "REPLACE_MQ_USERNAME"
+    volumes:
+      - /root/mosquitto.conf:/mosquitto/config/mosquitto.conf
+      - /root/wait.sh:/mosquitto/config/wait.sh
+      - mosquitto_logs:/mosquitto/log
   prometheus:
     container_name: prometheus
     image: gravitl/netmaker-prometheus:latest
@@ -112,8 +111,8 @@ services:
     depends_on:
       - netmaker
     environment:
-      SERVER_BROKER_ENDPOINT: "ws://mq:8083/mqtt"
-      BROKER_ENDPOINT: "wss://broker.NETMAKER_BASE_DOMAIN/mqtt"
+      SERVER_BROKER_ENDPOINT: "ws://mq:1883"
+      BROKER_ENDPOINT: "wss://broker.NETMAKER_BASE_DOMAIN"
       PROMETHEUS: "on"
       VERBOSITY: "1"
       API_PORT: "8085"

+ 2 - 32
controllers/enrollmentkeys.go

@@ -7,9 +7,9 @@ import (
 	"time"
 
 	"github.com/gorilla/mux"
+	"github.com/gravitl/netmaker/auth"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/logic/hostactions"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/mq"
 	"github.com/gravitl/netmaker/servercfg"
@@ -230,35 +230,5 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(&response)
 	// notify host of changes, peer and node updates
-	go checkNetRegAndHostUpdate(enrollmentKey.Networks, &newHost)
-}
-
-// run through networks and send a host update
-func checkNetRegAndHostUpdate(networks []string, h *models.Host) {
-	// publish host update through MQ
-	for i := range networks {
-		network := networks[i]
-		if ok, _ := logic.NetworkExists(network); ok {
-			newNode, err := logic.UpdateHostNetwork(h, network, true)
-			if err != nil {
-				logger.Log(0, "failed to add host to network:", h.ID.String(), h.Name, network, err.Error())
-				continue
-			}
-			logger.Log(1, "added new node", newNode.ID.String(), "to host", h.Name)
-			hostactions.AddAction(models.HostUpdate{
-				Action: models.JoinHostToNetwork,
-				Host:   *h,
-				Node:   *newNode,
-			})
-		}
-	}
-	if servercfg.IsMessageQueueBackend() {
-		mq.HostUpdate(&models.HostUpdate{
-			Action: models.RequestAck,
-			Host:   *h,
-		})
-		if err := mq.PublishPeerUpdate(); err != nil {
-			logger.Log(0, "failed to publish peer update during registration -", err.Error())
-		}
-	}
+	go auth.CheckNetRegAndHostUpdate(enrollmentKey.Networks, &newHost)
 }

+ 1 - 0
controllers/hosts.go

@@ -29,6 +29,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/auth-register/host", socketHandler)
 }
 
 // swagger:route GET /api/hosts hosts getHosts

+ 2 - 1
controllers/migrate.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"net/http"
 
+	"github.com/gravitl/netmaker/auth"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
@@ -83,5 +84,5 @@ func migrate(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(&response)
 	logger.Log(0, "successfully migrated host", data.NewHost.Name, data.NewHost.ID.String())
 	// notify host of changes, peer and node updates
-	go checkNetRegAndHostUpdate(networksToAdd, &data.NewHost)
+	go auth.CheckNetRegAndHostUpdate(networksToAdd, &data.NewHost)
 }

+ 0 - 98
controllers/network.go

@@ -22,7 +22,6 @@ func networkHandlers(r *mux.Router) {
 	r.HandleFunc("/api/networks", logic.SecurityCheck(false, http.HandlerFunc(getNetworks))).Methods(http.MethodGet)
 	r.HandleFunc("/api/networks", logic.SecurityCheck(true, checkFreeTierLimits(networks_l, http.HandlerFunc(createNetwork)))).Methods(http.MethodPost)
 	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(false, http.HandlerFunc(getNetwork))).Methods(http.MethodGet)
-	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(false, http.HandlerFunc(updateNetwork))).Methods(http.MethodPut)
 	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(deleteNetwork))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/networks/{networkname}/keyupdate", logic.SecurityCheck(true, http.HandlerFunc(keyUpdate))).Methods(http.MethodPost)
 	// ACLs
@@ -142,103 +141,6 @@ func keyUpdate(w http.ResponseWriter, r *http.Request) {
 	}
 }
 
-// swagger:route PUT /api/networks/{networkname} networks updateNetwork
-//
-// Update a network.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: networkBodyResponse
-func updateNetwork(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-	var params = mux.Vars(r)
-	var network models.Network
-	netname := params["networkname"]
-
-	network, err := logic.GetParentNetwork(netname)
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"), "failed to get network info: ",
-			err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	var newNetwork models.Network
-	err = json.NewDecoder(r.Body).Decode(&newNetwork)
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"), "error decoding request body: ",
-			err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	rangeupdate4, rangeupdate6, holepunchupdate, groupsDelta, userDelta, err := logic.UpdateNetwork(&network, &newNetwork)
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"), "failed to update network: ",
-			err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-
-	if len(groupsDelta) > 0 {
-		for _, g := range groupsDelta {
-			users, err := logic.GetGroupUsers(g)
-			if err == nil {
-				for _, user := range users {
-					logic.AdjustNetworkUserPermissions(&user, &newNetwork)
-				}
-			}
-		}
-	}
-	if len(userDelta) > 0 {
-		for _, uname := range userDelta {
-			user, err := logic.GetReturnUser(uname)
-			if err == nil {
-				logic.AdjustNetworkUserPermissions(&user, &newNetwork)
-			}
-		}
-	}
-	if rangeupdate4 {
-		err = logic.UpdateNetworkNodeAddresses(network.NetID)
-		if err != nil {
-			logger.Log(0, r.Header.Get("user"),
-				fmt.Sprintf("failed to update network [%s] ipv4 addresses: %v",
-					network.NetID, err.Error()))
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-			return
-		}
-	}
-	if rangeupdate6 {
-		err = logic.UpdateNetworkNodeAddresses6(network.NetID)
-		if err != nil {
-			logger.Log(0, r.Header.Get("user"),
-				fmt.Sprintf("failed to update network [%s] ipv6 addresses: %v",
-					network.NetID, err.Error()))
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-			return
-		}
-	}
-	if rangeupdate4 || rangeupdate6 || holepunchupdate {
-		nodes, err := logic.GetNetworkNodes(network.NetID)
-		if err != nil {
-			logger.Log(0, r.Header.Get("user"),
-				fmt.Sprintf("failed to get network [%s] nodes: %v",
-					network.NetID, err.Error()))
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-			return
-		}
-		for _, node := range nodes {
-			runUpdates(&node, true)
-		}
-	}
-
-	logger.Log(1, r.Header.Get("user"), "updated network", netname)
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(newNetwork)
-}
-
 // swagger:route PUT /api/networks/{networkname}/acls networks updateNetworkACL
 //
 // Update a network ACL (Access Control List).

+ 13 - 11
controllers/node.go

@@ -564,7 +564,7 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 	var params = mux.Vars(r)
 	nodeid := params["nodeid"]
 	netid := params["network"]
-	node, wasFailover, err := logic.DeleteIngressGateway(netid, nodeid)
+	node, wasFailover, removedClients, err := logic.DeleteIngressGateway(netid, nodeid)
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 			fmt.Sprintf("failed to delete ingress gateway on node [%s] on network [%s]: %v",
@@ -584,6 +584,18 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(apiNode)
 
+	if len(removedClients) > 0 {
+		host, err := logic.GetHost(node.HostID.String())
+		if err == nil {
+			go mq.PublishSingleHostPeerUpdate(
+				context.Background(),
+				host,
+				nil,
+				removedClients[:],
+			)
+		}
+	}
+
 	runUpdates(&node, true)
 }
 
@@ -641,16 +653,6 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
-	if newNode.IsIngressGateway {
-		host.ProxyEnabled = true
-		err := logic.UpsertHost(host)
-		if err != nil {
-			logger.Log(0, r.Header.Get("user"),
-				fmt.Sprintf("failed to update host [ %s ]: %v", host.ID.String(), err))
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-			return
-		}
-	}
 	relayedUpdate := false
 	if currentNode.IsRelayed && (currentNode.Address.String() != newNode.Address.String() || currentNode.Address6.String() != newNode.Address6.String()) {
 		relayedUpdate = true

+ 18 - 3
controllers/user.go

@@ -33,9 +33,8 @@ func userHandlers(r *mux.Router) {
 	r.HandleFunc("/api/users", logic.SecurityCheck(true, http.HandlerFunc(getUsers))).Methods(http.MethodGet)
 	r.HandleFunc("/api/oauth/login", auth.HandleAuthLogin).Methods(http.MethodGet)
 	r.HandleFunc("/api/oauth/callback", auth.HandleAuthCallback).Methods(http.MethodGet)
-	r.HandleFunc("/api/oauth/node-handler", socketHandler)
 	r.HandleFunc("/api/oauth/headless", auth.HandleHeadlessSSO)
-	r.HandleFunc("/api/oauth/register/{regKey}", auth.RegisterNodeSSO).Methods(http.MethodGet)
+	r.HandleFunc("/api/oauth/register/{regKey}", auth.RegisterHostSSO).Methods(http.MethodGet)
 }
 
 // swagger:route POST /api/users/adm/authenticate user authenticateUser
@@ -331,7 +330,18 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	var params = mux.Vars(r)
 	// start here
+	jwtUser, _, isadmin, err := logic.VerifyJWT(r.Header.Get("Authorization"))
+	if err != nil {
+		logger.Log(0, "verifyJWT error", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
 	username := params["username"]
+	if username != jwtUser && !isadmin {
+		logger.Log(0, "non-admin user", jwtUser, "attempted to update user", username)
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("not authorizied"), "unauthorized"))
+		return
+	}
 	user, err := logic.GetUser(username)
 	if err != nil {
 		logger.Log(0, username,
@@ -354,6 +364,11 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 	}
+	if userchange.IsAdmin && !isadmin {
+		logger.Log(0, "non-admin user", jwtUser, "attempted get admin privilages")
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("not authorizied"), "unauthorized"))
+		return
+	}
 	userchange.Networks = nil
 	user, err = logic.UpdateUser(&userchange, user)
 	if err != nil {
@@ -467,5 +482,5 @@ func socketHandler(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	// Start handling the session
-	// go auth.SessionHandler(conn)
+	go auth.SessionHandler(conn)
 }

+ 9 - 9
go.mod

@@ -4,7 +4,7 @@ go 1.19
 
 require (
 	github.com/eclipse/paho.mqtt.golang v1.4.2
-	github.com/go-playground/validator/v10 v10.11.2
+	github.com/go-playground/validator/v10 v10.12.0
 	github.com/golang-jwt/jwt/v4 v4.5.0
 	github.com/google/uuid v1.3.0
 	github.com/gorilla/handlers v1.5.1
@@ -15,11 +15,11 @@ require (
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 	github.com/stretchr/testify v1.8.2
 	github.com/txn2/txeh v1.3.0
-	golang.org/x/crypto v0.7.0
-	golang.org/x/net v0.8.0 // indirect
-	golang.org/x/oauth2 v0.6.0
-	golang.org/x/sys v0.6.0 // indirect
-	golang.org/x/text v0.8.0 // indirect
+	golang.org/x/crypto v0.8.0
+	golang.org/x/net v0.9.0 // indirect
+	golang.org/x/oauth2 v0.7.0
+	golang.org/x/sys v0.7.0
+	golang.org/x/text v0.9.0 // indirect
 	golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c // indirect
 	golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220324164955-056925b7df31
 	google.golang.org/protobuf v1.28.1 // indirect
@@ -47,7 +47,7 @@ require (
 	github.com/matryer/is v1.4.1
 	github.com/olekukonko/tablewriter v0.0.5
 	github.com/pion/turn/v2 v2.1.0
-	github.com/spf13/cobra v1.6.1
+	github.com/spf13/cobra v1.7.0
 	github.com/ulule/limiter/v3 v3.11.1
 )
 
@@ -58,7 +58,7 @@ require (
 	github.com/gin-contrib/sse v0.1.0 // indirect
 	github.com/go-jose/go-jose/v3 v3.0.0 // indirect
 	github.com/goccy/go-json v0.10.0 // indirect
-	github.com/inconshreveable/mousetrap v1.0.1 // indirect
+	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/klauspost/cpuid/v2 v2.0.9 // indirect
 	github.com/kr/pretty v0.3.1 // indirect
@@ -87,7 +87,7 @@ require (
 	github.com/google/go-cmp v0.5.9 // indirect
 	github.com/hashicorp/go-version v1.6.0
 	github.com/josharian/native v1.0.0 // indirect
-	github.com/leodido/go-urn v1.2.1 // indirect
+	github.com/leodido/go-urn v1.2.2 // indirect
 	github.com/mattn/go-runewidth v0.0.13 // indirect
 	github.com/mdlayher/genetlink v1.2.0 // indirect
 	github.com/mdlayher/netlink v1.6.0 // indirect

+ 20 - 21
go.sum

@@ -29,8 +29,6 @@ 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/devilcove/httpclient v0.6.1 h1:Q4jiep/pJPt27VTRggsioCHqdx3z9//JHl4lwqz2Zls=
-github.com/devilcove/httpclient v0.6.1/go.mod h1:ctrAO2gRgTT+GxtRdWBp2SMQ+vacuxXlbhmlM4oWhs8=
 github.com/eclipse/paho.mqtt.golang v1.4.2 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6rNA5PM12m4=
 github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
 github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
@@ -48,8 +46,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
-github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
+github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI=
+github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA=
 github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
 github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
@@ -81,8 +79,8 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO
 github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
-github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
 github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -96,8 +94,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
-github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
+github.com/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4=
+github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ=
 github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
 github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@@ -157,14 +155,15 @@ github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f/go.mod h1:UW/gxgQw
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
-github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
-github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
+github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
+github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -204,8 +203,8 @@ golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
-golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
+golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
+golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
 golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
 golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@@ -222,11 +221,11 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
 golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
 golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
-golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
+golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
 golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk=
-golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
-golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
+golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
+golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -251,8 +250,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -264,8 +263,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 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=
@@ -287,8 +286,8 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
 google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
 google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 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=

+ 2 - 3
logic/extpeers.go

@@ -141,7 +141,7 @@ func CreateExtClient(extclient *models.ExtClient) error {
 		}
 		extclient.PrivateKey = privateKey.String()
 		extclient.PublicKey = privateKey.PublicKey().String()
-	} else {
+	} else if len(extclient.PrivateKey) == 0 && len(extclient.PublicKey) > 0 {
 		extclient.PrivateKey = "[ENTER PRIVATE KEY]"
 	}
 
@@ -194,8 +194,7 @@ func UpdateExtClient(newclientid string, network string, enabled bool, client *m
 	if err != nil {
 		return client, err
 	}
-	if newclientid != client.ClientID {
-		//name change only
+	if newclientid != client.ClientID { // name change only
 		client.ClientID = newclientid
 		client.LastModified = time.Now().Unix()
 		data, err := json.Marshal(&client)

+ 19 - 16
logic/gateway.go

@@ -26,8 +26,8 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
 		return models.Node{}, errors.New(host.OS + " is unsupported for egress gateways")
 	}
 	for i := len(gateway.Ranges) - 1; i >= 0; i-- {
-		if gateway.Ranges[i] == "0.0.0.0/0" || gateway.Ranges[i] == "::/0" {
-			logger.Log(0, "currently internet gateways are not supported", gateway.Ranges[i])
+		if gateway.Ranges[i] == "::/0" {
+			logger.Log(0, "currently IPv6 internet gateways are not supported", gateway.Ranges[i])
 			gateway.Ranges = append(gateway.Ranges[:i], gateway.Ranges[i+1:]...)
 			continue
 		}
@@ -134,22 +134,22 @@ func CreateIngressGateway(netid string, nodeid string, failover bool) (models.No
 }
 
 // DeleteIngressGateway - deletes an ingress gateway
-func DeleteIngressGateway(networkName string, nodeid string) (models.Node, bool, error) {
+func DeleteIngressGateway(networkName string, nodeid string) (models.Node, bool, []models.ExtClient, error) {
+	removedClients := []models.ExtClient{}
 	node, err := GetNodeByID(nodeid)
 	if err != nil {
-		return models.Node{}, false, err
+		return models.Node{}, false, removedClients, err
 	}
-	//host, err := GetHost(node.ID.String())
-	//if err != nil {
-	//return models.Node{}, false, err
-	//}
-	//network, err := GetParentNetwork(networkName)
-	if err != nil {
-		return models.Node{}, false, err
+	clients, err := GetExtClientsByID(nodeid, networkName)
+	if err != nil && !database.IsEmptyRecord(err) {
+		return models.Node{}, false, removedClients, err
 	}
+
+	removedClients = clients
+
 	// delete ext clients belonging to ingress gateway
 	if err = DeleteGatewayExtClients(node.ID.String(), networkName); err != nil {
-		return models.Node{}, false, err
+		return models.Node{}, false, removedClients, err
 	}
 	logger.Log(3, "deleting ingress gateway")
 	wasFailover := node.Failover
@@ -169,20 +169,23 @@ func DeleteIngressGateway(networkName string, nodeid string) (models.Node, bool,
 
 	data, err := json.Marshal(&node)
 	if err != nil {
-		return models.Node{}, false, err
+		return models.Node{}, false, removedClients, err
 	}
 	err = database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
 	if err != nil {
-		return models.Node{}, wasFailover, err
+		return models.Node{}, wasFailover, removedClients, err
 	}
 	err = SetNetworkNodesLastModified(networkName)
-	return node, wasFailover, err
+	return node, wasFailover, removedClients, err
 }
 
 // DeleteGatewayExtClients - deletes ext clients based on gateway (mac) of ingress node and network
 func DeleteGatewayExtClients(gatewayID string, networkName string) error {
 	currentExtClients, err := GetNetworkExtClients(networkName)
-	if err != nil && !database.IsEmptyRecord(err) {
+	if database.IsEmptyRecord(err) {
+		return nil
+	}
+	if err != nil {
 		return err
 	}
 	for _, extClient := range currentExtClients {

+ 13 - 0
logic/jwts.go

@@ -3,6 +3,7 @@ package logic
 import (
 	"errors"
 	"fmt"
+	"strings"
 	"time"
 
 	"github.com/golang-jwt/jwt/v4"
@@ -101,6 +102,18 @@ func CreateUserJWT(username string, networks []string, isadmin bool) (response s
 	return "", err
 }
 
+// VerifyJWT verifies Auth Header
+func VerifyJWT(bearerToken string) (username string, networks []string, isadmin bool, err error) {
+	token := ""
+	tokenSplit := strings.Split(bearerToken, " ")
+	if len(tokenSplit) > 1 {
+		token = tokenSplit[1]
+	} else {
+		return "", nil, false, errors.New("invalid auth header")
+	}
+	return VerifyUserToken(token)
+}
+
 // VerifyUserToken func will used to Verify the JWT Token while using APIS
 func VerifyUserToken(tokenString string) (username string, networks []string, isadmin bool, err error) {
 	claims := &models.UserClaims{}

+ 68 - 65
logic/peers.go

@@ -141,7 +141,7 @@ func ResetPeerUpdateContext() {
 }
 
 // GetPeerUpdateForHost - gets the consolidated peer update for the host from all networks
-func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host, deletedNode *models.Node, deletedClient *models.ExtClient) (models.HostPeerUpdate, error) {
+func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host, deletedNode *models.Node, deletedClients []models.ExtClient) (models.HostPeerUpdate, error) {
 	if host == nil {
 		return models.HostPeerUpdate{}, errors.New("host is nil")
 	}
@@ -326,68 +326,68 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
 					hostPeerUpdate.NodePeers = append(hostPeerUpdate.NodePeers, nodePeer)
 				}
 			}
-			var extPeers []wgtypes.PeerConfig
-			var extPeerIDAndAddrs []models.IDandAddr
-			if node.IsIngressGateway {
-				extPeers, extPeerIDAndAddrs, err = getExtPeers(&node)
-				if err == nil {
-					for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
-						extPeerIdAndAddr := extPeerIdAndAddr
-						nodePeerMap[extPeerIdAndAddr.ID] = models.PeerRouteInfo{
-							PeerAddr: net.IPNet{
-								IP:   net.ParseIP(extPeerIdAndAddr.Address),
-								Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
-							},
-							PeerKey: extPeerIdAndAddr.ID,
-							Allow:   true,
-							ID:      extPeerIdAndAddr.ID,
-						}
+		}
+		var extPeers []wgtypes.PeerConfig
+		var extPeerIDAndAddrs []models.IDandAddr
+		if node.IsIngressGateway {
+			extPeers, extPeerIDAndAddrs, err = getExtPeers(&node)
+			if err == nil {
+				for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
+					extPeerIdAndAddr := extPeerIdAndAddr
+					nodePeerMap[extPeerIdAndAddr.ID] = models.PeerRouteInfo{
+						PeerAddr: net.IPNet{
+							IP:   net.ParseIP(extPeerIdAndAddr.Address),
+							Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
+						},
+						PeerKey: extPeerIdAndAddr.ID,
+						Allow:   true,
+						ID:      extPeerIdAndAddr.ID,
+					}
+				}
+				hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, extPeers...)
+				for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
+					extPeerIdAndAddr := extPeerIdAndAddr
+					hostPeerUpdate.HostPeerIDs[extPeerIdAndAddr.ID] = make(map[string]models.IDandAddr)
+					hostPeerUpdate.HostPeerIDs[extPeerIdAndAddr.ID][extPeerIdAndAddr.ID] = models.IDandAddr{
+						ID:      extPeerIdAndAddr.ID,
+						Address: extPeerIdAndAddr.Address,
+						Name:    extPeerIdAndAddr.Name,
+						Network: node.Network,
 					}
-					hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, extPeers...)
-					for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
-						extPeerIdAndAddr := extPeerIdAndAddr
-						hostPeerUpdate.HostPeerIDs[extPeerIdAndAddr.ID] = make(map[string]models.IDandAddr)
-						hostPeerUpdate.HostPeerIDs[extPeerIdAndAddr.ID][extPeerIdAndAddr.ID] = models.IDandAddr{
-							ID:      extPeerIdAndAddr.ID,
-							Address: extPeerIdAndAddr.Address,
-							Name:    extPeerIdAndAddr.Name,
-							Network: node.Network,
-						}
 
-						hostPeerUpdate.IngressInfo.ExtPeers[extPeerIdAndAddr.ID] = models.ExtClientInfo{
-							Masquerade: true,
-							IngGwAddr: net.IPNet{
-								IP:   net.ParseIP(node.PrimaryAddress()),
-								Mask: getCIDRMaskFromAddr(node.PrimaryAddress()),
-							},
-							Network: node.PrimaryNetworkRange(),
-							ExtPeerAddr: net.IPNet{
-								IP:   net.ParseIP(extPeerIdAndAddr.Address),
-								Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
-							},
-							ExtPeerKey: extPeerIdAndAddr.ID,
-							Peers:      filterNodeMapForClientACLs(extPeerIdAndAddr.ID, node.Network, nodePeerMap),
-						}
-						if node.Network == network {
-							hostPeerUpdate.PeerIDs[extPeerIdAndAddr.ID] = extPeerIdAndAddr
-							hostPeerUpdate.NodePeers = append(hostPeerUpdate.NodePeers, extPeers...)
-						}
+					hostPeerUpdate.IngressInfo.ExtPeers[extPeerIdAndAddr.ID] = models.ExtClientInfo{
+						Masquerade: true,
+						IngGwAddr: net.IPNet{
+							IP:   net.ParseIP(node.PrimaryAddress()),
+							Mask: getCIDRMaskFromAddr(node.PrimaryAddress()),
+						},
+						Network: node.PrimaryNetworkRange(),
+						ExtPeerAddr: net.IPNet{
+							IP:   net.ParseIP(extPeerIdAndAddr.Address),
+							Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
+						},
+						ExtPeerKey: extPeerIdAndAddr.ID,
+						Peers:      filterNodeMapForClientACLs(extPeerIdAndAddr.ID, node.Network, nodePeerMap),
+					}
+					if node.Network == network {
+						hostPeerUpdate.PeerIDs[extPeerIdAndAddr.ID] = extPeerIdAndAddr
+						hostPeerUpdate.NodePeers = append(hostPeerUpdate.NodePeers, extPeers...)
 					}
-				} else if !database.IsEmptyRecord(err) {
-					logger.Log(1, "error retrieving external clients:", err.Error())
 				}
+			} else if !database.IsEmptyRecord(err) {
+				logger.Log(1, "error retrieving external clients:", err.Error())
 			}
-			if node.IsEgressGateway {
-				hostPeerUpdate.EgressInfo[node.ID.String()] = models.EgressInfo{
-					EgressID: node.ID.String(),
-					Network:  node.PrimaryNetworkRange(),
-					EgressGwAddr: net.IPNet{
-						IP:   net.ParseIP(node.PrimaryAddress()),
-						Mask: getCIDRMaskFromAddr(node.PrimaryAddress()),
-					},
-					GwPeers:     nodePeerMap,
-					EgressGWCfg: node.EgressGatewayRequest,
-				}
+		}
+		if node.IsEgressGateway {
+			hostPeerUpdate.EgressInfo[node.ID.String()] = models.EgressInfo{
+				EgressID: node.ID.String(),
+				Network:  node.PrimaryNetworkRange(),
+				EgressGwAddr: net.IPNet{
+					IP:   net.ParseIP(node.PrimaryAddress()),
+					Mask: getCIDRMaskFromAddr(node.PrimaryAddress()),
+				},
+				GwPeers:     nodePeerMap,
+				EgressGWCfg: node.EgressGatewayRequest,
 			}
 		}
 	}
@@ -409,13 +409,16 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
 		hostPeerUpdate.NodePeers[i] = peer
 	}
 
-	if deletedClient != nil {
-		key, err := wgtypes.ParseKey(deletedClient.PublicKey)
-		if err == nil {
-			hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, wgtypes.PeerConfig{
-				PublicKey: key,
-				Remove:    true,
-			})
+	if len(deletedClients) > 0 {
+		for i := range deletedClients {
+			deletedClient := deletedClients[i]
+			key, err := wgtypes.ParseKey(deletedClient.PublicKey)
+			if err == nil {
+				hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, wgtypes.PeerConfig{
+					PublicKey: key,
+					Remove:    true,
+				})
+			}
 		}
 	}
 

+ 8 - 5
logic/pro/netcache/netcache.go

@@ -6,6 +6,7 @@ import (
 	"time"
 
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/models"
 )
 
 const (
@@ -14,11 +15,13 @@ const (
 
 // CValue - the cache object for a network
 type CValue struct {
-	Network    string    `json:"network"`
-	Value      string    `json:"value"`
-	Pass       string    `json:"pass"`
-	User       string    `json:"user"`
-	Expiration time.Time `json:"expiration"`
+	Network    string      `json:"network,omitempty"`
+	Value      string      `json:"value"`
+	Host       models.Host `json:"host"`
+	Pass       string      `json:"pass,omitempty"`
+	User       string      `json:"user,omitempty"`
+	ALL        bool        `json:"all,omitempty"`
+	Expiration time.Time   `json:"expiration"`
 }
 
 var ErrExpired = fmt.Errorf("expired")

+ 9 - 0
models/host.go

@@ -142,3 +142,12 @@ type Signal struct {
 	ToHostPubKey      string `json:"to_host_pubkey"`
 	Reply             bool   `json:"reply"`
 }
+
+// RegisterMsg - login message struct for hosts to join via SSO login
+type RegisterMsg struct {
+	RegisterHost Host   `json:"host"`
+	Network      string `json:"network,omitempty"`
+	User         string `json:"user,omitempty"`
+	Password     string `json:"password,omitempty"`
+	JoinAll      bool   `json:"join_all,omitempty"`
+}

+ 0 - 9
models/promodels/pro.go

@@ -8,12 +8,3 @@ type ProNetwork struct {
 	AllowedUsers           []string `json:"allowedusers" bson:"allowedusers" yaml:"allowedusers"`
 	AllowedGroups          []string `json:"allowedgroups" bson:"allowedgroups" yaml:"allowedgroups"`
 }
-
-// LoginMsg - login message struct for nodes to join via SSO login
-// Need to change mac to public key for tighter verification ?
-type LoginMsg struct {
-	Mac      string `json:"mac"`
-	Network  string `json:"network"`
-	User     string `json:"user,omitempty"`
-	Password string `json:"password,omitempty"`
-}

+ 4 - 4
mq/emqx.go

@@ -205,7 +205,7 @@ func GetUserACL(username string) (*aclObject, error) {
 	if err != nil {
 		return nil, err
 	}
-	req, err := http.NewRequest(http.MethodGet, servercfg.GetEmqxRestEndpoint()+"/api/v5/authorization/sources/built_in_database/rules/users/"+username, nil)
+	req, err := http.NewRequest(http.MethodGet, servercfg.GetEmqxRestEndpoint()+"/api/v5/authorization/sources/built_in_database/username/"+username, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -241,7 +241,7 @@ func CreateDefaultDenyRule() error {
 	if err != nil {
 		return err
 	}
-	req, err := http.NewRequest(http.MethodPost, servercfg.GetEmqxRestEndpoint()+"/api/v5/authorization/sources/built_in_database/rules/all", bytes.NewReader(payload))
+	req, err := http.NewRequest(http.MethodPost, servercfg.GetEmqxRestEndpoint()+"/api/v5/authorization/sources/built_in_database/all", bytes.NewReader(payload))
 	if err != nil {
 		return err
 	}
@@ -301,7 +301,7 @@ func CreateHostACL(hostID, serverName string) error {
 	if err != nil {
 		return err
 	}
-	req, err := http.NewRequest(http.MethodPut, servercfg.GetEmqxRestEndpoint()+"/api/v5/authorization/sources/built_in_database/rules/users/"+hostID, bytes.NewReader(payload))
+	req, err := http.NewRequest(http.MethodPut, servercfg.GetEmqxRestEndpoint()+"/api/v5/authorization/sources/built_in_database/username/"+hostID, bytes.NewReader(payload))
 	if err != nil {
 		return err
 	}
@@ -369,7 +369,7 @@ func AppendNodeUpdateACL(hostID, nodeNetwork, nodeID, serverName string) error {
 	if err != nil {
 		return err
 	}
-	req, err := http.NewRequest(http.MethodPut, servercfg.GetEmqxRestEndpoint()+"/api/v5/authorization/sources/built_in_database/rules/users/"+hostID, bytes.NewReader(payload))
+	req, err := http.NewRequest(http.MethodPut, servercfg.GetEmqxRestEndpoint()+"/api/v5/authorization/sources/built_in_database/username/"+hostID, bytes.NewReader(payload))
 	if err != nil {
 		return err
 	}

+ 3 - 3
mq/publishers.go

@@ -71,7 +71,7 @@ func PublishDeletedClientPeerUpdate(delClient *models.ExtClient) error {
 	logic.ResetPeerUpdateContext()
 	for _, host := range hosts {
 		host := host
-		if err = PublishSingleHostPeerUpdate(logic.PeerUpdateCtx, &host, nil, delClient); err != nil {
+		if err = PublishSingleHostPeerUpdate(logic.PeerUpdateCtx, &host, nil, []models.ExtClient{*delClient}); err != nil {
 			logger.Log(1, "failed to publish peer update to host", host.ID.String(), ": ", err.Error())
 		}
 	}
@@ -79,9 +79,9 @@ func PublishDeletedClientPeerUpdate(delClient *models.ExtClient) error {
 }
 
 // PublishSingleHostPeerUpdate --- determines and publishes a peer update to one host
-func PublishSingleHostPeerUpdate(ctx context.Context, host *models.Host, deletedNode *models.Node, deletedClient *models.ExtClient) error {
+func PublishSingleHostPeerUpdate(ctx context.Context, host *models.Host, deletedNode *models.Node, deletedClients []models.ExtClient) error {
 
-	peerUpdate, err := logic.GetPeerUpdateForHost(ctx, "", host, deletedNode, deletedClient)
+	peerUpdate, err := logic.GetPeerUpdateForHost(ctx, "", host, deletedNode, deletedClients)
 	if err != nil {
 		return err
 	}

+ 4 - 14
release.md

@@ -1,27 +1,17 @@
-# Netmaker v0.18.6
-
-## **Wait till out of pre-release to fully upgrade**
+# Netmaker v0.18.7
 
 ## whats new
-- Logic for ext client ACLs (not really usable until new UI is finished)
-- Default proxy mode, enables users to determine if all Hosts should have proxy enabled/disabled/auto by default
-  - specify with DEFAULT_PROXY_MODE="on/off/auto" 
+- deprecated editing of network parameters
     
 ## whats fixed
-- Proxy Peer calculation improvements
-- DNS is populated correctly after registration by enrollment key
-- Migrate is functional for Windows/Mac **note** Ports may be set to 0 after an upgrade, can be adjusted via UI to fix
-- Interface data is sent on netclient register
-- Upgrade script
-- Latency issue with Node <-> Node Metrics
-- Ports set from server for Hosts on register/join are actually used
 
 ## known issues
 - Caddy does not handle netmaker exporter well for EE
-- Migration causes a listen port of 0 for upgraded hosts
+- Migration causes a listen port of 0 for some upgraded hosts
 - Docker clients can not re-join after deletion
 - Innacurate Ext Client Metrics 
 - Issue with Mac + IPv6 addressing
 - Nodes on same local network may not always connect
 - List populates egress ranges twice
 - If you do NOT set STUN_LIST on server, it could lead to strange behavior on client
+- No internet gateways/default routes

+ 3 - 2
scripts/nm-quick.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-LATEST="v0.18.5"
+LATEST="v0.18.6"
 
 print_logo() {(
 cat << "EOF"
@@ -182,7 +182,7 @@ setup_netclient() {
 	netclient uninstall
 	set -e
 
-	wget -O netclient https://github.com/gravitl/netclient/releases/download/$LATEST/netclient_linux_amd64
+	wget -O netclient https://github.com/gravitl/netclient/releases/download/$LATEST/netclient-linux-amd64
 	chmod +x netclient
 	./netclient install
 	netclient register -t $TOKEN
@@ -429,6 +429,7 @@ set_install_vars() {
 	echo "          dashboard.$NETMAKER_BASE_DOMAIN"
 	echo "                api.$NETMAKER_BASE_DOMAIN"
 	echo "             broker.$NETMAKER_BASE_DOMAIN"
+	echo "               stun.$NETMAKER_BASE_DOMAIN"
 
 	if [ "$INSTALL_TYPE" = "ee" ]; then
 		echo "         prometheus.$NETMAKER_BASE_DOMAIN"

+ 4 - 4
scripts/nm-upgrade.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-LATEST="v0.18.5"
+LATEST="v0.18.6"
 INSTALL_PATH="/root"
 
 trap restore_old_netmaker_instructions
@@ -22,7 +22,7 @@ backup_v17_files() {
   mkdir $INSTALL_PATH/netmaker_0.17.1_backup
   cp $INSTALL_PATH/docker-compose.yml  $INSTALL_PATH/netmaker_0.17.1_backup/docker-compose.yml
   cp $INSTALL_PATH/Caddyfile $INSTALL_PATH/netmaker_0.17.1_backup/Caddyfile
-  cp $INSTALL_PATH/mosquitto.conf %INSTALL_PATH/netmaker_0.17.1_backup/mosquitto.conf
+  cp $INSTALL_PATH/mosquitto.conf $INSTALL_PATH/netmaker_0.17.1_backup/mosquitto.conf
   cp $INSTALL_PATH/wait.sh $INSTALL_PATH/netmaker_0.17.1_backup/wait.sh
 }
 
@@ -38,7 +38,7 @@ backup_volumes() {
 restore_old_netmaker_instructions() {
   echo "There was a problem with the installation. Your config files and volumes have been backed up."
   echo "To restore Netmaker back to v0.17.1, copy all the netmaker volume backups (caddy_conf-backup, caddy_data-backup, dnsconfig-backup, mosquitto_data-backup, mosquitto_logs-backup, and sqldata-backup) back to their regular names with out the -backup."
-  echo "Your config files should be located in ${INSALL_PATH}/netmaker_0.17.1_backup. Simply run cp ${INSALL_PATH}/netmaker_0.17.1_backup/* . (include the .) and run docker-compose up -d."
+  echo "Your config files should be located in ${INSTALL_PATH}/netmaker_0.17.1_backup. Simply run cp ${INSTALL_PATH}/netmaker_0.17.1_backup/* . (include the .) and run docker-compose up -d."
   echo "Your netmaker should be back to v0.17.1"
 }
 
@@ -119,7 +119,7 @@ install_dependencies() {
     echo "version: $(docker version)"
   else
     echo "Docker not found. adding to dependencies"
-    $dependencies += " docker.io"
+    dependencies+=" docker.io"
   fi
 
   ${update_cmd}