Browse Source

merge conflicts resolved

Abhishek Kondur 2 years ago
parent
commit
5e8e56b56e

+ 1 - 0
.github/ISSUE_TEMPLATE/bug-report.yml

@@ -31,6 +31,7 @@ body:
       label: Version
       label: Version
       description: What version are you running?
       description: What version are you running?
       options:
       options:
+        - v0.18.5
         - v0.18.4
         - v0.18.4
         - v0.18.3
         - v0.18.3
         - v0.18.2
         - v0.18.2

+ 58 - 0
.github/workflows/branchtest.yml

@@ -0,0 +1,58 @@
+name: Deploy and Test Branch
+
+on:
+  workflow_dispatch:
+  pull_request:
+      types: [opened, synchronize, reopened]
+      branches: [develop]
+
+jobs:
+  skip-check:
+    runs-on: ubuntu-latest
+    outputs:
+      skip: ${{ steps.check.outputs.skip }}
+    steps:
+      - id: skip
+        uses: fkirc/skip-duplicate-actions@v5
+        with:
+          concurrent_skipping: 'always'
+  getbranch:
+    runs-on: ubuntu-latest
+    needs: skip-check
+    if: ${{ needs.skip-check.outputs.skip != 'true' }}
+    outputs:
+      netclientbranch: ${{ steps.checkbranch.outputs.netclientbranch }}
+    steps:
+      - name: checkout
+        uses: actions/checkout@v3
+        with:
+          repository: gravitl/netclient
+          ref: develop
+      - name: check if branch exists
+        id: checkbranch
+        run: |
+          if git show-ref ${{ github.head_ref}}; then
+            echo branch exists
+            echo "netclientbranch=${{ github.head_ref }}" >> $GITHUB_OUTPUT
+          else
+            echo branch does not exist
+            echo "netclientbranch=develop" >> $GITHUB_OUTPUT
+          fi
+  
+  terraform:
+    needs: getbranch
+    uses: gravitl/devops/.github/workflows/terraform.yml@master
+    with:
+      netmakerbranch: ${{ github.head_ref }}
+      netclientbranch: ${{ needs.getbranch.outputs.netclientbranch }}
+    secrets: inherit
+
+
+  testbranch:
+    needs: [getbranch, terraform]
+    uses: gravitl/devops/.github/workflows/branchtest.yml@master
+    with:
+      tag: ${{ github.run_id }}-${{ github.run_attempt }}
+      network: terraform
+    secrets: inherit
+    

+ 38 - 0
.github/workflows/deletedroplets.yml

@@ -0,0 +1,38 @@
+name: Delete Droplets
+
+on:
+  workflow_run:
+    workflows: [Deploy and Test Branch]
+    types:
+      - completed
+
+jobs:
+  on-success:
+    runs-on: ubuntu-latest
+    if: ${{ github.event.workflow_run.conclusion == 'success' }}
+    steps:
+      - name: delete droplets
+        run: |
+          sleep 15m
+          curl -X GET \
+            -H "Content-Type: application/json" \
+            -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
+            "https://api.digitalocean.com/v2/droplets?tag_name=$TAG"
+        env:
+          DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }}
+          TAG: ${{ github.event.workflow_run.run_id }}-${{ github.event.workflow_run.run_attempt }}
+
+  on-failure:
+    runs-on: ubuntu-latest
+    if: ${{ github.event.workflow_run.conclusion == 'failure' }}
+    steps:
+      - name: delete droplets
+        run: |
+          sleep 6h
+          curl -X GET \
+            -H "Content-Type: application/json" \
+            -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
+            "https://api.digitalocean.com/v2/droplets?tag_name=$TAG"
+        env:
+          DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }}
+          TAG: ${{ github.event.workflow_run.run_id }}-${{ github.event.workflow_run.run_attempt }}

+ 1 - 1
README.md

@@ -17,7 +17,7 @@
 
 
 <p align="center">
 <p align="center">
   <a href="https://github.com/gravitl/netmaker/releases">
   <a href="https://github.com/gravitl/netmaker/releases">
-    <img src="https://img.shields.io/badge/Version-0.18.4-informational?style=flat-square" />
+    <img src="https://img.shields.io/badge/Version-0.18.5-informational?style=flat-square" />
   </a>
   </a>
   <a href="https://hub.docker.com/r/gravitl/netmaker/tags">
   <a href="https://hub.docker.com/r/gravitl/netmaker/tags">
     <img src="https://img.shields.io/docker/pulls/gravitl/netmaker?label=downloads" />
     <img src="https://img.shields.io/docker/pulls/gravitl/netmaker?label=downloads" />

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

@@ -3,7 +3,7 @@ version: "3.4"
 services:
 services:
   netmaker:
   netmaker:
     container_name: netmaker
     container_name: netmaker
-    image: gravitl/netmaker:v0.18.4
+    image: gravitl/netmaker:v0.18.5
     restart: always
     restart: always
     volumes:
     volumes:
       - dnsconfig:/root/config/dnsconfig
       - dnsconfig:/root/config/dnsconfig

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

@@ -3,7 +3,7 @@ version: "3.4"
 services:
 services:
   netclient:
   netclient:
     container_name: netclient
     container_name: netclient
-    image: 'gravitl/netclient:v0.18.4'
+    image: 'gravitl/netclient:v0.18.5'
     hostname: netmaker-1
     hostname: netmaker-1
     network_mode: host
     network_mode: host
     restart: always
     restart: always

+ 1 - 1
controllers/docs.go

@@ -10,7 +10,7 @@
 //
 //
 //	Schemes: https
 //	Schemes: https
 //	BasePath: /
 //	BasePath: /
-//	Version: 0.18.4
+//	Version: 0.18.5
 //	Host: netmaker.io
 //	Host: netmaker.io
 //
 //
 //	Consumes:
 //	Consumes:

+ 17 - 0
controllers/enrollmentkeys.go

@@ -182,9 +182,21 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("invalid enrollment key"), "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("invalid enrollment key"), "badrequest"))
 		return
 		return
 	}
 	}
+	hostPass := newHost.HostPass
 	if !hostExists {
 	if !hostExists {
 		// register host
 		// register host
 		logic.CheckHostPorts(&newHost)
 		logic.CheckHostPorts(&newHost)
+		// create EMQX credentials and ACLs for host
+		if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
+			if err := mq.CreateEmqxUser(newHost.ID.String(), newHost.HostPass, false); err != nil {
+				logger.Log(0, "failed to create host credentials for EMQX: ", err.Error())
+				return
+			}
+			if err := mq.CreateHostACL(newHost.ID.String(), servercfg.GetServerInfo().Server); err != nil {
+				logger.Log(0, "failed to add host ACL rules to EMQX: ", err.Error())
+				return
+			}
+		}
 		if err = logic.CreateHost(&newHost); err != nil {
 		if err = logic.CreateHost(&newHost); err != nil {
 			logger.Log(0, "host", newHost.ID.String(), newHost.Name, "failed registration -", err.Error())
 			logger.Log(0, "host", newHost.ID.String(), newHost.Name, "failed registration -", err.Error())
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
@@ -205,6 +217,11 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
 	// ready the response
 	// ready the response
 	server := servercfg.GetServerInfo()
 	server := servercfg.GetServerInfo()
 	server.TrafficKey = key
 	server.TrafficKey = key
+	if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
+		// set MQ username and password for EMQX clients
+		server.MQUserName = newHost.ID.String()
+		server.MQPassword = hostPass
+	}
 	response := models.RegisterResponse{
 	response := models.RegisterResponse{
 		ServerConf:    server,
 		ServerConf:    server,
 		RequestedHost: newHost,
 		RequestedHost: newHost,

+ 1 - 1
controllers/ext_client.go

@@ -215,7 +215,7 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
 	if network.DefaultKeepalive != 0 {
 	if network.DefaultKeepalive != 0 {
 		keepalive = "PersistentKeepalive = " + strconv.Itoa(int(network.DefaultKeepalive))
 		keepalive = "PersistentKeepalive = " + strconv.Itoa(int(network.DefaultKeepalive))
 	}
 	}
-	gwendpoint := host.EndpointIP.String() + ":" + strconv.Itoa(logic.GetPeerListenPort(host))
+	gwendpoint := host.EndpointIP.String() + ":" + strconv.Itoa(host.ListenPort)
 	newAllowedIPs := network.AddressRange
 	newAllowedIPs := network.AddressRange
 	if newAllowedIPs != "" && network.AddressRange6 != "" {
 	if newAllowedIPs != "" && network.AddressRange6 != "" {
 		newAllowedIPs += ","
 		newAllowedIPs += ","

+ 49 - 0
controllers/hosts.go

@@ -1,6 +1,7 @@
 package controller
 package controller
 
 
 import (
 import (
+	"context"
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
@@ -26,6 +27,7 @@ func hostHandlers(r *mux.Router) {
 	r.HandleFunc("/api/hosts/{hostid}/relay", logic.SecurityCheck(false, http.HandlerFunc(createHostRelay))).Methods(http.MethodPost)
 	r.HandleFunc("/api/hosts/{hostid}/relay", logic.SecurityCheck(false, http.HandlerFunc(createHostRelay))).Methods(http.MethodPost)
 	r.HandleFunc("/api/hosts/{hostid}/relay", logic.SecurityCheck(false, http.HandlerFunc(deleteHostRelay))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/hosts/{hostid}/relay", logic.SecurityCheck(false, http.HandlerFunc(deleteHostRelay))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/hosts/adm/authenticate", authenticateHost).Methods(http.MethodPost)
 	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)
 }
 }
 
 
 // swagger:route GET /api/hosts hosts getHosts
 // swagger:route GET /api/hosts hosts getHosts
@@ -53,6 +55,53 @@ func getHosts(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(apiHosts)
 	json.NewEncoder(w).Encode(apiHosts)
 }
 }
 
 
+// swagger:route GET /api/v1/host pull pullHost
+//
+// Used by clients for "pull" command
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: pull
+func pull(w http.ResponseWriter, r *http.Request) {
+
+	hostID := r.Header.Get(hostIDHeader) // return JSON/API formatted keys
+	if len(hostID) == 0 {
+		logger.Log(0, "no host authorized to pull")
+		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("no host authorized to pull"), "internal"))
+		return
+	}
+	host, err := logic.GetHost(hostID)
+	if err != nil {
+		logger.Log(0, "no host found during pull", hostID)
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	hPU, err := logic.GetPeerUpdateForHost(context.Background(), "", host, nil, nil)
+	if err != nil {
+		logger.Log(0, "could not pull peers for host", hostID)
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	serverConf := servercfg.GetServerInfo()
+	if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
+		serverConf.MQUserName = hostID
+	}
+	response := models.HostPull{
+		Host:         *host,
+		ServerConfig: serverConf,
+		Peers:        hPU.Peers,
+		PeerIDs:      hPU.PeerIDs,
+	}
+
+	logger.Log(1, hostID, "completed a pull")
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(&response)
+}
+
 // swagger:route PUT /api/hosts/{hostid} hosts updateHost
 // swagger:route PUT /api/hosts/{hostid} hosts updateHost
 //
 //
 // Updates a Netclient host on Netmaker server.
 // Updates a Netclient host on Netmaker server.

+ 1 - 0
controllers/migrate.go

@@ -61,6 +61,7 @@ func migrate(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 	if !logic.HostExists(&data.NewHost) {
 	if !logic.HostExists(&data.NewHost) {
+		logic.CheckHostPorts(&data.NewHost)
 		if err = logic.CreateHost(&data.NewHost); err != nil {
 		if err = logic.CreateHost(&data.NewHost); err != nil {
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 			return
 			return

+ 12 - 5
controllers/node.go

@@ -19,6 +19,8 @@ import (
 	"golang.org/x/crypto/bcrypt"
 	"golang.org/x/crypto/bcrypt"
 )
 )
 
 
+var hostIDHeader = "host-id"
+
 func nodeHandlers(r *mux.Router) {
 func nodeHandlers(r *mux.Router) {
 
 
 	r.HandleFunc("/api/nodes", authorize(false, false, "user", http.HandlerFunc(getAllNodes))).Methods(http.MethodGet)
 	r.HandleFunc("/api/nodes", authorize(false, false, "user", http.HandlerFunc(getAllNodes))).Methods(http.MethodGet)
@@ -152,7 +154,7 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
 // even if it's technically ok
 // even if it's technically ok
 // This is kind of a poor man's RBAC. There's probably a better/smarter way.
 // This is kind of a poor man's RBAC. There's probably a better/smarter way.
 // TODO: Consider better RBAC implementations
 // TODO: Consider better RBAC implementations
-func authorize(nodesAllowed, networkCheck bool, authNetwork string, next http.Handler) http.HandlerFunc {
+func authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Handler) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 	return func(w http.ResponseWriter, r *http.Request) {
 		var errorResponse = models.ErrorResponse{
 		var errorResponse = models.ErrorResponse{
 			Code: http.StatusUnauthorized, Message: logic.Unauthorized_Msg,
 			Code: http.StatusUnauthorized, Message: logic.Unauthorized_Msg,
@@ -184,11 +186,11 @@ func authorize(nodesAllowed, networkCheck bool, authNetwork string, next http.Ha
 				logic.ReturnErrorResponse(w, r, errorResponse)
 				logic.ReturnErrorResponse(w, r, errorResponse)
 				return
 				return
 			}
 			}
-			//check if node instead of user
-			if nodesAllowed {
+			// check if host instead of user
+			if hostAllowed {
 				// TODO --- should ensure that node is only operating on itself
 				// TODO --- should ensure that node is only operating on itself
-				if _, _, _, err := logic.VerifyToken(authToken); err == nil {
-
+				if hostID, _, _, err := logic.VerifyHostToken(authToken); err == nil {
+					r.Header.Set(hostIDHeader, hostID)
 					// this indicates request is from a node
 					// this indicates request is from a node
 					// used for failover - if a getNode comes from node, this will trigger a metrics wipe
 					// used for failover - if a getNode comes from node, this will trigger a metrics wipe
 					next.ServeHTTP(w, r)
 					next.ServeHTTP(w, r)
@@ -244,6 +246,7 @@ func authorize(nodesAllowed, networkCheck bool, authNetwork string, next http.Ha
 					} else {
 					} else {
 						isAuthorized = (nodeID == params["netid"])
 						isAuthorized = (nodeID == params["netid"])
 					}
 					}
+				case "host":
 				case "user":
 				case "user":
 					isAuthorized = true
 					isAuthorized = true
 				default:
 				default:
@@ -394,6 +397,10 @@ func getNode(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 	server := servercfg.GetServerInfo()
 	server := servercfg.GetServerInfo()
+	if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
+		// set MQ username for EMQX clients
+		server.MQUserName = host.ID.String()
+	}
 	response := models.NodeGet{
 	response := models.NodeGet{
 		Node:         node,
 		Node:         node,
 		Host:         *host,
 		Host:         *host,

+ 6 - 7
controllers/relay.go

@@ -115,7 +115,7 @@ func createHostRelay(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 	relay.HostID = params["hostid"]
 	relay.HostID = params["hostid"]
-	relayHost, _, err := logic.CreateHostRelay(relay)
+	relayHost, relayedHosts, err := logic.CreateHostRelay(relay)
 	if err != nil {
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 		logger.Log(0, r.Header.Get("user"),
 			fmt.Sprintf("failed to create relay on host [%s]: %v", relay.HostID, err))
 			fmt.Sprintf("failed to create relay on host [%s]: %v", relay.HostID, err))
@@ -131,15 +131,14 @@ func createHostRelay(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 	logger.Log(1, r.Header.Get("user"), "created relay on host", relay.HostID)
 	logger.Log(1, r.Header.Get("user"), "created relay on host", relay.HostID)
 	go func(relayHostID string) {
 	go func(relayHostID string) {
-		relatedhosts := logic.GetRelatedHosts(relayHostID)
-		for _, relatedHost := range relatedhosts {
-			relatedHost.ProxyEnabled = true
-			logic.UpsertHost(&relatedHost)
+		for _, relayedHost := range relayedHosts {
+			relayedHost.ProxyEnabled = true
+			logic.UpsertHost(&relayedHost)
 			if err := mq.HostUpdate(&models.HostUpdate{
 			if err := mq.HostUpdate(&models.HostUpdate{
 				Action: models.UpdateHost,
 				Action: models.UpdateHost,
-				Host:   relatedHost,
+				Host:   relayedHost,
 			}); err != nil {
 			}); err != nil {
-				logger.Log(0, "failed to send host update: ", relatedHost.ID.String(), err.Error())
+				logger.Log(0, "failed to send host update: ", relayedHost.ID.String(), err.Error())
 			}
 			}
 		}
 		}
 		if err := mq.PublishPeerUpdate(); err != nil {
 		if err := mq.PublishPeerUpdate(); err != nil {

+ 1 - 1
docker/Caddyfile

@@ -1,6 +1,6 @@
 {
 {
         # ZeroSSL account
         # ZeroSSL account
-        acme_ca https://acme.zerossl.com/v2/DV90
+        # acme_ca https://acme.zerossl.com/v2/DV90
         email YOUR_EMAIL
         email YOUR_EMAIL
 }
 }
 
 

+ 1 - 1
k8s/client/netclient-daemonset.yaml

@@ -16,7 +16,7 @@ spec:
       hostNetwork: true
       hostNetwork: true
       containers:
       containers:
       - name: netclient
       - name: netclient
-        image: gravitl/netclient:v0.18.4
+        image: gravitl/netclient:v0.18.5
         env:
         env:
         - name: TOKEN
         - name: TOKEN
           value: "TOKEN_VALUE"
           value: "TOKEN_VALUE"

+ 1 - 1
k8s/client/netclient.yaml

@@ -28,7 +28,7 @@ spec:
       #           - "<node label value>"
       #           - "<node label value>"
       containers:
       containers:
       - name: netclient
       - name: netclient
-        image: gravitl/netclient:v0.18.4
+        image: gravitl/netclient:v0.18.5
         env:
         env:
         - name: TOKEN
         - name: TOKEN
           value: "TOKEN_VALUE"
           value: "TOKEN_VALUE"

+ 1 - 1
k8s/server/netmaker-server.yaml

@@ -79,7 +79,7 @@ spec:
           value: "Kubernetes"
           value: "Kubernetes"
         - name: VERBOSITY
         - name: VERBOSITY
           value: "3"
           value: "3"
-        image: gravitl/netmaker:v0.18.4
+        image: gravitl/netmaker:v0.18.5
         imagePullPolicy: Always
         imagePullPolicy: Always
         name: netmaker
         name: netmaker
         ports:
         ports:

+ 1 - 1
k8s/server/netmaker-ui.yaml

@@ -15,7 +15,7 @@ spec:
     spec:
     spec:
       containers:
       containers:
       - name: netmaker-ui
       - name: netmaker-ui
-        image: gravitl/netmaker-ui:v0.18.4
+        image: gravitl/netmaker-ui:v0.18.5
         ports:
         ports:
         - containerPort: 443
         - containerPort: 443
         env:
         env:

+ 17 - 0
logic/extpeers.go

@@ -194,6 +194,23 @@ func UpdateExtClient(newclientid string, network string, enabled bool, client *m
 	if err != nil {
 	if err != nil {
 		return client, err
 		return client, err
 	}
 	}
+	if newclientid != client.ClientID {
+		//name change only
+		client.ClientID = newclientid
+		client.LastModified = time.Now().Unix()
+		data, err := json.Marshal(&client)
+		if err != nil {
+			return nil, err
+		}
+		key, err := GetRecordKey(client.ClientID, client.Network)
+		if err != nil {
+			return nil, err
+		}
+		if err = database.Insert(key, string(data), database.EXT_CLIENT_TABLE_NAME); err != nil {
+			return client, err
+		}
+		return client, nil
+	}
 	client.ClientID = newclientid
 	client.ClientID = newclientid
 	client.Enabled = enabled
 	client.Enabled = enabled
 	SetClientACLs(client, newACLs)
 	SetClientACLs(client, newACLs)

+ 2 - 2
logic/jwts.go

@@ -129,8 +129,8 @@ func VerifyUserToken(tokenString string) (username string, networks []string, is
 	return "", nil, false, err
 	return "", nil, false, err
 }
 }
 
 
-// VerifyToken - [nodes] Only
-func VerifyToken(tokenString string) (hostID string, mac string, network string, err error) {
+// VerifyHostToken - [hosts] Only
+func VerifyHostToken(tokenString string) (hostID string, mac string, network string, err error) {
 	claims := &models.Claims{}
 	claims := &models.Claims{}
 
 
 	// this may be a stupid way of serving up a master key
 	// this may be a stupid way of serving up a master key

+ 10 - 0
logic/nodes.go

@@ -52,6 +52,16 @@ func GetNetworkNodesMemory(allNodes []models.Node, network string) []models.Node
 	return nodes
 	return nodes
 }
 }
 
 
+// UpdateNodeCheckin - updates the checkin time of a node
+func UpdateNodeCheckin(node *models.Node) error {
+	node.SetLastCheckIn()
+	data, err := json.Marshal(node)
+	if err != nil {
+		return err
+	}
+	return database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
+}
+
 // UpdateNode - takes a node and updates another node with it's values
 // UpdateNode - takes a node and updates another node with it's values
 func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
 func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
 	if newNode.Address.IP.String() != currentNode.Address.IP.String() {
 	if newNode.Address.IP.String() != currentNode.Address.IP.String() {

+ 1 - 1
logic/peers.go

@@ -221,7 +221,7 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
 				}
 				}
 				peerConfig.Endpoint = &net.UDPAddr{
 				peerConfig.Endpoint = &net.UDPAddr{
 					IP:   peerHost.EndpointIP,
 					IP:   peerHost.EndpointIP,
-					Port: GetPeerListenPort(peerHost),
+					Port: peerHost.ListenPort,
 				}
 				}
 
 
 				if uselocal {
 				if uselocal {

+ 1 - 1
main.go

@@ -28,7 +28,7 @@ import (
 	"github.com/gravitl/netmaker/turnserver"
 	"github.com/gravitl/netmaker/turnserver"
 )
 )
 
 
-var version = "v0.18.4"
+var version = "v0.18.5"
 
 
 // Start DB Connection and start API Request Handler
 // Start DB Connection and start API Request Handler
 func main() {
 func main() {

+ 2 - 0
models/host.go

@@ -96,6 +96,8 @@ const (
 	Acknowledgement = "ACK"
 	Acknowledgement = "ACK"
 	// RequestAck - request an ACK
 	// RequestAck - request an ACK
 	RequestAck = "REQ_ACK"
 	RequestAck = "REQ_ACK"
+	// CheckIn - update last check in times and public address and interfaces
+	CheckIn = "CHECK_IN"
 )
 )
 
 
 // HostUpdate - struct for host update
 // HostUpdate - struct for host update

+ 8 - 0
models/structs.go

@@ -198,6 +198,14 @@ type TrafficKeys struct {
 	Server []byte `json:"server" bson:"server" yaml:"server"`
 	Server []byte `json:"server" bson:"server" yaml:"server"`
 }
 }
 
 
+// HostPull - response of a host's pull
+type HostPull struct {
+	Host         Host                 `json:"host" yaml:"host"`
+	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"`
+}
+
 // NodeGet - struct for a single node get response
 // NodeGet - struct for a single node get response
 type NodeGet struct {
 type NodeGet struct {
 	Node         Node                 `json:"node" bson:"node" yaml:"node"`
 	Node         Node                 `json:"node" bson:"node" yaml:"node"`

+ 237 - 0
mq/emqx.go

@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"net/http"
 	"net/http"
+	"sync"
 
 
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
 )
 )
@@ -29,6 +30,17 @@ type (
 		Token   string `json:"token"`
 		Token   string `json:"token"`
 		Version string `json:"version"`
 		Version string `json:"version"`
 	}
 	}
+
+	aclRule struct {
+		Topic      string `json:"topic"`
+		Permission string `json:"permission"`
+		Action     string `json:"action"`
+	}
+
+	aclObject struct {
+		Rules    []aclRule `json:"rules"`
+		Username string    `json:"username,omitempty"`
+	}
 )
 )
 
 
 func getEmqxAuthToken() (string, error) {
 func getEmqxAuthToken() (string, error) {
@@ -152,3 +164,228 @@ func CreateEmqxDefaultAuthenticator() error {
 	}
 	}
 	return nil
 	return nil
 }
 }
+
+// CreateEmqxDefaultAuthorizer - creates a default ACL authorization mechanism based on the built in database
+func CreateEmqxDefaultAuthorizer() error {
+	token, err := getEmqxAuthToken()
+	if err != nil {
+		return err
+	}
+	payload, err := json.Marshal(&struct {
+		Enable bool   `json:"enable"`
+		Type   string `json:"type"`
+	}{Enable: true, Type: "built_in_database"})
+	if err != nil {
+		return err
+	}
+	req, err := http.NewRequest(http.MethodPost, servercfg.GetEmqxRestEndpoint()+"/api/v5/authorization/sources", bytes.NewReader(payload))
+	if err != nil {
+		return err
+	}
+	req.Header.Add("content-type", "application/json")
+	req.Header.Add("authorization", "Bearer "+token)
+	resp, err := (&http.Client{}).Do(req)
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode != http.StatusNoContent {
+		msg, err := io.ReadAll(resp.Body)
+		if err != nil {
+			return err
+		}
+		return fmt.Errorf("error creating default EMQX ACL authorization mechanism %v", string(msg))
+	}
+	return nil
+}
+
+// GetUserACL - returns ACL rules by username
+func GetUserACL(username string) (*aclObject, error) {
+	token, err := getEmqxAuthToken()
+	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)
+	if err != nil {
+		return nil, err
+	}
+	req.Header.Add("content-type", "application/json")
+	req.Header.Add("authorization", "Bearer "+token)
+	resp, err := (&http.Client{}).Do(req)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	response, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("error fetching ACL rules %v", string(response))
+	}
+	body := new(aclObject)
+	if err := json.Unmarshal(response, body); err != nil {
+		return nil, err
+	}
+	return body, nil
+}
+
+// CreateDefaultDenyRule - creates a rule to deny access to all topics for all users by default
+// to allow user access to topics use the `mq.CreateUserAccessRule` function
+func CreateDefaultDenyRule() error {
+	token, err := getEmqxAuthToken()
+	if err != nil {
+		return err
+	}
+	payload, err := json.Marshal(&aclObject{Rules: []aclRule{{Topic: "#", Permission: "deny", Action: "all"}}})
+	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))
+	if err != nil {
+		return err
+	}
+	req.Header.Add("content-type", "application/json")
+	req.Header.Add("authorization", "Bearer "+token)
+	resp, err := (&http.Client{}).Do(req)
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode != http.StatusNoContent {
+		msg, err := io.ReadAll(resp.Body)
+		if err != nil {
+			return err
+		}
+		return fmt.Errorf("error creating default ACL rules %v", string(msg))
+	}
+	return nil
+}
+
+// CreateHostACL - create host ACL rules
+func CreateHostACL(hostID, serverName string) error {
+	token, err := getEmqxAuthToken()
+	if err != nil {
+		return err
+	}
+	payload, err := json.Marshal(&aclObject{
+		Username: hostID,
+		Rules: []aclRule{
+			{
+				Topic:      fmt.Sprintf("peers/host/%s/%s", hostID, serverName),
+				Permission: "allow",
+				Action:     "all",
+			},
+			{
+				Topic:      fmt.Sprintf("host/update/%s/%s", hostID, serverName),
+				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",
+				Action:     "all",
+			},
+		},
+	})
+	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))
+	if err != nil {
+		return err
+	}
+	req.Header.Add("content-type", "application/json")
+	req.Header.Add("authorization", "Bearer "+token)
+	resp, err := (&http.Client{}).Do(req)
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode != http.StatusNoContent {
+		msg, err := io.ReadAll(resp.Body)
+		if err != nil {
+			return err
+		}
+		return fmt.Errorf("error adding ACL Rules for user %s Error: %v", hostID, string(msg))
+	}
+	return nil
+}
+
+// a lock required for preventing simultaneous updates to the same ACL object leading to overwriting each other
+// might occur when multiple nodes belonging to the same host are created at the same time
+var nodeAclMux sync.Mutex
+
+// AppendNodeUpdateACL - adds ACL rule for subscribing to node updates for a node ID
+func AppendNodeUpdateACL(hostID, nodeNetwork, nodeID, serverName string) error {
+	nodeAclMux.Lock()
+	defer nodeAclMux.Unlock()
+	token, err := getEmqxAuthToken()
+	if err != nil {
+		return err
+	}
+	aclObject, err := GetUserACL(hostID)
+	if err != nil {
+		return err
+	}
+	aclObject.Rules = append(aclObject.Rules, []aclRule{
+		{
+			Topic:      fmt.Sprintf("node/update/%s/%s", nodeNetwork, nodeID),
+			Permission: "allow",
+			Action:     "subscribe",
+		},
+		{
+			Topic:      fmt.Sprintf("ping/%s/%s", serverName, nodeID),
+			Permission: "allow",
+			Action:     "all",
+		},
+		{
+			Topic:      fmt.Sprintf("update/%s/%s", serverName, nodeID),
+			Permission: "allow",
+			Action:     "all",
+		},
+		{
+			Topic:      fmt.Sprintf("signal/%s/%s", serverName, nodeID),
+			Permission: "allow",
+			Action:     "all",
+		},
+		{
+			Topic:      fmt.Sprintf("metrics/%s/%s", serverName, nodeID),
+			Permission: "allow",
+			Action:     "all",
+		},
+	}...)
+	payload, err := json.Marshal(aclObject)
+	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))
+	if err != nil {
+		return err
+	}
+	req.Header.Add("content-type", "application/json")
+	req.Header.Add("authorization", "Bearer "+token)
+	resp, err := (&http.Client{}).Do(req)
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode != http.StatusNoContent {
+		msg, err := io.ReadAll(resp.Body)
+		if err != nil {
+			return err
+		}
+		return fmt.Errorf("error adding ACL Rules for user %s Error: %v", hostID, string(msg))
+	}
+	return nil
+}

+ 50 - 88
mq/handlers.go

@@ -23,94 +23,6 @@ func DefaultHandler(client mqtt.Client, msg mqtt.Message) {
 	logger.Log(0, "MQTT Message: Topic: ", string(msg.Topic()), " Message: ", string(msg.Payload()))
 	logger.Log(0, "MQTT Message: Topic: ", string(msg.Topic()), " Message: ", string(msg.Payload()))
 }
 }
 
 
-// Ping message Handler -- handles ping topic from client nodes
-func Ping(client mqtt.Client, msg mqtt.Message) {
-	id, err := getID(msg.Topic())
-	if err != nil {
-		logger.Log(0, "error getting node.ID sent on ping topic ")
-		return
-	}
-	node, err := logic.GetNodeByID(id)
-	if err != nil {
-		logger.Log(3, "mq-ping error getting node: ", err.Error())
-		node, err := logic.GetNodeByID(id)
-		if err != nil {
-			logger.Log(3, "mq-ping error getting node: ", err.Error())
-			if database.IsEmptyRecord(err) {
-				h := logic.GetHostByNodeID(id) // check if a host is still associated
-				if h != nil {                  // inform host that node should be removed
-					fakeNode := models.Node{}
-					fakeNode.ID, _ = uuid.Parse(id)
-					fakeNode.Action = models.NODE_DELETE
-					fakeNode.PendingDelete = true
-					if err := NodeUpdate(&fakeNode); err != nil {
-						logger.Log(0, "failed to inform host", h.Name, h.ID.String(), "to remove node", id, err.Error())
-					}
-				}
-			}
-			return
-		}
-		decrypted, decryptErr := decryptMsg(&node, msg.Payload())
-		if decryptErr != nil {
-			logger.Log(0, "error decrypting when updating node ", node.ID.String(), decryptErr.Error())
-			return
-		}
-		var checkin models.NodeCheckin
-		if err := json.Unmarshal(decrypted, &checkin); err != nil {
-			logger.Log(1, "error unmarshaling payload ", err.Error())
-			return
-		}
-		host, err := logic.GetHost(node.HostID.String())
-		if err != nil {
-			logger.Log(0, "error retrieving host for node ", node.ID.String(), err.Error())
-			return
-		}
-		node.SetLastCheckIn()
-		host.Version = checkin.Version
-		node.Connected = checkin.Connected
-		host.Interfaces = checkin.Ifaces
-		for i := range host.Interfaces {
-			host.Interfaces[i].AddressString = host.Interfaces[i].Address.String()
-		}
-		if err := logic.UpdateNode(&node, &node); err != nil {
-			logger.Log(0, "error updating node", node.ID.String(), " on checkin", err.Error())
-			return
-		}
-
-		return
-	}
-	decrypted, decryptErr := decryptMsg(&node, msg.Payload())
-	if decryptErr != nil {
-		logger.Log(0, "error decrypting when updating node ", node.ID.String(), decryptErr.Error())
-		return
-	}
-	var checkin models.NodeCheckin
-	if err := json.Unmarshal(decrypted, &checkin); err != nil {
-		logger.Log(1, "error unmarshaling payload ", err.Error())
-		return
-	}
-	host, err := logic.GetHost(node.HostID.String())
-	if err != nil {
-		logger.Log(0, "error retrieving host for node ", node.ID.String(), err.Error())
-		return
-	}
-	node.SetLastCheckIn()
-	host.Version = checkin.Version
-	node.Connected = checkin.Connected
-	host.Interfaces = checkin.Ifaces
-	for i := range host.Interfaces {
-		host.Interfaces[i].AddressString = host.Interfaces[i].Address.String()
-	}
-	if err := logic.UpdateNode(&node, &node); err != nil {
-		logger.Log(0, "error updating node", node.ID.String(), " on checkin", err.Error())
-		return
-	}
-
-	logger.Log(3, "ping processed for node", node.ID.String())
-	// --TODO --set client version once feature is implemented.
-	//node.SetClientVersion(msg.Payload())
-}
-
 // UpdateNode  message Handler -- handles updates from client nodes
 // UpdateNode  message Handler -- handles updates from client nodes
 func UpdateNode(client mqtt.Client, msg mqtt.Message) {
 func UpdateNode(client mqtt.Client, msg mqtt.Message) {
 	id, err := getID(msg.Topic())
 	id, err := getID(msg.Topic())
@@ -179,6 +91,8 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
 	logger.Log(3, fmt.Sprintf("recieved host update: %s\n", hostUpdate.Host.ID.String()))
 	logger.Log(3, fmt.Sprintf("recieved host update: %s\n", hostUpdate.Host.ID.String()))
 	var sendPeerUpdate bool
 	var sendPeerUpdate bool
 	switch hostUpdate.Action {
 	switch hostUpdate.Action {
+	case models.CheckIn:
+		sendPeerUpdate = handleHostCheckin(&hostUpdate.Host, currentHost)
 	case models.Acknowledgement:
 	case models.Acknowledgement:
 		hu := hostactions.GetAction(currentHost.ID.String())
 		hu := hostactions.GetAction(currentHost.ID.String())
 		if hu != nil {
 		if hu != nil {
@@ -186,6 +100,12 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
 				logger.Log(0, "failed to send new node to host", hostUpdate.Host.Name, currentHost.ID.String(), err.Error())
 				logger.Log(0, "failed to send new node to host", hostUpdate.Host.Name, currentHost.ID.String(), err.Error())
 				return
 				return
 			} else {
 			} else {
+				if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
+					if err = AppendNodeUpdateACL(hu.Host.ID.String(), hu.Node.Network, hu.Node.ID.String(), servercfg.GetServer()); err != nil {
+						logger.Log(0, "failed to add ACLs for EMQX node", err.Error())
+						return
+					}
+				}
 				if err = PublishSingleHostPeerUpdate(context.Background(), currentHost, nil, nil); err != nil {
 				if err = PublishSingleHostPeerUpdate(context.Background(), currentHost, nil, nil); err != nil {
 					logger.Log(0, "failed peers publish after join acknowledged", hostUpdate.Host.Name, currentHost.ID.String(), err.Error())
 					logger.Log(0, "failed peers publish after join acknowledged", hostUpdate.Host.Name, currentHost.ID.String(), err.Error())
 					return
 					return
@@ -447,3 +367,45 @@ func handleNewNodeDNS(host *models.Host, node *models.Node) error {
 	}
 	}
 	return nil
 	return nil
 }
 }
+
+func handleHostCheckin(h, currentHost *models.Host) bool {
+	if h == nil {
+		return false
+	}
+
+	for i := range currentHost.Nodes {
+		currNodeID := currentHost.Nodes[i]
+		node, err := logic.GetNodeByID(currNodeID)
+		if err != nil {
+			if database.IsEmptyRecord(err) {
+				fakeNode := models.Node{}
+				fakeNode.ID, _ = uuid.Parse(currNodeID)
+				fakeNode.Action = models.NODE_DELETE
+				fakeNode.PendingDelete = true
+				if err := NodeUpdate(&fakeNode); err != nil {
+					logger.Log(0, "failed to inform host", currentHost.Name, currentHost.ID.String(), "to remove node", currNodeID, err.Error())
+				}
+			}
+			continue
+		}
+		if err := logic.UpdateNodeCheckin(&node); err != nil {
+			logger.Log(0, "error updating node", node.ID.String(), " on checkin", err.Error())
+		}
+	}
+
+	for i := range h.Interfaces {
+		h.Interfaces[i].AddressString = h.Interfaces[i].Address.String()
+	}
+	ifaceDelta := len(h.Interfaces) != len(currentHost.Interfaces) || !h.EndpointIP.Equal(currentHost.EndpointIP)
+	currentHost.EndpointIP = h.EndpointIP
+	currentHost.Interfaces = h.Interfaces
+	currentHost.DefaultInterface = h.DefaultInterface
+	if err := logic.UpsertHost(currentHost); err != nil {
+		logger.Log(0, "failed to update host after check-in", h.Name, h.ID.String(), err.Error())
+		return false
+	}
+
+	logger.Log(0, "ping processed for host", h.Name, h.ID.String())
+	return ifaceDelta
+
+}

+ 8 - 4
mq/mq.go

@@ -50,15 +50,19 @@ func SetupMQTT() {
 		if err := CreateEmqxUser(servercfg.GetMqUserName(), servercfg.GetMqPassword(), true); err != nil {
 		if err := CreateEmqxUser(servercfg.GetMqUserName(), servercfg.GetMqPassword(), true); err != nil {
 			log.Fatal(err)
 			log.Fatal(err)
 		}
 		}
+		// create an ACL authorization source for the built in EMQX MNESIA database
+		if err := CreateEmqxDefaultAuthorizer(); err != nil {
+			logger.Log(0, err.Error())
+		}
+		// create a default deny ACL to all topics for all users
+		if err := CreateDefaultDenyRule(); err != nil {
+			log.Fatal(err)
+		}
 	}
 	}
 	opts := mqtt.NewClientOptions()
 	opts := mqtt.NewClientOptions()
 	setMqOptions(servercfg.GetMqUserName(), servercfg.GetMqPassword(), opts)
 	setMqOptions(servercfg.GetMqUserName(), servercfg.GetMqPassword(), opts)
 	opts.SetOnConnectHandler(func(client mqtt.Client) {
 	opts.SetOnConnectHandler(func(client mqtt.Client) {
 		serverName := servercfg.GetServer()
 		serverName := servercfg.GetServer()
-		if token := client.Subscribe(fmt.Sprintf("ping/%s/#", serverName), 2, mqtt.MessageHandler(Ping)); token.WaitTimeout(MQ_TIMEOUT*time.Second) && token.Error() != nil {
-			client.Disconnect(240)
-			logger.Log(0, "ping subscription failed")
-		}
 		if token := client.Subscribe(fmt.Sprintf("update/%s/#", serverName), 0, mqtt.MessageHandler(UpdateNode)); token.WaitTimeout(MQ_TIMEOUT*time.Second) && token.Error() != nil {
 		if token := client.Subscribe(fmt.Sprintf("update/%s/#", serverName), 0, mqtt.MessageHandler(UpdateNode)); token.WaitTimeout(MQ_TIMEOUT*time.Second) && token.Error() != nil {
 			client.Disconnect(240)
 			client.Disconnect(240)
 			logger.Log(0, "node update subscription failed")
 			logger.Log(0, "node update subscription failed")

+ 10 - 6
mq/publishers.go

@@ -88,15 +88,19 @@ func PublishSingleHostPeerUpdate(ctx context.Context, host *models.Host, deleted
 	if len(peerUpdate.Peers) == 0 { // no peers to send
 	if len(peerUpdate.Peers) == 0 { // no peers to send
 		return nil
 		return nil
 	}
 	}
-	if host.ProxyEnabled || host.ShouldUseTurn {
-		proxyUpdate, err := logic.GetProxyUpdateForHost(ctx, host)
-		if err != nil {
-			return err
-		}
+	proxyUpdate, err := logic.GetProxyUpdateForHost(ctx, host)
+	if err != nil {
+		return err
+	}
+	proxyUpdate.Server = servercfg.GetServer()
+	if host.ProxyEnabled {
 		proxyUpdate.Action = models.ProxyUpdate
 		proxyUpdate.Action = models.ProxyUpdate
-		peerUpdate.ProxyUpdate = proxyUpdate
+	} else {
+		proxyUpdate.Action = models.NoProxy
 	}
 	}
 
 
+	peerUpdate.ProxyUpdate = proxyUpdate
+
 	data, err := json.Marshal(&peerUpdate)
 	data, err := json.Marshal(&peerUpdate)
 	if err != nil {
 	if err != nil {
 		return err
 		return err

+ 1 - 1
release.md

@@ -1,4 +1,4 @@
-# Netmaker v0.18.4
+# Netmaker v0.18.5
 
 
 ## **Wait till out of pre-release to fully upgrade**
 ## **Wait till out of pre-release to fully upgrade**
 
 

+ 14 - 33
scripts/nm-quick.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 #!/bin/bash
 
 
-LATEST="v0.18.4"
+LATEST="v0.18.5"
 
 
 print_logo() {(
 print_logo() {(
 cat << "EOF"
 cat << "EOF"
@@ -43,7 +43,7 @@ usage () {
     echo "  -t      tag of build; if buildtype=version, tag=version. If builtype=branch or builtype=local, tag=branch"
     echo "  -t      tag of build; if buildtype=version, tag=version. If builtype=branch or builtype=local, tag=branch"
     echo "  -a      auto-build; skip prompts and use defaults, if none provided"
     echo "  -a      auto-build; skip prompts and use defaults, if none provided"
     echo "examples:"
     echo "examples:"
-	echo "          nm-quick.sh -e -b version -t v0.18.4"
+	echo "          nm-quick.sh -e -b version -t $LATEST"
 	echo "          nm-quick.sh -e -b local -t feature_v0.17.2_newfeature"	
 	echo "          nm-quick.sh -e -b local -t feature_v0.17.2_newfeature"	
 	echo "          nm-quick.sh -e -b branch -t develop"
 	echo "          nm-quick.sh -e -b branch -t develop"
     exit 1
     exit 1
@@ -178,21 +178,6 @@ install_yq() {
 # setup_netclient - adds netclient to docker-compose
 # setup_netclient - adds netclient to docker-compose
 setup_netclient() {
 setup_netclient() {
 
 
-
-	# yq ".services.netclient += {\"container_name\": \"netclient\"}" -i /root/docker-compose.yml
-	# yq ".services.netclient += {\"image\": \"gravitl/netclient:$IMAGE_TAG\"}" -i /root/docker-compose.yml
-	# yq ".services.netclient += {\"hostname\": \"netmaker-1\"}" -i /root/docker-compose.yml
-	# yq ".services.netclient += {\"network_mode\": \"host\"}" -i /root/docker-compose.yml
-	# yq ".services.netclient.depends_on += [\"netmaker\"]" -i /root/docker-compose.yml
-	# yq ".services.netclient += {\"restart\": \"always\"}" -i /root/docker-compose.yml
-	# yq ".services.netclient.environment += {\"TOKEN\": \"$TOKEN\"}" -i /root/docker-compose.yml
-	# yq ".services.netclient.volumes += [\"/etc/netclient:/etc/netclient\"]" -i /root/docker-compose.yml
-	# yq ".services.netclient.cap_add += [\"NET_ADMIN\"]" -i /root/docker-compose.yml
-	# yq ".services.netclient.cap_add += [\"NET_RAW\"]" -i /root/docker-compose.yml
-	# yq ".services.netclient.cap_add += [\"SYS_MODULE\"]" -i /root/docker-compose.yml
-
-	# docker-compose up -d
-
 	set +e
 	set +e
 	netclient uninstall
 	netclient uninstall
 	set -e
 	set -e
@@ -200,7 +185,7 @@ setup_netclient() {
 	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
 	chmod +x netclient
 	./netclient install
 	./netclient install
-	netclient join -t $TOKEN
+	netclient register -t $TOKEN
 
 
 	echo "waiting for client to become available"
 	echo "waiting for client to become available"
 	wait_seconds 10 
 	wait_seconds 10 
@@ -210,9 +195,9 @@ setup_netclient() {
 configure_netclient() {
 configure_netclient() {
 
 
 	NODE_ID=$(sudo cat /etc/netclient/nodes.yml | yq -r .netmaker.commonnode.id)
 	NODE_ID=$(sudo cat /etc/netclient/nodes.yml | yq -r .netmaker.commonnode.id)
-	echo "join complete. New node ID: $NODE_ID"
+	echo "register complete. New node ID: $NODE_ID"
 	HOST_ID=$(sudo cat /etc/netclient/netclient.yml | yq -r .host.id)
 	HOST_ID=$(sudo cat /etc/netclient/netclient.yml | yq -r .host.id)
-	echo "For first join, making host a default"
+	echo "making host a default"
 	echo "Host ID: $HOST_ID"
 	echo "Host ID: $HOST_ID"
 	# set as a default host
 	# set as a default host
 	set +e
 	set +e
@@ -225,11 +210,8 @@ configure_netclient() {
 # setup_nmctl - pulls nmctl and makes it executable
 # setup_nmctl - pulls nmctl and makes it executable
 setup_nmctl() {
 setup_nmctl() {
 
 
-	# DEV_TEMP - Temporary instructions for testing
-	wget -O /usr/bin/nmctl https://fileserver.netmaker.org/testing/nmctl
+	wget -O /usr/bin/nmctl https://github.com/gravitl/netmaker/releases/download/$LATEST/nmctl_linux_amd64
 
 
-	# RELEASE_REPLACE - Use this once release is ready
-	# wget https://github.com/gravitl/netmaker/releases/download/v0.17.1/nmctl
     chmod +x /usr/bin/nmctl
     chmod +x /usr/bin/nmctl
     echo "using server api.$NETMAKER_BASE_DOMAIN"
     echo "using server api.$NETMAKER_BASE_DOMAIN"
     echo "using master key $MASTER_KEY"
     echo "using master key $MASTER_KEY"
@@ -295,7 +277,7 @@ install_dependencies() {
 
 
 	OS=$(uname)
 	OS=$(uname)
 	if [ -f /etc/debian_version ]; then
 	if [ -f /etc/debian_version ]; then
-		dependencies="git wireguard wireguard-tools jq docker.io docker-compose"
+		dependencies="git wireguard wireguard-tools dnsutils jq docker.io docker-compose"
 		update_cmd='apt update'
 		update_cmd='apt update'
 		install_cmd='apt-get install -y'
 		install_cmd='apt-get install -y'
 	elif [ -f /etc/alpine-release ]; then
 	elif [ -f /etc/alpine-release ]; then
@@ -303,19 +285,19 @@ install_dependencies() {
 		update_cmd='apk update'
 		update_cmd='apk update'
 		install_cmd='apk --update add'
 		install_cmd='apk --update add'
 	elif [ -f /etc/centos-release ]; then
 	elif [ -f /etc/centos-release ]; then
-		dependencies="git wireguard jq docker.io docker-compose"
+		dependencies="git wireguard jq bind-utils docker.io docker-compose"
 		update_cmd='yum update'
 		update_cmd='yum update'
 		install_cmd='yum install -y'
 		install_cmd='yum install -y'
 	elif [ -f /etc/fedora-release ]; then
 	elif [ -f /etc/fedora-release ]; then
-		dependencies="git wireguard jq docker.io docker-compose"
+		dependencies="git wireguard bind-utils jq docker.io docker-compose"
 		update_cmd='dnf update'
 		update_cmd='dnf update'
 		install_cmd='dnf install -y'
 		install_cmd='dnf install -y'
 	elif [ -f /etc/redhat-release ]; then
 	elif [ -f /etc/redhat-release ]; then
-		dependencies="git wireguard jq docker.io docker-compose"
+		dependencies="git wireguard jq docker.io bind-utils docker-compose"
 		update_cmd='yum update'
 		update_cmd='yum update'
 		install_cmd='yum install -y'
 		install_cmd='yum install -y'
 	elif [ -f /etc/arch-release ]; then
 	elif [ -f /etc/arch-release ]; then
-			dependecies="git wireguard-tools jq docker.io docker-compose"
+			dependecies="git wireguard-tools dnsutils jq docker.io docker-compose"
 		update_cmd='pacman -Sy'
 		update_cmd='pacman -Sy'
 		install_cmd='pacman -S --noconfirm'
 		install_cmd='pacman -S --noconfirm'
 	elif [ "${OS}" = "FreeBSD" ]; then
 	elif [ "${OS}" = "FreeBSD" ]; then
@@ -660,11 +642,10 @@ setup_mesh() {
 
 
 	wait_seconds 5
 	wait_seconds 5
 
 
-	echo "Creating netmaker access key"
+	echo "Creating netmaker enrollment key"
 
 
-	nmctl keys create test1 99999 --name netmaker-key
-	tokenJson=$(nmctl keys create netmaker 2)
-	TOKEN=$(jq -r '.accessstring' <<< ${tokenJson})
+	tokenJson=$(nmctl enrollment_key create --unlimited --networks netmaker)
+	TOKEN=$(jq -r '.token' <<< ${tokenJson})
 
 
 	wait_seconds 3
 	wait_seconds 3
 
 

+ 141 - 83
scripts/nm-upgrade.sh

@@ -1,6 +1,9 @@
 #!/bin/bash
 #!/bin/bash
 
 
-LATEST="testing"
+LATEST="v0.18.5"
+INSTALL_PATH="/root"
+
+trap restore_old_netmaker_instructions
 
 
 # check_version - make sure current version is 0.17.1 before continuing
 # check_version - make sure current version is 0.17.1 before continuing
 check_version() {
 check_version() {
@@ -15,6 +18,51 @@ check_version() {
   fi
   fi
 }
 }
 
 
+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/wait.sh $INSTALL_PATH/netmaker_0.17.1_backup/wait.sh
+}
+
+backup_volumes() {
+  cp -r /var/lib/docker/volumes/root_caddy_conf/ /var/lib/docker/volumes/root_caddy_conf-backup/
+  cp -r /var/lib/docker/volumes/root_caddy_data/ /var/lib/docker/volumes/root_caddy_data-backup/
+  cp -r /var/lib/docker/volumes/root_dnsconfig/ /var/lib/docker/volumes/root_dnsconfig-backup/
+  cp -r /var/lib/docker/volumes/root_mosquitto_data/ /var/lib/docker/volumes/root_mosquitto_data-backup/
+  cp -r /var/lib/docker/volumes/root_mosquitto_logs/ /var/lib/docker/volumes/root_mosquitto_logs-backup/
+  cp -r /var/lib/docker/volumes/root_sqldata/ /var/lib/docker/volumes/root_sqldata-backup/
+}
+
+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 netmaker should be back to v0.17.1"
+}
+
+get_install_path() {
+  echo "-----------------------------------------------------"
+  echo "Is your docker-compose located in $INSTALL_PATH ?"
+  echo "-----------------------------------------------------"
+  select install_option in "yes" "no (enter manually)"; do
+    case $REPLY in
+      1)
+        echo "using $INSTALL_PATH for an installation path."
+      break
+        ;;      
+      2)
+        read -p "Enter path where your docker-compose is located: " install_path
+        SERVER_HTTP_HOST=$install_path
+        echo "using $INSTALL_PATH"
+        break
+        ;;
+      *) echo "invalid option $REPLY";;
+    esac
+  done
+}
+
 # wait_seconds - wait a number of seconds, print a log
 # wait_seconds - wait a number of seconds, print a log
 wait_seconds() {
 wait_seconds() {
   for ((a=1; a <= $1; a++))
   for ((a=1; a <= $1; a++))
@@ -40,23 +88,23 @@ confirm() {
 install_dependencies() {
 install_dependencies() {
   OS=$(uname)
   OS=$(uname)
   if [ -f /etc/debian_version ]; then
   if [ -f /etc/debian_version ]; then
-    dependencies="jq wireguard jq docker.io docker-compose"
+    dependencies="jq wireguard jq dnsutils docker-compose"
     update_cmd='apt update'
     update_cmd='apt update'
     install_cmd='apt install -y'
     install_cmd='apt install -y'
   elif [ -f /etc/centos-release ]; then
   elif [ -f /etc/centos-release ]; then
-    dependencies="wireguard jq docker.io docker-compose"
+    dependencies="wireguard jq bind-utils docker-compose"
     update_cmd='yum update'
     update_cmd='yum update'
     install_cmd='yum install -y'
     install_cmd='yum install -y'
   elif [ -f /etc/fedora-release ]; then
   elif [ -f /etc/fedora-release ]; then
-    dependencies="wireguard jq docker.io docker-compose"
+    dependencies="wireguard jq bind-utils docker-compose"
     update_cmd='dnf update'
     update_cmd='dnf update'
     install_cmd='dnf install -y'
     install_cmd='dnf install -y'
   elif [ -f /etc/redhat-release ]; then
   elif [ -f /etc/redhat-release ]; then
-    dependencies="wireguard jq docker.io docker-compose"
+    dependencies="wireguard jq bind-utils docker-compose"
     update_cmd='yum update'
     update_cmd='yum update'
     install_cmd='yum install -y'
     install_cmd='yum install -y'
   elif [ -f /etc/arch-release ]; then
   elif [ -f /etc/arch-release ]; then
-        dependecies="wireguard-tools jq docker.io docker-compose netclient"
+        dependencies="wireguard-tools jq dnsutils docker-compose netclient"
     update_cmd='pacman -Sy'
     update_cmd='pacman -Sy'
     install_cmd='pacman -S --noconfirm'
     install_cmd='pacman -S --noconfirm'
   else
   else
@@ -66,6 +114,14 @@ install_dependencies() {
 
 
   set -- $dependencies
   set -- $dependencies
 
 
+  if command -v docker >/dev/null 2>&1 ; then
+    echo "Docker found"
+    echo "version: $(docker version)"
+  else
+    echo "Docker not found. adding to dependencies"
+    $dependencies += " docker.io"
+  fi
+
   ${update_cmd}
   ${update_cmd}
 
 
   set +e
   set +e
@@ -264,17 +320,17 @@ collect_node_settings() {
 # setup_caddy - updates Caddy with new info
 # setup_caddy - updates Caddy with new info
 setup_caddy() {
 setup_caddy() {
 
 
-  echo "backing up Caddyfile to /root/Caddyfile.backup"
-  cp /root/Caddyfile /root/Caddyfile.backup
+  echo "backing up Caddyfile to ${INSTALL_PATH}/Caddyfile.backup"
+  cp $INSTALL_PATH/Caddyfile $INSTALL_PATH/Caddyfile.backup
 
 
   if grep -wq "acme.zerossl.com/v2/DV90" Caddyfile; then 
   if grep -wq "acme.zerossl.com/v2/DV90" Caddyfile; then 
       echo "zerossl already set, continuing" 
       echo "zerossl already set, continuing" 
   else 
   else 
     echo "editing Caddyfile"
     echo "editing Caddyfile"
-    sed -i '0,/email/{s~email~acme_ca https://acme.zerossl.com/v2/DV90\n\t&~}' /root/Caddyfile
+    sed -i '0,/email/{s~email~acme_ca https://acme.zerossl.com/v2/DV90\n\t&~}' $INSTALL_PATH/Caddyfile
   fi
   fi
 
 
-cat <<EOT >> /root/Caddyfile
+cat <<EOT >> $INSTALL_PATH/Caddyfile
 
 
 # STUN
 # STUN
 https://$STUN_DOMAIN {
 https://$STUN_DOMAIN {
@@ -334,69 +390,63 @@ set_compose() {
   set_mq_credentials
   set_mq_credentials
 
 
   echo "retrieving updated wait script and mosquitto conf"  
   echo "retrieving updated wait script and mosquitto conf"  
-  rm /root/wait.sh
-  rm /root/mosquitto.conf
+  rm $INSTALL_PATH/wait.sh
+  rm $INSTALL_PATH/mosquitto.conf
 
 
-  # DEV_TEMP
-  wget -O /root/wait.sh https://raw.githubusercontent.com/gravitl/netmaker/develop/docker/wait.sh 
-  # RELEASE_REPLACE - Use this once release is ready
-  # wget -O /root/wait.sh https://raw.githubusercontent.com/gravitl/netmaker/master/docker/wait.sh
+  wget -O $INSTALL_PATH/wait.sh https://raw.githubusercontent.com/gravitl/netmaker/master/docker/wait.sh
 
 
-  chmod +x /root/wait.sh
+  chmod +x $INSTALL_PATH/wait.sh
 
 
-  # DEV_TEMP
-  wget -O /root/mosquitto.conf https://raw.githubusercontent.com/gravitl/netmaker/develop/docker/mosquitto.conf
-  # RELEASE_REPLACE - Use this once release is ready
-  # wget -O /root/wait.sh https://raw.githubusercontent.com/gravitl/netmaker/master/docker/wait.sh
+  wget -O $INSTALL_PATH/mosquitto.conf https://raw.githubusercontent.com/gravitl/netmaker/master/docker/mosquitto.conf
 
 
-  chmod +x /root/mosquitto.conf
+  chmod +x $INSTALL_PATH/mosquitto.conf
 
 
   # DEV_TEMP
   # DEV_TEMP
-  sed -i "s/v0.17.1/$LATEST/g" /root/docker-compose.yml
+  sed -i "s/v0.17.1/$LATEST/g" $INSTALL_PATH/docker-compose.yml
 
 
   STUN_PORT=3478
   STUN_PORT=3478
 
 
   # RELEASE_REPLACE - Use this once release is ready
   # RELEASE_REPLACE - Use this once release is ready
-  #sed -i "s/v0.17.1/v0.18.4/g" /root/docker-compose.yml
-  yq ".services.netmaker.environment.SERVER_NAME = \"$SERVER_NAME\"" -i /root/docker-compose.yml
-  yq ".services.netmaker.environment += {\"BROKER_ENDPOINT\": \"wss://$BROKER_NAME\"}" -i /root/docker-compose.yml  
-  yq ".services.netmaker.environment += {\"SERVER_BROKER_ENDPOINT\": \"ws://mq:1883\"}" -i /root/docker-compose.yml  
-  yq ".services.netmaker.environment += {\"STUN_LIST\": \"$STUN_DOMAIN:$STUN_PORT,stun1.netmaker.io:3478,stun2.netmaker.io:3478,stun1.l.google.com:19302,stun2.l.google.com:19302\"}" -i /root/docker-compose.yml  
-  yq ".services.netmaker.environment += {\"MQ_PASSWORD\": \"$MQ_PASSWORD\"}" -i /root/docker-compose.yml  
-  yq ".services.netmaker.environment += {\"MQ_USERNAME\": \"$MQ_USERNAME\"}" -i /root/docker-compose.yml  
-  yq ".services.netmaker.environment += {\"STUN_PORT\": \"$STUN_PORT\"}" -i /root/docker-compose.yml  
-  yq ".services.netmaker.ports += \"3478:3478/udp\"" -i /root/docker-compose.yml
-
-  yq ".services.mq.environment += {\"MQ_PASSWORD\": \"$MQ_PASSWORD\"}" -i /root/docker-compose.yml  
-  yq ".services.mq.environment += {\"MQ_USERNAME\": \"$MQ_USERNAME\"}" -i /root/docker-compose.yml  
+  #sed -i "s/v0.17.1/v0.18.5/g" $INSTALL_PATH/docker-compose.yml
+  yq ".services.netmaker.environment.SERVER_NAME = \"$SERVER_NAME\"" -i $INSTALL_PATH/docker-compose.yml
+  yq ".services.netmaker.environment += {\"BROKER_ENDPOINT\": \"wss://$BROKER_NAME\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.netmaker.environment += {\"SERVER_BROKER_ENDPOINT\": \"ws://mq:1883\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.netmaker.environment += {\"STUN_LIST\": \"$STUN_DOMAIN:$STUN_PORT,stun1.netmaker.io:3478,stun2.netmaker.io:3478,stun1.l.google.com:19302,stun2.l.google.com:19302\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.netmaker.environment += {\"MQ_PASSWORD\": \"$MQ_PASSWORD\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.netmaker.environment += {\"MQ_USERNAME\": \"$MQ_USERNAME\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.netmaker.environment += {\"STUN_PORT\": \"$STUN_PORT\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.netmaker.ports += \"3478:3478/udp\"" -i $INSTALL_PATH/docker-compose.yml
+
+  yq ".services.mq.environment += {\"MQ_PASSWORD\": \"$MQ_PASSWORD\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.mq.environment += {\"MQ_USERNAME\": \"$MQ_USERNAME\"}" -i $INSTALL_PATH/docker-compose.yml  
 
 
   #remove unnecessary ports
   #remove unnecessary ports
-  yq eval 'del( .services.netmaker.ports[] | select(. == "51821*") )' -i /root/docker-compose.yml
-  yq eval 'del( .services.mq.ports[] | select(. == "8883*") )' -i /root/docker-compose.yml
-  yq eval 'del( .services.mq.ports[] | select(. == "1883*") )' -i /root/docker-compose.yml
-  yq eval 'del( .services.mq.expose[] | select(. == "8883*") )' -i /root/docker-compose.yml
-  yq eval 'del( .services.mq.expose[] | select(. == "1883*") )' -i /root/docker-compose.yml
+  yq eval 'del( .services.netmaker.ports[] | select(. == "51821*") )' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del( .services.mq.ports[] | select(. == "8883*") )' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del( .services.mq.ports[] | select(. == "1883*") )' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del( .services.mq.expose[] | select(. == "8883*") )' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del( .services.mq.expose[] | select(. == "1883*") )' -i $INSTALL_PATH/docker-compose.yml
 
 
   # delete unnecessary compose sections
   # delete unnecessary compose sections
-  yq eval 'del(.services.netmaker.cap_add)' -i /root/docker-compose.yml
-  yq eval 'del(.services.netmaker.sysctls)' -i /root/docker-compose.yml
-  yq eval 'del(.services.netmaker.environment.MQ_ADMIN_PASSWORD)' -i /root/docker-compose.yml
-  yq eval 'del(.services.netmaker.environment.MQ_HOST)' -i /root/docker-compose.yml
-  yq eval 'del(.services.netmaker.environment.MQ_PORT)' -i /root/docker-compose.yml
-  yq eval 'del(.services.netmaker.environment.MQ_SERVER_PORT)' -i /root/docker-compose.yml
-  yq eval 'del(.services.netmaker.environment.PORT_FORWARD_SERVICES)' -i /root/docker-compose.yml
-  yq eval 'del(.services.netmaker.environment.CLIENT_MODE)' -i /root/docker-compose.yml
-  yq eval 'del(.services.netmaker.environment.HOST_NETWORK)' -i /root/docker-compose.yml
-  yq eval 'del(.services.mq.environment.NETMAKER_SERVER_HOST)' -i /root/docker-compose.yml
-  yq eval 'del( .services.netmaker.volumes[] | select(. == "mosquitto_data*") )' -i /root/docker-compose.yml
-  yq eval 'del( .services.mq.volumes[] | select(. == "mosquitto_data*") )' -i /root/docker-compose.yml
-  yq eval 'del( .volumes.mosquitto_data )' -i /root/docker-compose.yml
+  yq eval 'del(.services.netmaker.cap_add)' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del(.services.netmaker.sysctls)' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del(.services.netmaker.environment.MQ_ADMIN_PASSWORD)' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del(.services.netmaker.environment.MQ_HOST)' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del(.services.netmaker.environment.MQ_PORT)' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del(.services.netmaker.environment.MQ_SERVER_PORT)' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del(.services.netmaker.environment.PORT_FORWARD_SERVICES)' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del(.services.netmaker.environment.CLIENT_MODE)' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del(.services.netmaker.environment.HOST_NETWORK)' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del(.services.mq.environment.NETMAKER_SERVER_HOST)' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del( .services.netmaker.volumes[] | select(. == "mosquitto_data*") )' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del( .services.mq.volumes[] | select(. == "mosquitto_data*") )' -i $INSTALL_PATH/docker-compose.yml
+  yq eval 'del( .volumes.mosquitto_data )' -i $INSTALL_PATH/docker-compose.yml
 
 
 }
 }
 
 
 # start_containers - run docker-compose up -d
 # start_containers - run docker-compose up -d
 start_containers() {
 start_containers() {
-  docker-compose -f /root/docker-compose.yml up -d
+  docker-compose -f $INSTALL_PATH/docker-compose.yml up -d
 }
 }
 
 
 # test_caddy - make sure caddy is working
 # test_caddy - make sure caddy is working
@@ -427,29 +477,28 @@ test_caddy() {
 # setup_netclient - adds netclient to docker-compose
 # setup_netclient - adds netclient to docker-compose
 setup_netclient() {
 setup_netclient() {
 
 
-  # yq ".services.netclient += {\"container_name\": \"netclient\"}" -i /root/docker-compose.yml
-  # yq ".services.netclient += {\"image\": \"gravitl/netclient:testing\"}" -i /root/docker-compose.yml
-  # yq ".services.netclient += {\"hostname\": \"netmaker-1\"}" -i /root/docker-compose.yml
-  # yq ".services.netclient += {\"network_mode\": \"host\"}" -i /root/docker-compose.yml
-  # yq ".services.netclient.depends_on += [\"netmaker\"]" -i /root/docker-compose.yml
-  # yq ".services.netclient += {\"restart\": \"always\"}" -i /root/docker-compose.yml
-  # yq ".services.netclient.environment += {\"TOKEN\": \"$KEY\"}" -i /root/docker-compose.yml
-  # yq ".services.netclient.volumes += [\"/etc/netclient:/etc/netclient\"]" -i /root/docker-compose.yml
-  # yq ".services.netclient.cap_add += [\"NET_ADMIN\"]" -i /root/docker-compose.yml
-  # yq ".services.netclient.cap_add += [\"NET_RAW\"]" -i /root/docker-compose.yml
-  # yq ".services.netclient.cap_add += [\"SYS_MODULE\"]" -i /root/docker-compose.yml
-
-  # docker-compose up -d
-
 	set +e
 	set +e
 	netclient uninstall
 	netclient uninstall
-	set -e
+	HAS_APT=false
+  set -e
+  if command -v apt >/dev/null; then
+    HAS_APT=true
+  fi
+  set +e
+
+  if  [ "$HAS_APT" = "true" ]; then
+    curl -sL 'https://apt.netmaker.org/gpg.key' | sudo tee /etc/apt/trusted.gpg.d/netclient.asc
+    curl -sL 'https://apt.netmaker.org/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/netclient.list
+    sudo apt update
+    sudo apt install netclient
+  else
+     wget -O /tmp/netclient https://github.com/gravitl/netclient/releases/download/$LATEST/netclient_linux_amd64 
 
 
-  wget -O /tmp/netclient https://fileserver.netmaker.org/$LATEST/netclient 
+	  chmod +x /tmp/netclient
+	  /tmp/netclient install
+  fi
 
 
-	chmod +x /tmp/netclient
-	/tmp/netclient install
-	netclient join -t $KEY
+	netclient register -t $KEY
 
 
 	echo "waiting for client to become available"
 	echo "waiting for client to become available"
 	wait_seconds 10 
 	wait_seconds 10 
@@ -459,11 +508,8 @@ setup_netclient() {
 # setup_nmctl - pulls nmctl and makes it executable
 # setup_nmctl - pulls nmctl and makes it executable
 setup_nmctl() {
 setup_nmctl() {
 
 
-  # DEV_TEMP - Temporary instructions for testing
-  wget https://fileserver.netmaker.org/testing/nmctl
- 
-  # RELEASE_REPLACE - Use this once release is ready
-  # wget https://github.com/gravitl/netmaker/releases/download/v0.17.1/nmctl
+    wget -O nmctl https://github.com/gravitl/netmaker/releases/download/$LATEST/nmctl_linux_amd64
+  
     chmod +x nmctl
     chmod +x nmctl
     echo "using server $SERVER_HTTP_HOST"
     echo "using server $SERVER_HTTP_HOST"
     echo "using master key $MASTER_KEY"
     echo "using master key $MASTER_KEY"
@@ -500,6 +546,11 @@ join_networks() {
               HAS_EGRESS="yes"
               HAS_EGRESS="yes"
               echo "          egress ranges: $(jq -r ".[$NUM].egressgatewayranges" ./nodejson.tmp | tr -d '[]\n"[:space:]')"
               echo "          egress ranges: $(jq -r ".[$NUM].egressgatewayranges" ./nodejson.tmp | tr -d '[]\n"[:space:]')"
               EGRESS_RANGES=$(jq -r ".[$NUM].egressgatewayranges" ./nodejson.tmp | tr -d '[]\n"[:space:]')
               EGRESS_RANGES=$(jq -r ".[$NUM].egressgatewayranges" ./nodejson.tmp | tr -d '[]\n"[:space:]')
+              EGRESS_RANGES=${EGRESS_RANGES//0.0.0.0\/0/0.0.0.0\/5,8.0.0.0\/7,11.0.0.0\/8,12.0.0.0\/6,16.0.0.0\/4,32.0.0.0\/3,64.0.0.0\/2,128.0.0.0\/3,160.0.0.0\/5,168.0.0.0\/6,172.0.0.0\/12,172.32.0.0\/11,172.64.0.0\/10,172.128.0.0\/9,173.0.0.0\/8,174.0.0.0\/7,176.0.0.0\/4,192.0.0.0\/9,192.128.0.0\/11,192.160.0.0\/13,192.169.0.0\/16,192.170.0.0\/15,192.172.0.0\/14,192.176.0.0\/12,192.192.0.0\/10,193.0.0.0\/8,194.0.0.0\/7,196.0.0.0\/6,200.0.0.0\/5,208.0.0.0\/4}
+              EGRESS_RANGES=${EGRESS_RANGES//0::\/0/}
+              EGRESS_RANGES=${EGRESS_RANGES//,,/,}
+              EGRESS_RANGES=`echo $EGRESS_RANGES | sed 's/,*$//g'`
+              EGRESS_RANGES=`echo $EGRESS_RANGES | sed 's/^,*//g'`
 
 
           fi
           fi
           echo "      is ingress: $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp)"
           echo "      is ingress: $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp)"
@@ -520,11 +571,11 @@ join_networks() {
           confirm
           confirm
 
 
           if [[ $NUM -eq 0 ]]; then 
           if [[ $NUM -eq 0 ]]; then 
-            echo "running command: ./nmctl keys create $NETWORK 1"
-            KEY_JSON=$(./nmctl keys create $NETWORK 1)          
-            KEY=$(echo $KEY_JSON | jq -r .accessstring)
+            echo "running command: ./nmctl enrollment_key create --uses 1 --networks $NETWORK"
+          	KEY_JSON=$(./nmctl enrollment_key create --uses 1 --networks $NETWORK)
+          	KEY=$(jq -r '.token' <<< ${KEY_JSON})
 
 
-            echo "join key created: $KEY"
+            echo "enrollment key created: $KEY"
 
 
             setup_netclient
             setup_netclient
           else
           else
@@ -603,6 +654,13 @@ fi
 
 
 set +e
 set +e
 
 
+#backup volumes and v0.17.1 configs in case of failure.
+backup_volumes
+backup_v17_files
+
+# get the installation path for docker-compose.yml and other config files
+get_install_path
+
 echo "...installing dependencies for script"
 echo "...installing dependencies for script"
 install_dependencies
 install_dependencies
 
 
@@ -624,7 +682,7 @@ echo "...retrieving current server node settings"
 collect_node_settings
 collect_node_settings
 
 
 echo "...backing up docker compose to docker-compose.yml.backup"
 echo "...backing up docker compose to docker-compose.yml.backup"
-cp /root/docker-compose.yml /root/docker-compose.yml.backup
+cp $INSTALL_PATH/docker-compose.yml $INSTALL_PATH/docker-compose.yml.backup
 
 
 echo "...setting Caddyfile values"
 echo "...setting Caddyfile values"
 setup_caddy
 setup_caddy

+ 1 - 1
swagger.yaml

@@ -704,7 +704,7 @@ info:
 
 
         API calls must be authenticated via a header of the format -H “Authorization: Bearer <YOUR_SECRET_KEY>” There are two methods to obtain YOUR_SECRET_KEY: 1. Using the masterkey. By default, this value is “secret key,” but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [Netmaker](https://docs.netmaker.org/index.html) documentation for more details. 2. Using a JWT received for a node. This can be retrieved by calling the /api/nodes/<network>/authenticate endpoint, as documented below.
         API calls must be authenticated via a header of the format -H “Authorization: Bearer <YOUR_SECRET_KEY>” There are two methods to obtain YOUR_SECRET_KEY: 1. Using the masterkey. By default, this value is “secret key,” but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [Netmaker](https://docs.netmaker.org/index.html) documentation for more details. 2. Using a JWT received for a node. This can be retrieved by calling the /api/nodes/<network>/authenticate endpoint, as documented below.
     title: Netmaker
     title: Netmaker
-    version: 0.18.4
+    version: 0.18.5
 paths:
 paths:
     /api/dns:
     /api/dns:
         get:
         get: