diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 881d19cc..308324d0 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.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 diff --git a/.github/workflows/branchtest.yml b/.github/workflows/branchtest.yml index a2659fd6..bb496beb 100644 --- a/.github/workflows/branchtest.yml +++ b/.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 diff --git a/.github/workflows/deletedroplets.yml b/.github/workflows/deletedroplets.yml index 1521ef69..fc310fee 100644 --- a/.github/workflows/deletedroplets.yml +++ b/.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 }} diff --git a/README.md b/README.md index c7baee54..37467723 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@

- + diff --git a/compose/docker-compose-emqx.yml b/compose/docker-compose-emqx.yml index b546babb..e7899e18 100644 --- a/compose/docker-compose-emqx.yml +++ b/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: diff --git a/compose/docker-compose.ee.yml b/compose/docker-compose.ee.yml index 2e7928ae..80139f8d 100644 --- a/compose/docker-compose.ee.yml +++ b/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: "*" diff --git a/compose/docker-compose.netclient.yml b/compose/docker-compose.netclient.yml index 2b93485a..2c949fdf 100644 --- a/compose/docker-compose.netclient.yml +++ b/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: diff --git a/compose/docker-compose.reference.yml b/compose/docker-compose.reference.yml index 17c9a5d0..b34d3cc3 100644 --- a/compose/docker-compose.reference.yml +++ b/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. diff --git a/compose/docker-compose.yml b/compose/docker-compose.yml index 0c8e5580..b0c43dfe 100644 --- a/compose/docker-compose.yml +++ b/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: "*" diff --git a/config/config.go b/config/config.go index e462cb85..c6621141 100644 --- a/config/config.go +++ b/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"` diff --git a/controllers/dns.go b/controllers/dns.go index 4a38aa0c..eb038fc0 100644 --- a/controllers/dns.go +++ b/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) } diff --git a/controllers/docs.go b/controllers/docs.go index a30e7ae9..c6b05d4e 100644 --- a/controllers/docs.go +++ b/controllers/docs.go @@ -10,7 +10,7 @@ // // Schemes: https // BasePath: / -// Version: 0.18.6 +// Version: 0.18.7 // Host: netmaker.io // // Consumes: diff --git a/controllers/ext_client.go b/controllers/ext_client.go index 8a1c2541..057a8239 100644 --- a/controllers/ext_client.go +++ b/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) } diff --git a/controllers/hosts.go b/controllers/hosts.go index a10dd27f..cf159198 100644 --- a/controllers/hosts.go +++ b/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 } diff --git a/controllers/network.go b/controllers/network.go index f96c6902..8be03dca 100644 --- a/controllers/network.go +++ b/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). diff --git a/controllers/node.go b/controllers/node.go index 51518106..92852c64 100644 --- a/controllers/node.go +++ b/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) } diff --git a/controllers/user.go b/controllers/user.go index 9cd1d3fc..b6b470c9 100644 --- a/controllers/user.go +++ b/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) } diff --git a/ee/license.go b/ee/license.go index 8341c6d1..521326ec 100644 --- a/ee/license.go +++ b/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 diff --git a/go.mod b/go.mod index 965a33d9..c1cb2036 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index a7745744..69891367 100644 --- a/go.sum +++ b/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= diff --git a/k8s/client/netclient-daemonset.yaml b/k8s/client/netclient-daemonset.yaml index 6c6c55c1..2416fd77 100644 --- a/k8s/client/netclient-daemonset.yaml +++ b/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" diff --git a/k8s/client/netclient.yaml b/k8s/client/netclient.yaml index 5406534a..3f57229a 100644 --- a/k8s/client/netclient.yaml +++ b/k8s/client/netclient.yaml @@ -28,7 +28,7 @@ spec: # - "" containers: - name: netclient - image: gravitl/netclient:v0.18.6 + image: gravitl/netclient:v0.18.7 env: - name: TOKEN value: "TOKEN_VALUE" diff --git a/k8s/server/netmaker-server.yaml b/k8s/server/netmaker-server.yaml index 49f61009..94032acb 100644 --- a/k8s/server/netmaker-server.yaml +++ b/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: diff --git a/k8s/server/netmaker-ui.yaml b/k8s/server/netmaker-ui.yaml index 5aade688..58d4ff97 100644 --- a/k8s/server/netmaker-ui.yaml +++ b/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: diff --git a/logger/logger.go b/logger/logger.go index ad258772..154d2618 100644 --- a/logger/logger.go +++ b/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 diff --git a/logic/clients.go b/logic/clients.go index 01427e5f..2eb58fed 100644 --- a/logic/clients.go +++ b/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 + }) +} diff --git a/logic/dns.go b/logic/dns.go index 4a0955f5..146a72fe 100644 --- a/logic/dns.go +++ b/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 { diff --git a/logic/hosts.go b/logic/hosts.go index 7b4acd1e..eb983085 100644 --- a/logic/hosts.go +++ b/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 + }) +} diff --git a/logic/networks.go b/logic/networks.go index b24795a9..ecdce22a 100644 --- a/logic/networks.go +++ b/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 == diff --git a/logic/nodes.go b/logic/nodes.go index f68602a7..955a7b54 100644 --- a/logic/nodes.go +++ b/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 == diff --git a/logic/users.go b/logic/users.go index 1761a999..4c86f3c1 100644 --- a/logic/users.go +++ b/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 + }) +} diff --git a/main.go b/main.go index a3bcaa2f..fafa8540 100644 --- a/main.go +++ b/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() { diff --git a/models/host.go b/models/host.go index 1e71c867..3a2897d1 100644 --- a/models/host.go +++ b/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 diff --git a/models/node.go b/models/node.go index 945323c7..60677af0 100644 --- a/models/node.go +++ b/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 diff --git a/mq/handlers.go b/mq/handlers.go index 6b339bfc..9ea691ca 100644 --- a/mq/handlers.go +++ b/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 { diff --git a/release.md b/release.md index 50039dfd..02382cfc 100644 --- a/release.md +++ b/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 diff --git a/scripts/nm-quick.sh b/scripts/nm-quick.sh index 63185805..ee9814e2 100755 --- a/scripts/nm-quick.sh +++ b/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" diff --git a/servercfg/serverconf.go b/servercfg/serverconf.go index 9576d9b9..c837278c 100644 --- a/servercfg/serverconf.go +++ b/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 diff --git a/swagger.yaml b/swagger.yaml index 9b89ed9f..9e96a2a1 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -704,7 +704,7 @@ info: API calls must be authenticated via a header of the format -H “Authorization: Bearer ” 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//authenticate endpoint, as documented below. title: Netmaker - version: 0.18.6 + version: 0.18.7 paths: /api/dns: get: