Browse Source

merge conflicts resolved

Abhishek Kondur 2 years ago
parent
commit
9e0038f39c

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

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

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

@@ -53,6 +53,6 @@ jobs:
     uses: gravitl/devops/.github/workflows/branchtest.yml@master
     with:
       tag: ${{ github.run_id }}-${{ github.run_attempt }}
-      network: terraform
+      network: netmaker
     secrets: inherit
     

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

@@ -11,6 +11,29 @@ jobs:
     runs-on: ubuntu-latest
     if: ${{ github.event.workflow_run.conclusion == 'success' }}
     steps:
+      - name: get logs
+        uses: dawidd6/action-download-artifact@v2
+        with:
+          run_id: ${{ github.event.workflow_run.id}}
+          if_no_artifact_found: warn
+      - name: discord success message
+        uses: appleboy/discord-action@master
+        with:
+          webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }}
+          webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
+          color: "#42f545"
+          username: "GitHub Bot"
+          message: " ${{ github.event.workflow_run.name }} was successful"
+          file: ./results/results.log
+      - name: discord server message
+        uses: appleboy/discord-action@master
+        with:
+          webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }}
+          webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
+          color: "#42f545"
+          username: "GitHub Bot"
+          message: "droplets from this workflow will be deleted in 15 min"
+          file: ./server/serverinfo.txt
       - name: delete droplets
         run: |
           sleep 15m
@@ -20,12 +43,44 @@ jobs:
             "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 }}
+          TAG: ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}
 
   on-failure:
     runs-on: ubuntu-latest
     if: ${{ github.event.workflow_run.conclusion == 'failure' }}
     steps:
+      - name: get logs
+        uses: dawidd6/action-download-artifact@v2
+        with:
+          run_id: ${{ github.event.workflow_run.id}}
+          if_no_artifact_found: warn
+      - name: discord failure message
+        uses: appleboy/discord-action@master
+        with:
+          webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }}
+          webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
+          color: "#42f545"
+          username: "GitHub Bot"
+          message: " ${{ github.event.workflow_run.name }} failed"
+          file: ./results/results.log
+      - name: discord server message
+        uses: appleboy/discord-action@master
+        with:
+          webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }}
+          webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
+          color: "#42f545"
+          username: "GitHub Bot"
+          message: "droplets from this workflow will be deleted in 6 hours"
+          file: ./server/serverinfo.txt
+      - name: discord error message
+        uses: appleboy/discord-action@master
+        with:
+          webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }}
+          webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
+          color: "#990000"
+          username: "GitHub Bot"
+          message: "errors from ${{ github.event.workflow_run.name }}"
+          file: ./results/errors.log
       - name: delete droplets
         run: |
           sleep 6h
@@ -35,4 +90,4 @@ jobs:
             "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 }}
+          TAG: ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}

+ 1 - 1
README.md

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

+ 4 - 3
compose/docker-compose-emqx.yml

@@ -3,8 +3,8 @@ version: "3.4"
 services:
   netmaker:
     container_name: netmaker
-    image: gravitl/netmaker:v0.18.6
-    restart: always
+    image: gravitl/netmaker:v0.18.7
+    restart: on-failure
     volumes:
       - dnsconfig:/root/config/dnsconfig
       - sqldata:/root/data
@@ -19,6 +19,7 @@ services:
       COREDNS_ADDR: "SERVER_PUBLIC_IP"
       DNS_MODE: "on"
       SERVER_HTTP_HOST: "api.NETMAKER_BASE_DOMAIN"
+      NETCLIENT_AUTO_UPDATE: "enabled"
       API_PORT: "8081"
       MASTER_KEY: "REPLACE_MASTER_KEY"
       CORS_ALLOWED_ORIGIN: "*"
@@ -35,7 +36,7 @@ services:
       - "3478:3478/udp"
   netmaker-ui:
     container_name: netmaker-ui
-    image: gravitl/netmaker-ui:v0.18.6
+    image: gravitl/netmaker-ui:v0.18.7
     depends_on:
       - netmaker
     links:

+ 2 - 1
compose/docker-compose.ee.yml

@@ -4,7 +4,7 @@ services:
   netmaker:
     container_name: netmaker
     image: gravitl/netmaker:REPLACE_SERVER_IMAGE_TAG
-    restart: always
+    restart: on-failure
     volumes:
       - dnsconfig:/root/config/dnsconfig
       - sqldata:/root/data
@@ -17,6 +17,7 @@ services:
       COREDNS_ADDR: "SERVER_PUBLIC_IP"
       DNS_MODE: "on"
       SERVER_HTTP_HOST: "api.NETMAKER_BASE_DOMAIN"
+      NETCLIENT_AUTO_UPDATE: "enabled"
       API_PORT: "8081"
       MASTER_KEY: "REPLACE_MASTER_KEY"
       CORS_ALLOWED_ORIGIN: "*"

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

@@ -3,10 +3,10 @@ version: "3.4"
 services:
   netclient:
     container_name: netclient
-    image: 'gravitl/netclient:v0.18.6'
+    image: 'gravitl/netclient:v0.18.7'
     hostname: netmaker-1
     network_mode: host
-    restart: always
+    restart: on-failure
     environment:
       TOKEN: "TOKEN_VALUE"
     volumes:

+ 2 - 1
compose/docker-compose.reference.yml

@@ -4,7 +4,7 @@ services:
   netmaker: # The Primary Server for running Netmaker
     container_name: netmaker
     image: gravitl/netmaker:REPLACE_SERVER_IMAGE_TAG
-    restart: always
+    restart: on-failure
     volumes: # Volume mounts necessary for sql, coredns, and mqtt
       - dnsconfig:/root/config/dnsconfig
       - sqldata:/root/data
@@ -14,6 +14,7 @@ services:
       SERVER_NAME: "NETMAKER_BASE_DOMAIN" # The base domain of netmaker
       SERVER_HOST: "SERVER_PUBLIC_IP" # Set to public IP of machine.
       SERVER_HTTP_HOST: "api.NETMAKER_BASE_DOMAIN" # Overrides SERVER_HOST if set. Useful for making HTTP available via different interfaces/networks.
+      NETCLIENT_AUTO_UPDATE: "enabled" # Enable auto update of netclient ? ENUM:- enabled,disabled | default: enabled
       SERVER_API_CONN_STRING: "api.NETMAKER_BASE_DOMAIN:443"
       COREDNS_ADDR: "SERVER_PUBLIC_IP" # Address of the CoreDNS server. Defaults to SERVER_HOST
       DNS_MODE: "on" # Enables DNS Mode, meaning all nodes will set hosts file for private dns settings.

+ 2 - 1
compose/docker-compose.yml

@@ -4,7 +4,7 @@ services:
   netmaker:
     container_name: netmaker
     image: gravitl/netmaker:REPLACE_SERVER_IMAGE_TAG
-    restart: always
+    restart: on-failure
     volumes:
       - dnsconfig:/root/config/dnsconfig
       - sqldata:/root/data
@@ -17,6 +17,7 @@ services:
       COREDNS_ADDR: "SERVER_PUBLIC_IP"
       DNS_MODE: "on"
       SERVER_HTTP_HOST: "api.NETMAKER_BASE_DOMAIN"
+      NETCLIENT_AUTO_UPDATE: "enabled"
       API_PORT: "8081"
       MASTER_KEY: "REPLACE_MASTER_KEY"
       CORS_ALLOWED_ORIGIN: "*"

+ 1 - 0
config/config.go

@@ -40,6 +40,7 @@ type ServerConfig struct {
 	ServerBrokerEndpoint string    `yaml:"serverbrokerendpoint"`
 	BrokerType           string    `yaml:"brokertype"`
 	EmqxRestEndpoint     string    `yaml:"emqxrestendpoint"`
+	NetclientAutoUpdate  string    `yaml:"netclientautoupdate"`
 	MasterKey            string    `yaml:"masterkey"`
 	DNSKey               string    `yaml:"dnskey"`
 	AllowedOrigin        string    `yaml:"allowedorigin"`

+ 1 - 0
controllers/dns.go

@@ -70,6 +70,7 @@ func getAllDNS(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
+	logic.SortDNSEntrys(dns[:])
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(dns)
 }

+ 1 - 1
controllers/docs.go

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

+ 1 - 0
controllers/ext_client.go

@@ -117,6 +117,7 @@ func getAllExtClients(w http.ResponseWriter, r *http.Request) {
 	}
 
 	//Return all the extclients in JSON format
+	logic.SortExtClient(clients[:])
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(clients)
 }

+ 77 - 0
controllers/hosts.go

@@ -20,6 +20,8 @@ import (
 
 func hostHandlers(r *mux.Router) {
 	r.HandleFunc("/api/hosts", logic.SecurityCheck(true, http.HandlerFunc(getHosts))).Methods(http.MethodGet)
+	r.HandleFunc("/api/hosts/keys", logic.SecurityCheck(true, http.HandlerFunc(updateAllKeys))).Methods(http.MethodPut)
+	r.HandleFunc("/api/hosts/{hostid}/keys", logic.SecurityCheck(true, http.HandlerFunc(updateKeys))).Methods(http.MethodPut)
 	r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(updateHost))).Methods(http.MethodPut)
 	r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(deleteHost))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(addHostToNetwork))).Methods(http.MethodPost)
@@ -53,6 +55,7 @@ func getHosts(w http.ResponseWriter, r *http.Request) {
 	// return JSON/API formatted hosts
 	apiHosts := logic.GetAllHostsAPI(currentHosts[:])
 	logger.Log(2, r.Header.Get("user"), "fetched all hosts")
+	logic.SortApiHosts(apiHosts[:])
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(apiHosts)
 }
@@ -448,9 +451,15 @@ func authenticateHost(response http.ResponseWriter, request *http.Request) {
 	response.Write(successJSONResponse)
 }
 
+<<<<<<< HEAD
 // swagger:route POST /api/hosts/{hostid}/signalpeer signalPeer
 //
 // send signal to peer.
+=======
+// swagger:route POST /api/hosts/keys host updateAllKeys
+//
+// Update keys for a network.
+>>>>>>> 6abbf075e061ffe58c35b3e75493ba5ffa175022
 //
 //			Schemes: https
 //
@@ -458,6 +467,7 @@ func authenticateHost(response http.ResponseWriter, request *http.Request) {
 //	  		oauth
 //
 //			Responses:
+<<<<<<< HEAD
 //				200: nodeResponse
 func signalPeer(w http.ResponseWriter, r *http.Request) {
 	var params = mux.Vars(r)
@@ -512,4 +522,71 @@ func signalPeer(w http.ResponseWriter, r *http.Request) {
 	}
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(signal)
+=======
+//				200: networkBodyResponse
+func updateAllKeys(w http.ResponseWriter, r *http.Request) {
+	var errorResponse = models.ErrorResponse{}
+	w.Header().Set("Content-Type", "application/json")
+	hosts, err := logic.GetAllHosts()
+	if err != nil {
+		errorResponse.Code = http.StatusBadRequest
+		errorResponse.Message = err.Error()
+		logger.Log(0, r.Header.Get("user"),
+			"error retrieving hosts ", err.Error())
+		logic.ReturnErrorResponse(w, r, errorResponse)
+		return
+	}
+	go func() {
+		hostUpdate := models.HostUpdate{}
+		hostUpdate.Action = models.UpdateKeys
+		for _, host := range hosts {
+			hostUpdate.Host = host
+			logger.Log(2, "updating host", host.ID.String(), " for a key update")
+			if err = mq.HostUpdate(&hostUpdate); err != nil {
+				logger.Log(0, "failed to send update to node during a network wide key update", host.ID.String(), err.Error())
+			}
+		}
+	}()
+	logger.Log(2, r.Header.Get("user"), "updated keys for all hosts")
+	w.WriteHeader(http.StatusOK)
+}
+
+// swagger:route POST /api/hosts/{hostid}keys host updateKeys
+//
+// Update keys for a network.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: networkBodyResponse
+func updateKeys(w http.ResponseWriter, r *http.Request) {
+	var errorResponse = models.ErrorResponse{}
+	w.Header().Set("Content-Type", "application/json")
+	var params = mux.Vars(r)
+	hostid := params["hostid"]
+	host, err := logic.GetHost(hostid)
+	if err != nil {
+		logger.Log(0, "failed to retrieve host", hostid, err.Error())
+		errorResponse.Code = http.StatusBadRequest
+		errorResponse.Message = err.Error()
+		logger.Log(0, r.Header.Get("user"),
+			"error retrieving hosts ", err.Error())
+		logic.ReturnErrorResponse(w, r, errorResponse)
+		return
+	}
+	go func() {
+		hostUpdate := models.HostUpdate{
+			Action: models.UpdateKeys,
+			Host:   *host,
+		}
+		if err = mq.HostUpdate(&hostUpdate); err != nil {
+			logger.Log(0, "failed to send host key update", host.ID.String(), err.Error())
+		}
+	}()
+	logger.Log(2, r.Header.Get("user"), "updated key on host", host.Name)
+	w.WriteHeader(http.StatusOK)
+>>>>>>> 6abbf075e061ffe58c35b3e75493ba5ffa175022
 }

+ 1 - 39
controllers/network.go

@@ -23,7 +23,6 @@ func networkHandlers(r *mux.Router) {
 	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(true, http.HandlerFunc(deleteNetwork))).Methods(http.MethodDelete)
-	r.HandleFunc("/api/networks/{networkname}/keyupdate", logic.SecurityCheck(true, http.HandlerFunc(keyUpdate))).Methods(http.MethodPost)
 	// ACLs
 	r.HandleFunc("/api/networks/{networkname}/acls", logic.SecurityCheck(true, http.HandlerFunc(updateNetworkACL))).Methods(http.MethodPut)
 	r.HandleFunc("/api/networks/{networkname}/acls", logic.SecurityCheck(true, http.HandlerFunc(getNetworkACL))).Methods(http.MethodGet)
@@ -70,6 +69,7 @@ func getNetworks(w http.ResponseWriter, r *http.Request) {
 	}
 
 	logger.Log(2, r.Header.Get("user"), "fetched networks.")
+	logic.SortNetworks(allnetworks[:])
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(allnetworks)
 }
@@ -103,44 +103,6 @@ func getNetwork(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(network)
 }
 
-// swagger:route POST /api/networks/{networkname}/keyupdate networks keyUpdate
-//
-// Update keys for a network.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: networkBodyResponse
-func keyUpdate(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-	var params = mux.Vars(r)
-	netname := params["networkname"]
-	network, err := logic.KeyUpdate(netname)
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"), fmt.Sprintf("failed to update keys for network [%s]: %v",
-			netname, err))
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	logger.Log(2, r.Header.Get("user"), "updated key on network", netname)
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(network)
-	nodes, err := logic.GetNetworkNodes(netname)
-	if err != nil {
-		logger.Log(0, "failed to retrieve network nodes for network", netname, err.Error())
-		return
-	}
-	for _, node := range nodes {
-		logger.Log(2, "updating node ", node.ID.String(), " for a key update")
-		if err = mq.NodeUpdate(&node); err != nil {
-			logger.Log(1, "failed to send update to node during a network wide key update", node.ID.String(), err.Error())
-		}
-	}
-}
-
 // swagger:route PUT /api/networks/{networkname}/acls networks updateNetworkACL
 //
 // Update a network ACL (Access Control List).

+ 1 - 0
controllers/node.go

@@ -340,6 +340,7 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
 	// return all the nodes in JSON/API format
 	apiNodes := logic.GetAllNodesAPI(nodes[:])
 	logger.Log(3, r.Header.Get("user"), "fetched all nodes they have access to")
+	logic.SortApiNodes(apiNodes[:])
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(apiNodes)
 }

+ 1 - 0
controllers/user.go

@@ -186,6 +186,7 @@ func getUsers(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	logic.SortUsers(users[:])
 	logger.Log(2, r.Header.Get("user"), "fetched users")
 	json.NewEncoder(w).Encode(users)
 }

+ 9 - 9
ee/license.go

@@ -44,17 +44,17 @@ func ValidateLicense() error {
 	netmakerAccountID := servercfg.GetNetmakerAccountID()
 	logger.Log(0, "proceeding with Netmaker license validation...")
 	if len(licenseKeyValue) == 0 || len(netmakerAccountID) == 0 {
-		logger.FatalLog(errValidation.Error())
+		logger.FatalLog0(errValidation.Error())
 	}
 
 	apiPublicKey, err := getLicensePublicKey(licenseKeyValue)
 	if err != nil {
-		logger.FatalLog(errValidation.Error())
+		logger.FatalLog0(errValidation.Error())
 	}
 
 	tempPubKey, tempPrivKey, err := FetchApiServerKeys()
 	if err != nil {
-		logger.FatalLog(errValidation.Error())
+		logger.FatalLog0(errValidation.Error())
 	}
 
 	licenseSecret := LicenseSecret{
@@ -64,32 +64,32 @@ func ValidateLicense() error {
 
 	secretData, err := json.Marshal(&licenseSecret)
 	if err != nil {
-		logger.FatalLog(errValidation.Error())
+		logger.FatalLog0(errValidation.Error())
 	}
 
 	encryptedData, err := ncutils.BoxEncrypt(secretData, apiPublicKey, tempPrivKey)
 	if err != nil {
-		logger.FatalLog(errValidation.Error())
+		logger.FatalLog0(errValidation.Error())
 	}
 
 	validationResponse, err := validateLicenseKey(encryptedData, tempPubKey)
 	if err != nil || len(validationResponse) == 0 {
-		logger.FatalLog(errValidation.Error())
+		logger.FatalLog0(errValidation.Error())
 	}
 
 	var licenseResponse ValidatedLicense
 	if err = json.Unmarshal(validationResponse, &licenseResponse); err != nil {
-		logger.FatalLog(errValidation.Error())
+		logger.FatalLog0(errValidation.Error())
 	}
 
 	respData, err := ncutils.BoxDecrypt(base64decode(licenseResponse.EncryptedLicense), apiPublicKey, tempPrivKey)
 	if err != nil {
-		logger.FatalLog(errValidation.Error())
+		logger.FatalLog0(errValidation.Error())
 	}
 
 	license := LicenseKey{}
 	if err = json.Unmarshal(respData, &license); err != nil {
-		logger.FatalLog(errValidation.Error())
+		logger.FatalLog0(errValidation.Error())
 	}
 
 	Limits.Networks = math.MaxInt

+ 1 - 1
go.mod

@@ -9,7 +9,7 @@ require (
 	github.com/google/uuid v1.3.0
 	github.com/gorilla/handlers v1.5.1
 	github.com/gorilla/mux v1.8.0
-	github.com/lib/pq v1.10.7
+	github.com/lib/pq v1.10.8
 	github.com/mattn/go-sqlite3 v1.14.16
 	github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e

+ 2 - 2
go.sum

@@ -96,8 +96,8 @@ 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.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/lib/pq v1.10.8 h1:3fdt97i/cwSU83+E0hZTC/Xpc9mTZxc6UWSCRcSbxiE=
+github.com/lib/pq v1.10.8/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
 github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=

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

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

+ 1 - 1
k8s/client/netclient.yaml

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

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

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

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

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

+ 6 - 0
logger/logger.go

@@ -138,6 +138,12 @@ func FatalLog(message ...string) {
 	os.Exit(2)
 }
 
+// FatalLog0 - exits os after logging
+func FatalLog0(message ...string) {
+	fmt.Printf("[%s] Fatal: %s \n", program, MakeString(" ", message...))
+	os.Exit(0)
+}
+
 // == private ==
 
 // resetLogs - reallocates logs map

+ 12 - 1
logic/clients.go

@@ -1,6 +1,10 @@
 package logic
 
-import "github.com/gravitl/netmaker/models"
+import (
+	"sort"
+
+	"github.com/gravitl/netmaker/models"
+)
 
 // functions defined here, handle client ACLs, should be set on ee
 
@@ -51,3 +55,10 @@ func IsClientNodeAllowedByID(clientID, networkName, clientOrNodeID string) bool
 	}
 	return IsClientNodeAllowed(&client, clientOrNodeID)
 }
+
+// SortExtClient - Sorts slice of ExtClients by their ClientID alphabetically with numbers first
+func SortExtClient(unsortedExtClient []models.ExtClient) {
+	sort.Slice(unsortedExtClient, func(i, j int) bool {
+		return unsortedExtClient[i].ClientID < unsortedExtClient[j].ClientID
+	})
+}

+ 8 - 0
logic/dns.go

@@ -3,6 +3,7 @@ package logic
 import (
 	"encoding/json"
 	"os"
+	"sort"
 
 	validator "github.com/go-playground/validator/v10"
 	"github.com/gravitl/netmaker/database"
@@ -194,6 +195,13 @@ func GetDNSEntryNum(domain string, network string) (int, error) {
 	return num, nil
 }
 
+// SortDNSEntrys - Sorts slice of DNSEnteys by their Address alphabetically with numbers first
+func SortDNSEntrys(unsortedDNSEntrys []models.DNSEntry) {
+	sort.Slice(unsortedDNSEntrys, func(i, j int) bool {
+		return unsortedDNSEntrys[i].Address < unsortedDNSEntrys[j].Address
+	})
+}
+
 // ValidateDNSCreate - checks if an entry is valid
 func ValidateDNSCreate(entry models.DNSEntry) error {
 

+ 13 - 0
logic/hosts.go

@@ -8,6 +8,7 @@ import (
 	"fmt"
 	"log"
 	"net/http"
+	"sort"
 	"strconv"
 
 	"github.com/devilcove/httpclient"
@@ -106,6 +107,7 @@ func CreateHost(h *models.Host) error {
 		return err
 	}
 	h.HostPass = string(hash)
+	h.AutoUpdate = servercfg.AutoUpdateEnabled()
 	// if another server has already updated proxyenabled, leave it alone
 	if !h.ProxyEnabledSet {
 		log.Println("checking default proxy", servercfg.GetServerConfig().DefaultProxyMode)
@@ -160,6 +162,10 @@ func UpdateHost(newHost, currentHost *models.Host) {
 // UpdateHostFromClient - used for updating host on server with update recieved from client
 func UpdateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool) {
 
+	if newHost.PublicKey != currHost.PublicKey {
+		currHost.PublicKey = newHost.PublicKey
+		sendPeerUpdate = true
+	}
 	if newHost.ListenPort != 0 && currHost.ListenPort != newHost.ListenPort {
 		currHost.ListenPort = newHost.ListenPort
 		sendPeerUpdate = true
@@ -490,3 +496,10 @@ func DeRegisterHostWithTurn(hostID string) error {
 	}
 	return nil
 }
+
+// SortApiHosts - Sorts slice of ApiHosts by their ID alphabetically with numbers first
+func SortApiHosts(unsortedHosts []models.ApiHost) {
+	sort.Slice(unsortedHosts, func(i, j int) bool {
+		return unsortedHosts[i].ID < unsortedHosts[j].ID
+	})
+}

+ 8 - 44
logic/networks.go

@@ -5,6 +5,7 @@ import (
 	"errors"
 	"fmt"
 	"net"
+	"sort"
 	"strings"
 
 	"github.com/c-robinson/iplib"
@@ -554,15 +555,6 @@ func ParseNetwork(value string) (models.Network, error) {
 	return network, err
 }
 
-// KeyUpdate - updates keys on network
-func KeyUpdate(netname string) (models.Network, error) {
-	err := networkNodesUpdateAction(netname, models.NODE_UPDATE_KEY)
-	if err != nil {
-		return models.Network{}, err
-	}
-	return models.Network{}, nil
-}
-
 // SaveNetwork - save network struct to database
 func SaveNetwork(network *models.Network) error {
 	data, err := json.Marshal(network)
@@ -586,39 +578,11 @@ func NetworkExists(name string) (bool, error) {
 	return len(network) > 0, nil
 }
 
-// == Private ==
-
-func networkNodesUpdateAction(networkName string, action string) error {
-
-	collections, err := database.FetchRecords(database.NODES_TABLE_NAME)
-	if err != nil {
-		if database.IsEmptyRecord(err) {
-			return nil
-		}
-		return err
-	}
-
-	for k, value := range collections {
-		var node models.Node
-		err := json.Unmarshal([]byte(value), &node)
-		if err != nil {
-			if IsLegacyNode(k) { // ignore legacy nodes
-				continue
-			}
-			fmt.Println("error in node address assignment!")
-			return err
-		}
-		if action == models.NODE_UPDATE_KEY {
-			continue
-		}
-		if node.Network == networkName {
-			node.Action = action
-			data, err := json.Marshal(&node)
-			if err != nil {
-				return err
-			}
-			database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
-		}
-	}
-	return nil
+// SortNetworks - Sorts slice of Networks by their NetID alphabetically with numbers first
+func SortNetworks(unsortedNetworks []models.Network) {
+	sort.Slice(unsortedNetworks, func(i, j int) bool {
+		return unsortedNetworks[i].NetID < unsortedNetworks[j].NetID
+	})
 }
+
+// == Private ==

+ 8 - 0
logic/nodes.go

@@ -5,6 +5,7 @@ import (
 	"errors"
 	"fmt"
 	"net"
+	"sort"
 	"time"
 
 	validator "github.com/go-playground/validator/v10"
@@ -546,4 +547,11 @@ func createNode(node *models.Node) error {
 	return err
 }
 
+// SortApiNodes - Sorts slice of ApiNodes by their ID alphabetically with numbers first
+func SortApiNodes(unsortedNodes []models.ApiNode) {
+	sort.Slice(unsortedNodes, func(i, j int) bool {
+		return unsortedNodes[i].ID < unsortedNodes[j].ID
+	})
+}
+
 // == END PRO ==

+ 8 - 0
logic/users.go

@@ -2,6 +2,7 @@ package logic
 
 import (
 	"encoding/json"
+	"sort"
 
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
@@ -77,3 +78,10 @@ func SetUserDefaults(user *models.User) {
 		user.Groups = []string{pro.DEFAULT_ALLOWED_GROUPS}
 	}
 }
+
+// SortUsers - Sorts slice of Users by username
+func SortUsers(unsortedUsers []models.ReturnUser) {
+	sort.Slice(unsortedUsers, func(i, j int) bool {
+		return unsortedUsers[i].UserName < unsortedUsers[j].UserName
+	})
+}

+ 1 - 1
main.go

@@ -27,7 +27,7 @@ import (
 	stunserver "github.com/gravitl/netmaker/stun-server"
 )
 
-var version = "v0.18.6"
+var version = "v0.18.7"
 
 // Start DB Connection and start API Request Handler
 func main() {

+ 3 - 0
models/host.go

@@ -47,6 +47,7 @@ type Host struct {
 	Version          string           `json:"version" yaml:"version"`
 	IPForwarding     bool             `json:"ipforwarding" yaml:"ipforwarding"`
 	DaemonInstalled  bool             `json:"daemoninstalled" yaml:"daemoninstalled"`
+	AutoUpdate       bool             `json:"autoupdate" yaml:"autoupdate"`
 	HostPass         string           `json:"hostpass" yaml:"hostpass"`
 	Name             string           `json:"name" yaml:"name"`
 	OS               string           `json:"os" yaml:"os"`
@@ -118,6 +119,8 @@ const (
 	CheckIn = "CHECK_IN"
 	// REGISTER_WITH_TURN - registers host with turn server if configured
 	RegisterWithTurn = "REGISTER_WITH_TURN"
+	// UpdateKeys - update wireguard private/public keys
+	UpdateKeys = "UPDATE_KEYS"
 )
 
 // HostUpdate - struct for host update

+ 0 - 2
models/node.go

@@ -19,8 +19,6 @@ const (
 	// MAX_NAME_LENGTH - max name length of node
 	MAX_NAME_LENGTH = 62
 	// == ACTIONS == (can only be set by server)
-	// NODE_UPDATE_KEY - action to update key
-	NODE_UPDATE_KEY = "updatekey"
 	// NODE_DELETE - delete node action
 	NODE_DELETE = "delete"
 	// NODE_IS_PENDING - node pending status

+ 23 - 0
mq/handlers.go

@@ -16,6 +16,7 @@ import (
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/servercfg"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 )
 
 // DefaultHandler default message queue handler  -- NOT USED
@@ -117,6 +118,28 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
 			}
 		}
 	case models.UpdateHost:
+		if hostUpdate.Host.PublicKey != currentHost.PublicKey {
+			//remove old peer entry
+			peerUpdate := models.HostPeerUpdate{
+				ServerVersion: servercfg.GetVersion(),
+				Peers: []wgtypes.PeerConfig{
+					{
+						PublicKey: currentHost.PublicKey,
+						Remove:    true,
+					},
+				},
+			}
+			data, err := json.Marshal(&peerUpdate)
+			if err != nil {
+				logger.Log(2, "json error", err.Error())
+			}
+			hosts := logic.GetRelatedHosts(hostUpdate.Host.ID.String())
+			server := servercfg.GetServer()
+			for _, host := range hosts {
+				publish(&host, fmt.Sprintf("peers/host/%s/%s", host.ID.String(), server), data)
+			}
+
+		}
 		sendPeerUpdate = logic.UpdateHostFromClient(&hostUpdate.Host, currentHost)
 		err := logic.UpsertHost(currentHost)
 		if err != nil {

+ 5 - 1
release.md

@@ -1,9 +1,14 @@
 # Netmaker v0.18.7
 
 ## whats new
+- internet gateways (0.0.0.0/0) for egress
 - deprecated editing of network parameters
+- allow extra ips for extclient (not enabled in UI)
     
 ## whats fixed
+- nm-quick - determine lastest version from releases
+- wireguard public/private key rotation
+- ee-license checks
 
 ## known issues
 - Caddy does not handle netmaker exporter well for EE
@@ -14,4 +19,3 @@
 - 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

+ 1 - 1
scripts/nm-quick.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-LATEST="v0.18.6"
+LATEST=$(curl -s https://api.github.com/repos/gravitl/netmaker/releases/latest | grep "tag_name" | cut -d : -f 2,3 | tr -d [:space:],\")
 
 print_logo() {(
 cat << "EOF"

+ 16 - 0
servercfg/serverconf.go

@@ -46,6 +46,11 @@ func GetServerConfig() config.ServerConfig {
 	cfg.StunPort = GetStunPort()
 	cfg.BrokerType = GetBrokerType()
 	cfg.EmqxRestEndpoint = GetEmqxRestEndpoint()
+	if AutoUpdateEnabled() {
+		cfg.NetclientAutoUpdate = "enabled"
+	} else {
+		cfg.NetclientAutoUpdate = "disabled"
+	}
 	if IsRestBackend() {
 		cfg.RestBackend = "on"
 	}
@@ -405,6 +410,17 @@ func GetVerbosity() int32 {
 	return int32(verbosity)
 }
 
+// AutoUpdateEnabled returns a boolean indicating whether netclient auto update is enabled or disabled
+// default is enabled
+func AutoUpdateEnabled() bool {
+	if os.Getenv("NETCLIENT_AUTO_UPDATE") == "disabled" {
+		return false
+	} else if config.Config.Server.NetclientAutoUpdate == "disabled" {
+		return false
+	}
+	return true
+}
+
 // IsDNSMode - should it run with DNS
 func IsDNSMode() bool {
 	isdns := true

+ 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.
     title: Netmaker
-    version: 0.18.6
+    version: 0.18.7
 paths:
     /api/dns:
         get: