Browse Source

Merge pull request #1782 from gravitl/feature_proxy_metrics

Feature proxy metrics
Abhishek K 2 years ago
parent
commit
c4c1b795e3
73 changed files with 1665 additions and 1244 deletions
  1. 2 0
      .github/ISSUE_TEMPLATE/bug-report.yml
  2. 12 0
      .github/dependabot.yml
  3. 110 16
      .github/workflows/buildandrelease.yml
  4. 4 4
      .github/workflows/docker-builder.yml
  5. 40 46
      .github/workflows/publish-docker.yml
  6. 6 6
      .github/workflows/publish-netclient-docker-userspace.yml
  7. 8 8
      .github/workflows/publish-netclient-docker.yml
  8. 1 1
      .github/workflows/purgeGHCR.yml
  9. 16 14
      .github/workflows/test.yml
  10. 1 1
      Dockerfile
  11. 9 5
      README.md
  12. 1 1
      SECURITY.md
  13. 19 79
      compose/docker-compose.ee.yml
  14. 19 56
      compose/docker-compose.reference.yml
  15. 20 56
      compose/docker-compose.yml
  16. 0 2
      controllers/controller.go
  17. 1 1
      controllers/docs.go
  18. 1 1
      controllers/ext_client.go
  19. 1 52
      controllers/network.go
  20. 1 1
      controllers/network_test.go
  21. 0 33
      controllers/node.go
  22. 5 0
      docker/Caddyfile
  23. 50 0
      docker/Caddyfile-EE
  24. 1 1
      docker/Dockerfile-go-builder
  25. 1 1
      docker/Dockerfile-netclient-doks
  26. 1 1
      docker/Dockerfile-netclient-doks-uspace
  27. 1 1
      docker/Dockerfile-netclient-multiarch
  28. 1 1
      docker/Dockerfile-netclient-multiarch-userspace
  29. 2 0
      docker/mosquitto.conf
  30. 2 2
      ee/ee_controllers/networkusers.go
  31. 2 2
      ee/ee_controllers/usergroups.go
  32. 6 1
      ee/initialize.go
  33. 2 2
      ee/license.go
  34. 7 6
      go.mod
  35. 17 9
      go.sum
  36. 1 1
      k8s/client/netclient-daemonset.yaml
  37. 1 1
      k8s/client/netclient.yaml
  38. 1 1
      k8s/server/netmaker-server.yaml
  39. 1 1
      k8s/server/netmaker-ui.yaml
  40. 3 0
      logic/auth.go
  41. 1 1
      logic/gateway.go
  42. 5 3
      logic/networks.go
  43. 0 2
      logic/peers.go
  44. 1 1
      logic/security.go
  45. 1 1
      logic/zombie.go
  46. 1 1
      main.go
  47. 11 3
      netclient/bin-maker.sh
  48. 2 2
      netclient/functions/daemon.go
  49. 37 1
      netclient/gui/gui.go
  50. 1 1
      netclient/netclient.exe.manifest.xml
  51. 5 5
      netclient/versioninfo.json
  52. 18 10
      netclient/wireguard/common.go
  53. 6 2
      nm-proxy/common/common.go
  54. 22 0
      nm-proxy/common/functions.go
  55. 40 56
      nm-proxy/manager/manager.go
  56. 56 0
      nm-proxy/metrics/metrics.go
  57. 65 0
      nm-proxy/models/models.go
  58. 0 46
      nm-proxy/models/peer.go
  59. 6 5
      nm-proxy/nm-proxy.go
  60. 153 15
      nm-proxy/packet/packet.go
  61. 20 53
      nm-proxy/packet/packet_helper.go
  62. 16 1
      nm-proxy/packet/utils.go
  63. 34 33
      nm-proxy/peer/peer.go
  64. 65 36
      nm-proxy/proxy/proxy.go
  65. 245 0
      nm-proxy/proxy/proxy_helper.go
  66. 0 231
      nm-proxy/proxy/wireguard.go
  67. 161 99
      nm-proxy/server/server.go
  68. 14 3
      scripts/netclient-install.sh
  69. 291 186
      scripts/nm-quick-interactive.sh
  70. 3 1
      scripts/nm-quick.sh
  71. 6 1
      servercfg/serverconf.go
  72. 1 1
      servercfg/sqlconf.go
  73. 1 30
      swagger.yaml

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

@@ -31,6 +31,8 @@ body:
       label: Version
       label: Version
       description: What version are you running?
       description: What version are you running?
       options:
       options:
+        - v0.17.0
+        - v0.16.3
         - v0.16.2
         - v0.16.2
         - v0.16.1
         - v0.16.1
         - v0.16.0      
         - v0.16.0      

+ 12 - 0
.github/dependabot.yml

@@ -16,3 +16,15 @@ updates:
     schedule:
     schedule:
       interval: "weekly"
       interval: "weekly"
     target-branch: "develop"
     target-branch: "develop"
+  # Enable version updates for GitHubActions
+  - package-ecosystem: "github-actions"
+    directory: "/"
+    schedule:
+      interval: "weekly"
+    target-branch: "develop"
+  # Enable version updates for docker images 
+  - package-ecosystem: "docker"
+    directory: "/"
+    schedule:
+      interval: "weekly"
+    target-branch: "develop"

+ 110 - 16
.github/workflows/buildandrelease.yml

@@ -53,11 +53,11 @@ jobs:
           echo "NETMAKER_VERSION=${TAG}"  >> $GITHUB_ENV
           echo "NETMAKER_VERSION=${TAG}"  >> $GITHUB_ENV
           echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
           echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
       - name: Checkout
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Setup go
       - name: Setup go
         uses: actions/setup-go@v2
         uses: actions/setup-go@v2
         with:
         with:
-          go-version: 1.18
+          go-version: 1.19
       - name: Build
       - name: Build
         run: |
         run: |
           env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netmaker main.go
           env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netmaker main.go
@@ -76,7 +76,7 @@ jobs:
     needs: version
     needs: version
     steps:
     steps:
       - name: Checkout
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Set Variables
       - name: Set Variables
         run: |
         run: |
           TAG=${{needs.version.outputs.tag}}
           TAG=${{needs.version.outputs.tag}}
@@ -86,7 +86,7 @@ jobs:
       - name: Setup go
       - name: Setup go
         uses: actions/setup-go@v2
         uses: actions/setup-go@v2
         with:
         with:
-          go-version: 1.18
+          go-version: 1.19
 
 
       - name: Build cli
       - name: Build cli
         run: |
         run: |
@@ -106,6 +106,7 @@ jobs:
 
 
       - name: build gui
       - name: build gui
         run: |
         run: |
+          sudo apt-get update
           sudo apt-get install -y gcc libgl1-mesa-dev xorg-dev
           sudo apt-get install -y gcc libgl1-mesa-dev xorg-dev
           go build -tags=gui -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-gui .
           go build -tags=gui -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-gui .
 
 
@@ -180,7 +181,7 @@ jobs:
     needs: version
     needs: version
     steps:
     steps:
       - name: Checkout
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Set Variables
       - name: Set Variables
         run: |
         run: |
           TAG=${{needs.version.outputs.tag}}
           TAG=${{needs.version.outputs.tag}}
@@ -190,7 +191,7 @@ jobs:
       - name: Setup go
       - name: Setup go
         uses: actions/setup-go@v2
         uses: actions/setup-go@v2
         with:
         with:
-          go-version: 1.18
+          go-version: 1.19
       - name: Build
       - name: Build
         run: |
         run: |
           cd netclient
           cd netclient
@@ -280,7 +281,7 @@ jobs:
     needs: version
     needs: version
     steps:
     steps:
       - name: Checkout
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Set Variables
       - name: Set Variables
         run: |
         run: |
           TAG=${{needs.version.outputs.tag}}
           TAG=${{needs.version.outputs.tag}}
@@ -290,28 +291,121 @@ jobs:
       - name: Setup go
       - name: Setup go
         uses: actions/setup-go@v2
         uses: actions/setup-go@v2
         with:
         with:
-          go-version: 1.18
+          go-version: 1.19
       - name: Build
       - name: Build
         run: |
         run: |
           cd netclient
           cd netclient
-          env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "-s -w -X 'main.version=$NETMAKER_VERSION'" -o build/netclient-mipsle/netclient main.go && upx build/netclient-mipsle/netclient
+          env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "-s -w -X 'main.version=$NETMAKER_VERSION'" -o build/netclient-mipsle/netclient-mipsle main.go && upx -o build/netclient-mipsle/netclient-mipsle-upx build/netclient-mipsle/netclient-mipsle
+          env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "-s -w -X 'main.version=$NETMAKER_VERSION'" -o build/netclient-mipsle/netclient-mipsle-softfloat main.go && upx -o build/netclient-mipsle/netclient-mipsle-softfloat-upx build/netclient-mipsle/netclient-mipsle-softfloat
 
 
       - name: Upload mipsle to Release
       - name: Upload mipsle to Release
         uses: svenstaro/upload-release-action@v2
         uses: svenstaro/upload-release-action@v2
         with:
         with:
           repo_token: ${{ secrets.GITHUB_TOKEN }}
           repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-mipsle/netclient
+          file: netclient/build/netclient-mipsle/netclient-mipsle
           tag: ${{ env.NETMAKER_VERSION }}
           tag: ${{ env.NETMAKER_VERSION }}
           overwrite: true
           overwrite: true
           prerelease: true
           prerelease: true
           asset_name: netclient-mipsle
           asset_name: netclient-mipsle
 
 
+      - name: Upload mipsle-upx to Release
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: netclient/build/netclient-mipsle/netclient-mipsle-upx
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient-mipsle-upx
+
+      - name: Upload mipsle-softfloat to Release
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: netclient/build/netclient-mipsle/netclient-mipsle-softfloat
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient-mipsle-softfloat
+
+      - name: Upload mipsle-softfloat-upx to Release
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: netclient/build/netclient-mipsle/netclient-mipsle-softfloat-upx 
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient-mipsle-softfloat-upx 
+
+  netclient-mips:
+    runs-on: ubuntu-latest
+    needs: version
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v3
+      - name: Set Variables
+        run: |
+          TAG=${{needs.version.outputs.tag}}
+          VERSION=${{needs.version.outputs.version}}
+          echo "NETMAKER_VERSION=${TAG}"  >> $GITHUB_ENV
+          echo "PACKAGE_VERSION=${VERSION}" >> $GITHUB_ENV
+      - name: Setup go
+        uses: actions/setup-go@v2
+        with:
+          go-version: 1.19
+      - name: Build
+        run: |
+          cd netclient
+          env CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "-s -w -X 'main.version=$NETMAKER_VERSION'" -o build/netclient-mips/netclient-mips main.go && upx -o build/netclient-mips/netclient-mips-upx build/netclient-mips/netclient-mips 
+          env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "-s -w -X 'main.version=$NETMAKER_VERSION'" -o build/netclient-mips/netclient-mips-softfloat main.go && upx -o build/netclient-mips/netclient-mips-softfloat-upx build/netclient-mips/netclient-mips-softfloat 
+
+      - name: Upload mips to Release
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: netclient/build/netclient-mips/netclient-mips
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient-mips
+
+      - name: Upload mips-upx to Release
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: netclient/build/netclient-mips/netclient-mips-upx
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient-mips-upx
+
+      - name: Upload netclient-mips-softfloat to Release
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: netclient/build/netclient-mips/netclient-mips-softfloat
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient-mips-softfloat
+
+      - name: Upload netclient-mips-softfloat-upx to Release
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: netclient/build/netclient-mips/netclient-mips-softfloat-upx
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient-mips-softfloat-upx
+
   netclient-freebsd:
   netclient-freebsd:
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
     needs: version
     needs: version
     steps:
     steps:
       - name: Checkout
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Set Variables
       - name: Set Variables
         run: |
         run: |
           TAG=${{needs.version.outputs.tag}}
           TAG=${{needs.version.outputs.tag}}
@@ -321,7 +415,7 @@ jobs:
       - name: Setup go
       - name: Setup go
         uses: actions/setup-go@v2
         uses: actions/setup-go@v2
         with:
         with:
-          go-version: 1.18
+          go-version: 1.19
       - name: Build
       - name: Build
         run: |
         run: |
           cd netclient
           cd netclient
@@ -386,7 +480,7 @@ jobs:
     needs: version
     needs: version
     steps:
     steps:
       - name: Checkout
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Set Variables
       - name: Set Variables
         run: |
         run: |
           TAG=${{needs.version.outputs.tag}}
           TAG=${{needs.version.outputs.tag}}
@@ -396,7 +490,7 @@ jobs:
       - name: Setup go
       - name: Setup go
         uses: actions/setup-go@v2
         uses: actions/setup-go@v2
         with:
         with:
-          go-version: 1.18
+          go-version: 1.19
       - name: Build
       - name: Build
         run: |
         run: |
           cd netclient
           cd netclient
@@ -438,7 +532,7 @@ jobs:
     needs: version
     needs: version
     steps:
     steps:
       - name: Checkout
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Set Variables
       - name: Set Variables
         run: |
         run: |
           TAG=${{needs.version.outputs.tag}}
           TAG=${{needs.version.outputs.tag}}
@@ -449,7 +543,7 @@ jobs:
       - name: Setup go
       - name: Setup go
         uses: actions/setup-go@v3
         uses: actions/setup-go@v3
         with:
         with:
-          go-version: 1.18
+          go-version: 1.19
       - name: Mysys2 setup
       - name: Mysys2 setup
         uses: msys2/setup-msys2@v2
         uses: msys2/setup-msys2@v2
         with:
         with:

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

@@ -10,16 +10,16 @@ jobs:
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
     steps:
     steps:
     - name: Checkout
     - name: Checkout
-      uses: actions/checkout@v2
+      uses: actions/checkout@v3
     - name: SetUp Buildx
     - name: SetUp Buildx
-      uses: docker/setup-buildx-action@v1
+      uses: docker/setup-buildx-action@v2
     - name: Login to Dockerhub
     - name: Login to Dockerhub
-      uses: docker/login-action@v1
+      uses: docker/login-action@v2
       with:
       with:
           username: ${{ secrets.DOCKERHUB_USERNAME }}
           username: ${{ secrets.DOCKERHUB_USERNAME }}
           password: ${{ secrets.DOCKERHUB_TOKEN }}
           password: ${{ secrets.DOCKERHUB_TOKEN }}
     - name: Build and push to docker hub
     - name: Build and push to docker hub
-      uses: docker/build-push-action@v2
+      uses: docker/build-push-action@v3
       with:
       with:
         context: .
         context: .
         push: true
         push: true

+ 40 - 46
.github/workflows/publish-docker.yml

@@ -26,70 +26,64 @@ jobs:
             echo "TAG=${TAG}" >> $GITHUB_ENV
             echo "TAG=${TAG}" >> $GITHUB_ENV
       - 
       - 
         name: Checkout
         name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - 
       - 
         name: Set up QEMU
         name: Set up QEMU
-        uses: docker/setup-qemu-action@v1
+        uses: docker/setup-qemu-action@v2
       - 
       - 
         name: Set up Docker Buildx
         name: Set up Docker Buildx
-        uses: docker/setup-buildx-action@v1
+        uses: docker/setup-buildx-action@v2
       - 
       - 
         name: Login to DockerHub
         name: Login to DockerHub
-        uses: docker/login-action@v1
+        uses: docker/login-action@v2
         with:
         with:
           username: ${{ secrets.DOCKERHUB_USERNAME }}
           username: ${{ secrets.DOCKERHUB_USERNAME }}
           password: ${{ secrets.DOCKERHUB_TOKEN }}
           password: ${{ secrets.DOCKERHUB_TOKEN }}
-      - 
-        name: Build x86 and export to Docker
-        uses: docker/build-push-action@v2
-        with:
-          context: .
-          load: true
-          platforms: linux/amd64
-          tags: ${{ env.TAG }}
-          build-args: version=${{ env.TAG }}
-      -
-        name: Test x86
-        run: |
-            docker run --rm ${{ env.TAG }}&
-            sleep 10
-            kill %1
       -
       -
-        name: Build arm64 and export to Docker
-        uses: docker/build-push-action@v2
+        name: Build and push
+        uses: docker/build-push-action@v3
         with:
         with:
           context: .
           context: .
-          load: true
-          platforms: linux/arm64
-          tags: ${{ env.TAG }}
+          platforms: linux/amd64, linux/arm64, linux/arm/v7
+          push: true
+          tags: ${{ github.repository }}:${{ env.TAG }}, ${{ github.repository }}:latest
           build-args: version=${{ env.TAG }}
           build-args: version=${{ env.TAG }}
-      -
-        name: Test arm64
+
+  docker-ee:
+    runs-on: ubuntu-latest
+    steps:
+      - 
+        name: Set tag
         run: |
         run: |
-            docker run --rm ${{ env.TAG }}&
-            sleep 10
-            kill %1
-      -
-        name: Build armv7l and export to Docker
-        uses: docker/build-push-action@v2
+            if [[ -n "${{ github.event.inputs.tag }}" ]]; then
+              docker/[email protected] }}
+            elif [[ "${{ github.ref_name }}" == 'master' ]]; then
+              TAG="latest"
+            else
+              TAG="${{ github.ref_name }}"
+            fi
+            echo "TAG=${TAG}" >> $GITHUB_ENV
+      - 
+        name: Checkout
+        uses: actions/checkout@v3
+      - 
+        name: Set up QEMU
+        uses: docker/setup-qemu-action@v2
+      - 
+        name: Set up Docker Buildx
+        uses: docker/build-push-action@v3
+      - 
+        name: Login to DockerHub
+        uses: docker/login-action@v2
         with:
         with:
-          context: .
-          load: true
-          platforms: linux/arm/v7
-          tags: ${{ env.TAG }}
-          build-args: version=${{ env.TAG }}
-      -
-        name: Test armv7l
-        run: |
-            docker run --rm ${{ env.TAG }}&
-            sleep 10
-            kill %1
+          username: ${{ secrets.DOCKERHUB_USERNAME }}
+          password: ${{ secrets.DOCKERHUB_TOKEN }}
       -
       -
         name: Build and push
         name: Build and push
-        uses: docker/build-push-action@v2
+        uses: docker/build-push-action@v3
         with:
         with:
           context: .
           context: .
           platforms: linux/amd64, linux/arm64, linux/arm/v7
           platforms: linux/amd64, linux/arm64, linux/arm/v7
           push: true
           push: true
-          tags: ${{ github.repository }}:${{ env.TAG }}, ${{ github.repository }}:latest
-          build-args: version=${{ env.TAG }}
+          tags: ${{ github.repository }}:${{ env.TAG }}-ee
+          build-args: version=${{ env.TAG }}, tags="-tags=ee"

+ 6 - 6
.github/workflows/publish-netclient-docker-userspace.yml

@@ -26,25 +26,25 @@ jobs:
             echo "TAG=${TAG}" >> $GITHUB_ENV
             echo "TAG=${TAG}" >> $GITHUB_ENV
       - 
       - 
         name: Checkout
         name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - 
       - 
         name: Set up QEMU
         name: Set up QEMU
-        uses: docker/setup-qemu-action@v1
+        uses: docker/setup-qemu-action@v2
       - 
       - 
         name: Set up Docker Buildx
         name: Set up Docker Buildx
-        uses: docker/setup-buildx-action@v1
+        uses: docker/setup-buildx-action@v2
       - 
       - 
         name: Login to DockerHub
         name: Login to DockerHub
-        uses: docker/login-action@v1
+        uses: docker/login-action@v2
         with:
         with:
           username: ${{ secrets.DOCKERHUB_USERNAME }}
           username: ${{ secrets.DOCKERHUB_USERNAME }}
           password: ${{ secrets.DOCKERHUB_TOKEN }}
           password: ${{ secrets.DOCKERHUB_TOKEN }}
       -
       -
         name: Build and push
         name: Build and push
-        uses: docker/build-push-action@v2
+        uses: docker/build-push-action@v3
         with:
         with:
           context: .
           context: .
-          platforms: linux/amd64
+          platforms: linux/amd64, linux/arm64, linux/arm/v7
           file: ./docker/Dockerfile-netclient-multiarch-userspace
           file: ./docker/Dockerfile-netclient-multiarch-userspace
           push: true
           push: true
           tags: gravitl/netclient-go:${{ env.TAG }}, gravitl/netclient-userspace:latest
           tags: gravitl/netclient-go:${{ env.TAG }}, gravitl/netclient-userspace:latest

+ 8 - 8
.github/workflows/publish-netclient-docker.yml

@@ -26,22 +26,22 @@ jobs:
             echo "TAG=${TAG}" >> $GITHUB_ENV
             echo "TAG=${TAG}" >> $GITHUB_ENV
       - 
       - 
         name: Checkout
         name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - 
       - 
         name: Set up QEMU
         name: Set up QEMU
-        uses: docker/setup-qemu-action@v1
+        uses: docker/setup-qemu-action@v2
       - 
       - 
         name: Set up Docker Buildx
         name: Set up Docker Buildx
-        uses: docker/setup-buildx-action@v1
+        uses: docker/setup-buildx-action@v2
       - 
       - 
         name: Login to DockerHub
         name: Login to DockerHub
-        uses: docker/login-action@v1
+        uses: docker/login-action@v2
         with:
         with:
           username: ${{ secrets.DOCKERHUB_USERNAME }}
           username: ${{ secrets.DOCKERHUB_USERNAME }}
           password: ${{ secrets.DOCKERHUB_TOKEN }}
           password: ${{ secrets.DOCKERHUB_TOKEN }}
       - 
       - 
         name: Build x86 and export to Docker
         name: Build x86 and export to Docker
-        uses: docker/build-push-action@v2
+        uses: docker/build-push-action@v3
         with:
         with:
           context: .
           context: .
           load: true
           load: true
@@ -57,7 +57,7 @@ jobs:
             kill %1
             kill %1
       -
       -
         name: Build arm64 and export to Docker
         name: Build arm64 and export to Docker
-        uses: docker/build-push-action@v2
+        uses: docker/build-push-action@v3
         with:
         with:
           context: .
           context: .
           load: true
           load: true
@@ -73,7 +73,7 @@ jobs:
             kill %1
             kill %1
       -
       -
         name: Build armv7l and export to Docker
         name: Build armv7l and export to Docker
-        uses: docker/build-push-action@v2
+        uses: docker/build-push-action@v3
         with:
         with:
           context: .
           context: .
           load: true
           load: true
@@ -89,7 +89,7 @@ jobs:
             kill %1
             kill %1
       -
       -
         name: Build and push
         name: Build and push
-        uses: docker/build-push-action@v2
+        uses: docker/build-push-action@v3
         with:
         with:
           context: .
           context: .
           platforms: linux/amd64, linux/arm64, linux/arm/v7
           platforms: linux/amd64, linux/arm64, linux/arm/v7

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

@@ -9,7 +9,7 @@ jobs:
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
     steps:
     steps:
       - name: Checkout
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Prune Netmaker
       - name: Prune Netmaker
         uses: vlaurin/action-ghcr-prune@main
         uses: vlaurin/action-ghcr-prune@main
         with:
         with:

+ 16 - 14
.github/workflows/test.yml

@@ -9,11 +9,11 @@ jobs:
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
     steps:
     steps:
       - name: Checkout
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Setup Go
       - name: Setup Go
         uses: actions/setup-go@v2
         uses: actions/setup-go@v2
         with:
         with:
-          go-version: 1.18
+          go-version: 1.19
       - name: Build
       - name: Build
         run: |
         run: |
          env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build main.go
          env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build main.go
@@ -27,24 +27,25 @@ jobs:
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
     steps:
     steps:
       - name: Checkout
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Setup Go
       - name: Setup Go
         uses: actions/setup-go@v2
         uses: actions/setup-go@v2
         with:
         with:
-          go-version: 1.18
+          go-version: 1.19
       - name: Build
       - name: Build
         run: |
         run: |
+         sudo apt-get update
          sudo apt-get install -y gcc libgl1-mesa-dev xorg-dev
          sudo apt-get install -y gcc libgl1-mesa-dev xorg-dev
          env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -tags=gui main.go
          env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -tags=gui main.go
   mac-gui:
   mac-gui:
     runs-on: macos-latest
     runs-on: macos-latest
     steps:
     steps:
       - name: Checkout
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Setup Go
       - name: Setup Go
         uses: actions/setup-go@v2
         uses: actions/setup-go@v2
         with:
         with:
-          go-version: 1.18
+          go-version: 1.19
       - name: Build mac
       - name: Build mac
         run: |
         run: |
           env CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -tags=gui main.go
           env CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -tags=gui main.go
@@ -52,11 +53,11 @@ jobs:
     runs-on: windows-latest
     runs-on: windows-latest
     steps:
     steps:
       - name: Checkout
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Setup Go
       - name: Setup Go
         uses: actions/setup-go@v2
         uses: actions/setup-go@v2
         with:
         with:
-          go-version: 1.18
+          go-version: 1.19
       - name: Mysys2 setup
       - name: Mysys2 setup
         uses: msys2/setup-msys2@v2
         uses: msys2/setup-msys2@v2
         with:
         with:
@@ -72,17 +73,18 @@ jobs:
     runs-on: ubuntu-22.04
     runs-on: ubuntu-22.04
     steps:
     steps:
       - name: Checkout
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Setup Go
       - name: Setup Go
         uses: actions/setup-go@v2
         uses: actions/setup-go@v2
         with:
         with:
-          go-version: 1.18
+          go-version: 1.19
       - name: run tests
       - name: run tests
         run: |
         run: |
-            sudo apt-get install -y gcc libgl1-mesa-dev xorg-dev 
-            go test -p 1 ./... -v
-            go install honnef.co/go/tools/cmd/staticcheck@latest
-            { ~/go/bin/staticcheck  -tags=ee ./... ; }
+          sudo apt update
+          sudo apt-get install -y gcc libgl1-mesa-dev xorg-dev
+          go test -p 1 ./... -v
+          go install honnef.co/go/tools/cmd/staticcheck@latest
+          { ~/go/bin/staticcheck  -tags=ee ./... ; }
         env:
         env:
           DATABASE: sqlite
           DATABASE: sqlite
           CLIENT_MODE: "off"
           CLIENT_MODE: "off"

+ 1 - 1
Dockerfile

@@ -9,7 +9,7 @@ ENV GO111MODULE=auto
 RUN apk add git libpcap-dev
 RUN apk add git libpcap-dev
 RUN GOOS=linux CGO_ENABLED=1 go build ${tags} -ldflags="-s -X 'main.version=${version}'" .
 RUN GOOS=linux CGO_ENABLED=1 go build ${tags} -ldflags="-s -X 'main.version=${version}'" .
 # RUN go build -tags=ee . -o netmaker main.go
 # RUN go build -tags=ee . -o netmaker main.go
-FROM alpine:3.15.2
+FROM alpine:3.16.2
 
 
 # add a c lib
 # add a c lib
 RUN apk add gcompat iptables wireguard-tools libpcap-dev
 RUN apk add gcompat iptables wireguard-tools libpcap-dev

+ 9 - 5
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.16.2-informational?style=flat-square" />
+    <img src="https://img.shields.io/badge/Version-0.17.0-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" />
@@ -52,10 +52,14 @@
 
 
 (For production-grade installations, visit the [Install Docs](https://netmaker.readthedocs.io/en/master/install.html).)  
 (For production-grade installations, visit the [Install Docs](https://netmaker.readthedocs.io/en/master/install.html).)  
 
 
-1. Get a cloud VM with Ubuntu 20.04 and a public IP.
-2. Open ports 443 and 51821-51830/udp on the VM firewall and in cloud security settings.
-3. Run the script: `wget -qO - https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/nm-quick.sh | sudo bash`  
-3.a. (with custom domain + email): `wget -qO - https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/nm-quick.sh | sudo bash -s -- -d mynetmaker.domain.com -e [email protected]`    
+1. Get a cloud VM with Ubuntu 22.04 and a public IP.
+2. Open ports 443, 80, and 51821-51830/udp on the VM firewall and in cloud security settings.
+3. (optional) Prepare DNS - Set a wildcard subdomain in your DNS for Netmaker, e.g. *.netmaker.example.com
+4. Run the script: 
+
+`sudo wget -qO /root/nm-quick-interactive.sh https://raw.githubusercontent.com/gravitl/netmaker/test_v0.17.0_compose/scripts/nm-quick-interactive.sh && sudo chmod +x /root/nm-quick-interactive.sh && sudo /root/nm-quick-interactive.sh`  
+
+This script gives you the option to deploy the Community or Enterprise version of Netmaker. If deploying Enterprise, you get a free account with a 50 node limit by default. It also gives you the option to use your own domain (recommended) or an auto-generated domain. 
 
 
 <p float="left" align="middle">
 <p float="left" align="middle">
 <img src="https://raw.githubusercontent.com/gravitl/netmaker-docs/master/images/netmaker-github/readme.gif" />
 <img src="https://raw.githubusercontent.com/gravitl/netmaker-docs/master/images/netmaker-github/readme.gif" />

+ 1 - 1
SECURITY.md

@@ -9,4 +9,4 @@ However, there is no official bug bounty program up yet for the Netmaker project
 
 
 ## Reporting a Vulnerability
 ## Reporting a Vulnerability
 
 
-Please report security issues to `info@gravitl.com`
+Please report security issues to `info@netmaker.io`

+ 19 - 79
compose/docker-compose.ee.yml

@@ -3,7 +3,7 @@ version: "3.4"
 services:
 services:
   netmaker:
   netmaker:
     container_name: netmaker
     container_name: netmaker
-    image: gravitl/netmaker:v0.16.2-ee
+    image: gravitl/netmaker:v0.17.0-ee
     cap_add: 
     cap_add: 
       - NET_ADMIN
       - NET_ADMIN
       - NET_RAW
       - NET_RAW
@@ -45,17 +45,9 @@ services:
       MQ_ADMIN_PASSWORD: "REPLACE_MQ_ADMIN_PASSWORD"
       MQ_ADMIN_PASSWORD: "REPLACE_MQ_ADMIN_PASSWORD"
     ports:
     ports:
       - "51821-51830:51821-51830/udp"
       - "51821-51830:51821-51830/udp"
-    expose:
-      - "8081"
-    labels:
-      - traefik.enable=true
-      - traefik.http.routers.netmaker-api.entrypoints=websecure
-      - traefik.http.routers.netmaker-api.rule=Host(`api.NETMAKER_BASE_DOMAIN`)
-      - traefik.http.routers.netmaker-api.service=netmaker-api
-      - traefik.http.services.netmaker-api.loadbalancer.server.port=8081
   netmaker-ui:
   netmaker-ui:
     container_name: netmaker-ui
     container_name: netmaker-ui
-    image: gravitl/netmaker-ui:v0.16.2
+    image: gravitl/netmaker-ui:v0.17.0
     depends_on:
     depends_on:
       - netmaker
       - netmaker
     links:
     links:
@@ -63,21 +55,17 @@ services:
     restart: always
     restart: always
     environment:
     environment:
       BACKEND_URL: "https://api.NETMAKER_BASE_DOMAIN"
       BACKEND_URL: "https://api.NETMAKER_BASE_DOMAIN"
-    expose:
-      - "80"
-    labels:
-      - traefik.enable=true
-      - traefik.http.middlewares.nmui-security.headers.accessControlAllowOriginList=*.NETMAKER_BASE_DOMAIN
-      - traefik.http.middlewares.nmui-security.headers.stsSeconds=31536000
-      - traefik.http.middlewares.nmui-security.headers.browserXssFilter=true
-      - traefik.http.middlewares.nmui-security.headers.customFrameOptionsValue=SAMEORIGIN
-      - traefik.http.middlewares.nmui-security.headers.customResponseHeaders.X-Robots-Tag=none
-      - traefik.http.middlewares.nmui-security.headers.customResponseHeaders.Server= # Remove the server name
-      - traefik.http.routers.netmaker-ui.entrypoints=websecure
-      - traefik.http.routers.netmaker-ui.middlewares=nmui-security@docker
-      - traefik.http.routers.netmaker-ui.rule=Host(`dashboard.NETMAKER_BASE_DOMAIN`)
-      - traefik.http.routers.netmaker-ui.service=netmaker-ui
-      - traefik.http.services.netmaker-ui.loadbalancer.server.port=80
+  caddy:
+    image: caddy:2.6.2
+    container_name: caddy
+    restart: unless-stopped
+    volumes:
+      - /root/Caddyfile:/etc/caddy/Caddyfile
+      - caddy_data:/data
+      - caddy_conf:/config
+    ports:
+      - "80:80"
+      - "443:443"
   coredns:
   coredns:
     container_name: coredns
     container_name: coredns
     image: coredns/coredns
     image: coredns/coredns
@@ -87,29 +75,9 @@ services:
     restart: always
     restart: always
     volumes:
     volumes:
       - dnsconfig:/root/dnsconfig
       - dnsconfig:/root/dnsconfig
-  traefik:
-    image: traefik:v2.6
-    container_name: traefik
-    command:
-      - "--certificatesresolvers.http.acme.email=YOUR_EMAIL"
-      - "--certificatesresolvers.http.acme.storage=/letsencrypt/acme.json"
-      - "--certificatesresolvers.http.acme.tlschallenge=true"
-      - "--entrypoints.websecure.address=:443"
-      - "--entrypoints.websecure.http.tls=true"
-      - "--entrypoints.websecure.http.tls.certResolver=http"
-      - "--log.level=INFO"
-      - "--providers.docker=true"
-      - "--providers.docker.exposedByDefault=false"
-      - "--serverstransport.insecureskipverify=true"
-    restart: always
-    volumes:
-      - /var/run/docker.sock:/var/run/docker.sock:ro
-      - traefik_certs:/letsencrypt
-    ports:
-      - "443:443"
   mq:
   mq:
     container_name: mq
     container_name: mq
-    image: eclipse-mosquitto:2.0.11-openssl
+    image: eclipse-mosquitto:2.0.15-openssl
     depends_on:
     depends_on:
       - netmaker
       - netmaker
     restart: unless-stopped
     restart: unless-stopped
@@ -121,50 +89,29 @@ services:
       - /root/wait.sh:/mosquitto/config/wait.sh
       - /root/wait.sh:/mosquitto/config/wait.sh
       - mosquitto_data:/mosquitto/data
       - mosquitto_data:/mosquitto/data
       - mosquitto_logs:/mosquitto/log
       - mosquitto_logs:/mosquitto/log
-    expose:
-      - "8883"
-    labels:
-      - traefik.enable=true
-      - traefik.tcp.routers.mqtt.rule=HostSNI(`broker.NETMAKER_BASE_DOMAIN`)
-      - traefik.tcp.routers.mqtt.tls.certresolver=http
-      - traefik.tcp.services.mqtt.loadbalancer.server.port=8883
-      - traefik.tcp.routers.mqtt.entrypoints=websecure
+    ports:
+      - "1883:1883"
+      - "8883:8883"
   prometheus:
   prometheus:
     container_name: prometheus
     container_name: prometheus
     image: gravitl/netmaker-prometheus:latest
     image: gravitl/netmaker-prometheus:latest
     environment:
     environment:
       NETMAKER_METRICS_TARGET: "netmaker-exporter.NETMAKER_BASE_DOMAIN"
       NETMAKER_METRICS_TARGET: "netmaker-exporter.NETMAKER_BASE_DOMAIN"
       LICENSE_KEY: "YOUR_LICENSE_KEY"
       LICENSE_KEY: "YOUR_LICENSE_KEY"
-    labels:
-      - traefik.enable=true
-      - traefik.http.routers.prometheus.entrypoints=websecure
-      - traefik.http.routers.prometheus.rule=Host(`prometheus.NETMAKER_BASE_DOMAIN`)
-      - traefik.http.services.prometheus.loadbalancer.server.port=9090
-      - traefik.http.routers.prometheus.service=prometheus
     restart: always
     restart: always
     volumes:
     volumes:
       - prometheus_data:/prometheus
       - prometheus_data:/prometheus
     depends_on:
     depends_on:
       - netmaker
       - netmaker
-    ports:
-      - 9090:9090
   grafana:
   grafana:
     container_name: grafana
     container_name: grafana
     image: gravitl/netmaker-grafana:latest
     image: gravitl/netmaker-grafana:latest
-    labels:
-      - traefik.enable=true
-      - traefik.http.routers.grafana.entrypoints=websecure
-      - traefik.http.routers.grafana.rule=Host(`grafana.NETMAKER_BASE_DOMAIN`)
-      - traefik.http.services.grafana.loadbalancer.server.port=3000
-      - traefik.http.routers.grafana.service=grafana
     environment:
     environment:
       PROMETHEUS_HOST: "prometheus.NETMAKER_BASE_DOMAIN"
       PROMETHEUS_HOST: "prometheus.NETMAKER_BASE_DOMAIN"
       NETMAKER_METRICS_TARGET: "netmaker-exporter.NETMAKER_BASE_DOMAIN"
       NETMAKER_METRICS_TARGET: "netmaker-exporter.NETMAKER_BASE_DOMAIN"
       LICENSE_KEY: "YOUR_LICENSE_KEY"
       LICENSE_KEY: "YOUR_LICENSE_KEY"
     volumes:
     volumes:
       - grafana_data:/var/lib/grafana
       - grafana_data:/var/lib/grafana
-    ports:
-      - 3000:3000
     restart: always
     restart: always
     links:
     links:
       - prometheus
       - prometheus
@@ -174,12 +121,6 @@ services:
   netmaker-exporter:
   netmaker-exporter:
     container_name: netmaker-exporter
     container_name: netmaker-exporter
     image: gravitl/netmaker-exporter:latest
     image: gravitl/netmaker-exporter:latest
-    labels:
-      - traefik.enable=true
-      - traefik.http.routers.netmaker-exporter.entrypoints=websecure
-      - traefik.http.routers.netmaker-exporter.rule=Host(`netmaker-exporter.NETMAKER_BASE_DOMAIN`)
-      - traefik.http.services.netmaker-exporter.loadbalancer.server.port=8085
-      - traefik.http.routers.netmaker-exporter.service=netmaker-exporter
     restart: always
     restart: always
     depends_on:
     depends_on:
       - netmaker
       - netmaker
@@ -192,10 +133,9 @@ services:
       API_PORT: "8085"
       API_PORT: "8085"
       LICENSE_KEY: "YOUR_LICENSE_KEY"
       LICENSE_KEY: "YOUR_LICENSE_KEY"
       PROMETHEUS_HOST: https://prometheus.NETMAKER_BASE_DOMAIN
       PROMETHEUS_HOST: https://prometheus.NETMAKER_BASE_DOMAIN
-    expose:
-      - "8085"
 volumes:
 volumes:
-  traefik_certs: {}
+  caddy_data: {}
+  caddy_conf: {}
   sqldata: {}
   sqldata: {}
   dnsconfig: {}
   dnsconfig: {}
   mosquitto_data: {}
   mosquitto_data: {}

+ 19 - 56
compose/docker-compose.reference.yml

@@ -3,7 +3,7 @@ version: "3.4"
 services:
 services:
   netmaker: # The Primary Server for running Netmaker
   netmaker: # The Primary Server for running Netmaker
     container_name: netmaker
     container_name: netmaker
-    image: gravitl/netmaker:v0.16.2
+    image: gravitl/netmaker:v0.17.0
     cap_add: 
     cap_add: 
       - NET_ADMIN
       - NET_ADMIN
       - NET_RAW
       - NET_RAW
@@ -52,17 +52,9 @@ services:
       OIDC_ISSUER: "" # https://oidc.yourprovider.com - URL of oidc provider
       OIDC_ISSUER: "" # https://oidc.yourprovider.com - URL of oidc provider
     ports:
     ports:
       - "51821-51830:51821-51830/udp" # wireguard ports
       - "51821-51830:51821-51830/udp" # wireguard ports
-    expose:
-      - "8081" # api port
-    labels: # only for use with traefik proxy (default)
-      - traefik.enable=true
-      - traefik.http.routers.netmaker-api.entrypoints=websecure
-      - traefik.http.routers.netmaker-api.rule=Host(`api.NETMAKER_BASE_DOMAIN`)
-      - traefik.http.routers.netmaker-api.service=netmaker-api
-      - traefik.http.services.netmaker-api.loadbalancer.server.port=8081
   netmaker-ui:  # The Netmaker UI Component
   netmaker-ui:  # The Netmaker UI Component
     container_name: netmaker-ui
     container_name: netmaker-ui
-    image: gravitl/netmaker-ui:v0.16.2
+    image: gravitl/netmaker-ui:v0.17.0
     depends_on:
     depends_on:
       - netmaker
       - netmaker
     links:
     links:
@@ -70,21 +62,17 @@ services:
     restart: always
     restart: always
     environment:
     environment:
       BACKEND_URL: "https://api.NETMAKER_BASE_DOMAIN" # URL where UI will send API requests. Change based on SERVER_HOST, SERVER_HTTP_HOST, and API_PORT
       BACKEND_URL: "https://api.NETMAKER_BASE_DOMAIN" # URL where UI will send API requests. Change based on SERVER_HOST, SERVER_HTTP_HOST, and API_PORT
-    expose:
-      - "80"
-    labels:
-      - traefik.enable=true
-      - traefik.http.middlewares.nmui-security.headers.accessControlAllowOriginList=*.NETMAKER_BASE_DOMAIN
-      - traefik.http.middlewares.nmui-security.headers.stsSeconds=31536000
-      - traefik.http.middlewares.nmui-security.headers.browserXssFilter=true
-      - traefik.http.middlewares.nmui-security.headers.customFrameOptionsValue=SAMEORIGIN
-      - traefik.http.middlewares.nmui-security.headers.customResponseHeaders.X-Robots-Tag=none
-      - traefik.http.middlewares.nmui-security.headers.customResponseHeaders.Server= # Remove the server name
-      - traefik.http.routers.netmaker-ui.entrypoints=websecure
-      - traefik.http.routers.netmaker-ui.middlewares=nmui-security@docker
-      - traefik.http.routers.netmaker-ui.rule=Host(`dashboard.NETMAKER_BASE_DOMAIN`)
-      - traefik.http.routers.netmaker-ui.service=netmaker-ui
-      - traefik.http.services.netmaker-ui.loadbalancer.server.port=80
+  caddy: # The reverse proxy that manages traffic for Netmaker
+    image: caddy:2.6.2
+    container_name: caddy
+    restart: unless-stopped
+    volumes:
+      - /root/Caddyfile:/etc/caddy/Caddyfile # Config file for Caddy
+      - caddy_data:/data
+      - caddy_conf:/config
+    ports:
+      - "80:80"
+      - "443:443"
   coredns: # The DNS Server. CoreDNS can be removed unless doing special advanced use cases
   coredns: # The DNS Server. CoreDNS can be removed unless doing special advanced use cases
     container_name: coredns
     container_name: coredns
     image: coredns/coredns
     image: coredns/coredns
@@ -94,29 +82,9 @@ services:
     restart: always
     restart: always
     volumes:
     volumes:
       - dnsconfig:/root/dnsconfig
       - dnsconfig:/root/dnsconfig
-  traefik: # the default proxy - can be replaced with caddy or nginx, but requires careful configuration
-    image: traefik:v2.6
-    container_name: traefik
-    command:
-      - "--certificatesresolvers.http.acme.email=YOUR_EMAIL"
-      - "--certificatesresolvers.http.acme.storage=/letsencrypt/acme.json"
-      - "--certificatesresolvers.http.acme.tlschallenge=true"
-      - "--entrypoints.websecure.address=:443"
-      - "--entrypoints.websecure.http.tls=true"
-      - "--entrypoints.websecure.http.tls.certResolver=http"
-      - "--log.level=INFO"
-      - "--providers.docker=true"
-      - "--providers.docker.exposedByDefault=false"
-      - "--serverstransport.insecureskipverify=true"
-    restart: always
-    volumes:
-      - /var/run/docker.sock:/var/run/docker.sock:ro
-      - traefik_certs:/letsencrypt
-    ports:
-      - "443:443"
   mq: # the MQTT broker for netmaker
   mq: # the MQTT broker for netmaker
     container_name: mq
     container_name: mq
-    image: eclipse-mosquitto:2.0.11-openssl
+    image: eclipse-mosquitto:2.0.15-openssl
     depends_on:
     depends_on:
       - netmaker
       - netmaker
     restart: unless-stopped
     restart: unless-stopped
@@ -125,17 +93,12 @@ services:
       - mosquitto_data:/mosquitto/data
       - mosquitto_data:/mosquitto/data
       - mosquitto_logs:/mosquitto/log
       - mosquitto_logs:/mosquitto/log
       - shared_certs:/mosquitto/certs
       - shared_certs:/mosquitto/certs
-    expose:
-      - "8883"
-    labels:
-      - traefik.enable=true
-      - traefik.tcp.routers.mqtts.rule=HostSNI(`broker.NETMAKER_BASE_DOMAIN`)
-      - traefik.tcp.routers.mqtts.tls.passthrough=true
-      - traefik.tcp.services.mqtts-svc.loadbalancer.server.port=8883
-      - traefik.tcp.routers.mqtts.service=mqtts-svc
-      - traefik.tcp.routers.mqtts.entrypoints=websecure
+    ports:
+      - "1883:1883"
+      - "8883:8883"
 volumes:
 volumes:
-  traefik_certs: {} # ssl certificates - auto generated
+  caddy_data: {} # runtime data for caddy
+  caddy_conf: {} # configuration file for Caddy
   shared_certs: {} # netmaker certs generated for MQ comms - used by nodes/servers
   shared_certs: {} # netmaker certs generated for MQ comms - used by nodes/servers
   sqldata: {} # storage for embedded sqlite
   sqldata: {} # storage for embedded sqlite
   dnsconfig: {} # storage for coredns
   dnsconfig: {} # storage for coredns

+ 20 - 56
compose/docker-compose.yml

@@ -3,7 +3,7 @@ version: "3.4"
 services:
 services:
   netmaker:
   netmaker:
     container_name: netmaker
     container_name: netmaker
-    image: gravitl/netmaker:v0.16.2
+    image: gravitl/netmaker:v0.17.0
     cap_add: 
     cap_add: 
       - NET_ADMIN
       - NET_ADMIN
       - NET_RAW
       - NET_RAW
@@ -44,17 +44,9 @@ services:
       PROXY: "on"
       PROXY: "on"
     ports:
     ports:
       - "51821-51830:51821-51830/udp"
       - "51821-51830:51821-51830/udp"
-    expose:
-      - "8081"
-    labels:
-      - traefik.enable=true
-      - traefik.http.routers.netmaker-api.entrypoints=websecure
-      - traefik.http.routers.netmaker-api.rule=Host(`api.NETMAKER_BASE_DOMAIN`)
-      - traefik.http.routers.netmaker-api.service=netmaker-api
-      - traefik.http.services.netmaker-api.loadbalancer.server.port=8081
   netmaker-ui:
   netmaker-ui:
     container_name: netmaker-ui
     container_name: netmaker-ui
-    image: gravitl/netmaker-ui:v0.16.2
+    image: gravitl/netmaker-ui:v0.17.0
     depends_on:
     depends_on:
       - netmaker
       - netmaker
     links:
     links:
@@ -62,21 +54,17 @@ services:
     restart: always
     restart: always
     environment:
     environment:
       BACKEND_URL: "https://api.NETMAKER_BASE_DOMAIN"
       BACKEND_URL: "https://api.NETMAKER_BASE_DOMAIN"
-    expose:
-      - "80"
-    labels:
-      - traefik.enable=true
-      - traefik.http.middlewares.nmui-security.headers.accessControlAllowOriginList=*.NETMAKER_BASE_DOMAIN
-      - traefik.http.middlewares.nmui-security.headers.stsSeconds=31536000
-      - traefik.http.middlewares.nmui-security.headers.browserXssFilter=true
-      - traefik.http.middlewares.nmui-security.headers.customFrameOptionsValue=SAMEORIGIN
-      - traefik.http.middlewares.nmui-security.headers.customResponseHeaders.X-Robots-Tag=none
-      - traefik.http.middlewares.nmui-security.headers.customResponseHeaders.Server= # Remove the server name
-      - traefik.http.routers.netmaker-ui.entrypoints=websecure
-      - traefik.http.routers.netmaker-ui.middlewares=nmui-security@docker
-      - traefik.http.routers.netmaker-ui.rule=Host(`dashboard.NETMAKER_BASE_DOMAIN`)
-      - traefik.http.routers.netmaker-ui.service=netmaker-ui
-      - traefik.http.services.netmaker-ui.loadbalancer.server.port=80
+  caddy:
+    image: caddy:2.6.2
+    container_name: caddy
+    restart: unless-stopped
+    volumes:
+      - /root/Caddyfile:/etc/caddy/Caddyfile
+      - caddy_data:/data
+      - caddy_conf:/config
+    ports:
+      - "80:80"
+      - "443:443"
   coredns:
   coredns:
     container_name: coredns
     container_name: coredns
     image: coredns/coredns
     image: coredns/coredns
@@ -86,29 +74,9 @@ services:
     restart: always
     restart: always
     volumes:
     volumes:
       - dnsconfig:/root/dnsconfig
       - dnsconfig:/root/dnsconfig
-  traefik:
-    image: traefik:v2.6
-    container_name: traefik
-    command:
-      - "--certificatesresolvers.http.acme.email=YOUR_EMAIL"
-      - "--certificatesresolvers.http.acme.storage=/letsencrypt/acme.json"
-      - "--certificatesresolvers.http.acme.tlschallenge=true"
-      - "--entrypoints.websecure.address=:443"
-      - "--entrypoints.websecure.http.tls=true"
-      - "--entrypoints.websecure.http.tls.certResolver=http"
-      - "--log.level=INFO"
-      - "--providers.docker=true"
-      - "--providers.docker.exposedByDefault=false"
-      - "--serverstransport.insecureskipverify=true"
-    restart: always
-    volumes:
-      - /var/run/docker.sock:/var/run/docker.sock:ro
-      - traefik_certs:/letsencrypt
-    ports:
-      - "443:443"
   mq:
   mq:
     container_name: mq
     container_name: mq
-    image: eclipse-mosquitto:2.0.11-openssl
+    image: eclipse-mosquitto:2.0.15-openssl
     depends_on:
     depends_on:
       - netmaker
       - netmaker
     restart: unless-stopped
     restart: unless-stopped
@@ -120,17 +88,13 @@ services:
       - /root/wait.sh:/mosquitto/config/wait.sh
       - /root/wait.sh:/mosquitto/config/wait.sh
       - mosquitto_data:/mosquitto/data
       - mosquitto_data:/mosquitto/data
       - mosquitto_logs:/mosquitto/log
       - mosquitto_logs:/mosquitto/log
-    expose:
-      - "8883"
-    labels:
-      - traefik.enable=true
-      - traefik.tcp.routers.mqtt.rule=HostSNI(`broker.NETMAKER_BASE_DOMAIN`)
-      - traefik.tcp.routers.mqtt.tls.certresolver=http
-      - traefik.tcp.services.mqtt.loadbalancer.server.port=8883
-      - traefik.tcp.routers.mqtt.entrypoints=websecure
+    ports:
+      - "1883:1883"
+      - "8883:8883"
 volumes:
 volumes:
-  traefik_certs: {}
+  caddy_data: {}
+  caddy_conf: {}
   sqldata: {}
   sqldata: {}
   dnsconfig: {}
   dnsconfig: {}
   mosquitto_data: {}
   mosquitto_data: {}
-  mosquitto_logs: {}
+  mosquitto_logs: {}

+ 0 - 2
controllers/controller.go

@@ -27,8 +27,6 @@ var HttpHandlers = []interface{}{
 	extClientHandlers,
 	extClientHandlers,
 	ipHandlers,
 	ipHandlers,
 	loggerHandlers,
 	loggerHandlers,
-	userGroupsHandlers,
-	networkUsersHandlers,
 }
 }
 
 
 // HandleRESTRequests - handles the rest requests
 // HandleRESTRequests - handles the rest requests

+ 1 - 1
controllers/docs.go

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

+ 1 - 1
controllers/ext_client.go

@@ -101,7 +101,7 @@ func getAllExtClients(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 	clients := []models.ExtClient{}
 	clients := []models.ExtClient{}
 	var err error
 	var err error
-	if networksSlice[0] == logic.ALL_NETWORK_ACCESS {
+	if len(networksSlice) > 0 && networksSlice[0] == logic.ALL_NETWORK_ACCESS {
 		clients, err = functions.GetAllExtClients()
 		clients, err = functions.GetAllExtClients()
 		if err != nil && !database.IsEmptyRecord(err) {
 		if err != nil && !database.IsEmptyRecord(err) {
 			logger.Log(0, "failed to get all extclients: ", err.Error())
 			logger.Log(0, "failed to get all extclients: ", err.Error())

+ 1 - 52
controllers/network.go

@@ -22,7 +22,6 @@ func networkHandlers(r *mux.Router) {
 	r.HandleFunc("/api/networks", logic.SecurityCheck(true, checkFreeTierLimits(networks_l, http.HandlerFunc(createNetwork)))).Methods("POST")
 	r.HandleFunc("/api/networks", logic.SecurityCheck(true, checkFreeTierLimits(networks_l, http.HandlerFunc(createNetwork)))).Methods("POST")
 	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(false, http.HandlerFunc(getNetwork))).Methods("GET")
 	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(false, http.HandlerFunc(getNetwork))).Methods("GET")
 	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(false, http.HandlerFunc(updateNetwork))).Methods("PUT")
 	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(false, http.HandlerFunc(updateNetwork))).Methods("PUT")
-	r.HandleFunc("/api/networks/{networkname}/nodelimit", logic.SecurityCheck(true, http.HandlerFunc(updateNetworkNodeLimit))).Methods("PUT")
 	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(deleteNetwork))).Methods("DELETE")
 	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(deleteNetwork))).Methods("DELETE")
 	r.HandleFunc("/api/networks/{networkname}/keyupdate", logic.SecurityCheck(true, http.HandlerFunc(keyUpdate))).Methods("POST")
 	r.HandleFunc("/api/networks/{networkname}/keyupdate", logic.SecurityCheck(true, http.HandlerFunc(keyUpdate))).Methods("POST")
 	r.HandleFunc("/api/networks/{networkname}/keys", logic.SecurityCheck(false, http.HandlerFunc(createAccessKey))).Methods("POST")
 	r.HandleFunc("/api/networks/{networkname}/keys", logic.SecurityCheck(false, http.HandlerFunc(createAccessKey))).Methods("POST")
@@ -57,7 +56,7 @@ func getNetworks(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 	allnetworks := []models.Network{}
 	allnetworks := []models.Network{}
 	var err error
 	var err error
-	if networksSlice[0] == logic.ALL_NETWORK_ACCESS {
+	if len(networksSlice) > 0 && networksSlice[0] == logic.ALL_NETWORK_ACCESS {
 		allnetworks, err = logic.GetNetworks()
 		allnetworks, err = logic.GetNetworks()
 		if err != nil && !database.IsEmptyRecord(err) {
 		if err != nil && !database.IsEmptyRecord(err) {
 			logger.Log(0, r.Header.Get("user"), "failed to fetch networks: ", err.Error())
 			logger.Log(0, r.Header.Get("user"), "failed to fetch networks: ", err.Error())
@@ -278,56 +277,6 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(newNetwork)
 	json.NewEncoder(w).Encode(newNetwork)
 }
 }
 
 
-// swagger:route PUT /api/networks/{networkname}/nodelimit networks updateNetworkNodeLimit
-//
-// Update a network's node limit.
-//
-//		Schemes: https
-//
-// 		Security:
-//   		oauth
-//
-//		Responses:
-//			200: networkBodyResponse
-func updateNetworkNodeLimit(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-	var params = mux.Vars(r)
-	var network models.Network
-	netname := params["networkname"]
-	network, err := logic.GetParentNetwork(netname)
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"),
-			fmt.Sprintf("failed to get network [%s] nodes: %v",
-				network.NetID, err.Error()))
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-
-	var networkChange models.Network
-
-	err = json.NewDecoder(r.Body).Decode(&networkChange)
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"), "error decoding request body: ",
-			err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	if networkChange.NodeLimit != 0 {
-		network.NodeLimit = networkChange.NodeLimit
-		data, err := json.Marshal(&network)
-		if err != nil {
-			logger.Log(0, r.Header.Get("user"),
-				"error marshalling resp: ", err.Error())
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-			return
-		}
-		database.Insert(network.NetID, string(data), database.NETWORKS_TABLE_NAME)
-		logger.Log(1, r.Header.Get("user"), "updated network node limit on", netname)
-	}
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(network)
-}
-
 // swagger:route PUT /api/networks/{networkname}/acls networks updateNetworkACL
 // swagger:route PUT /api/networks/{networkname}/acls networks updateNetworkACL
 //
 //
 // Update a network ACL (Access Control List).
 // Update a network ACL (Access Control List).

+ 1 - 1
controllers/network_test.go

@@ -309,7 +309,7 @@ func TestIpv6Network(t *testing.T) {
 	nodeErr := logic.CreateNode(&node1)
 	nodeErr := logic.CreateNode(&node1)
 	t.Run("Test node on network IPv6", func(t *testing.T) {
 	t.Run("Test node on network IPv6", func(t *testing.T) {
 		assert.Nil(t, nodeErr)
 		assert.Nil(t, nodeErr)
-		assert.Equal(t, "fde6:be04:fa5e:d076::", node1.Address6)
+		assert.Equal(t, "fde6:be04:fa5e:d076::1", node1.Address6)
 	})
 	})
 }
 }
 
 

+ 0 - 33
controllers/node.go

@@ -33,7 +33,6 @@ func nodeHandlers(r *mux.Router) {
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods("DELETE")
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods("DELETE")
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/approve", authorize(false, true, "user", http.HandlerFunc(uncordonNode))).Methods("POST")
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/approve", authorize(false, true, "user", http.HandlerFunc(uncordonNode))).Methods("POST")
 	r.HandleFunc("/api/nodes/{network}", nodeauth(checkFreeTierLimits(node_l, http.HandlerFunc(createNode)))).Methods("POST")
 	r.HandleFunc("/api/nodes/{network}", nodeauth(checkFreeTierLimits(node_l, http.HandlerFunc(createNode)))).Methods("POST")
-	r.HandleFunc("/api/nodes/adm/{network}/lastmodified", authorize(false, true, "network", http.HandlerFunc(getLastModified))).Methods("GET")
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods("POST")
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods("POST")
 }
 }
 
 
@@ -501,38 +500,6 @@ func getNode(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(response)
 	json.NewEncoder(w).Encode(response)
 }
 }
 
 
-// swagger:route GET /api/nodes/adm/{network}/lastmodified nodes getLastModified
-//
-// Get the time that a network of nodes was last modified.
-//
-//		Schemes: https
-//
-// 		Security:
-//   		oauth
-//
-//		Responses:
-//			200: nodeLastModifiedResponse
-// TODO: This needs to be refactored
-// Potential way to do this: On UpdateNode, set a new field for "LastModified"
-// If we go with the existing way, we need to at least set network.NodesLastModified on UpdateNode
-func getLastModified(w http.ResponseWriter, r *http.Request) {
-	// set header.
-	w.Header().Set("Content-Type", "application/json")
-
-	var params = mux.Vars(r)
-	networkName := params["network"]
-	network, err := logic.GetNetwork(networkName)
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"),
-			fmt.Sprintf("error fetching network [%s] info: %v", networkName, err))
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	logger.Log(2, r.Header.Get("user"), "called last modified")
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(network.NodesLastModified)
-}
-
 // swagger:route POST /api/nodes/{network} nodes createNode
 // swagger:route POST /api/nodes/{network} nodes createNode
 //
 //
 // Create a node on a network.
 // Create a node on a network.

+ 5 - 0
docker/Caddyfile

@@ -28,3 +28,8 @@ https://dashboard.NETMAKER_BASE_DOMAIN {
 https://api.NETMAKER_BASE_DOMAIN {
 https://api.NETMAKER_BASE_DOMAIN {
         reverse_proxy http://netmaker:8081
         reverse_proxy http://netmaker:8081
 }
 }
+
+# MQ
+wss://broker.NETMAKER_BASE_DOMAIN {
+        reverse_proxy ws://mq:8883
+}

+ 50 - 0
docker/Caddyfile-EE

@@ -0,0 +1,50 @@
+{
+        # LetsEncrypt account
+        email YOUR_EMAIL
+}
+
+# Dashboard
+https://dashboard.NETMAKER_BASE_DOMAIN {
+        # Apply basic security headers
+        header {
+                # Enable cross origin access to *.NETMAKER_BASE_DOMAIN
+                Access-Control-Allow-Origin *.NETMAKER_BASE_DOMAIN
+                # Enable HTTP Strict Transport Security (HSTS)
+                Strict-Transport-Security "max-age=31536000;"
+                # Enable cross-site filter (XSS) and tell browser to block detected attacks
+                X-XSS-Protection "1; mode=block"
+                # Disallow the site to be rendered within a frame on a foreign domain (clickjacking protection)
+                X-Frame-Options "SAMEORIGIN"
+                # Prevent search engines from indexing
+                X-Robots-Tag "none"
+                # Remove the server name
+                -Server
+        }
+
+        reverse_proxy http://netmaker-ui
+}
+
+# Netmaker Exporter
+https://netmaker-exporter.NETMAKER_BASE_DOMAIN {
+        reverse_proxy http://netmaker-exporter:8085
+}
+
+# Prometheus
+https://prometheus.NETMAKER_BASE_DOMAIN {
+        reverse_proxy http://prometheus:9090
+}
+
+# Grafana
+https://grafana.NETMAKER_BASE_DOMAIN {
+        reverse_proxy http://grafana:3000
+}
+
+# API
+https://api.NETMAKER_BASE_DOMAIN {
+        reverse_proxy http://netmaker:8081
+}
+
+# MQ
+wss://broker.NETMAKER_BASE_DOMAIN {
+        reverse_proxy ws://mq:8883
+}

+ 1 - 1
docker/Dockerfile-go-builder

@@ -1,4 +1,4 @@
-FROM golang:1.18.0-alpine3.15 
+FROM golang:1.19-alpine3.16 
 ARG version 
 ARG version 
 RUN apk add build-base
 RUN apk add build-base
 WORKDIR /app
 WORKDIR /app

+ 1 - 1
docker/Dockerfile-netclient-doks

@@ -3,7 +3,7 @@ FROM debian:buster as builder
 
 
 RUN apt update -y && apt install -y wget bash gcc musl-dev openssl golang git build-essential libmnl-dev iptables
 RUN apt update -y && apt install -y wget bash gcc musl-dev openssl golang git build-essential libmnl-dev iptables
 
 
-RUN wget -O go.tgz https://go.dev/dl/go1.18.linux-amd64.tar.gz
+RUN wget -O go.tgz https://go.dev/dl/go1.19.linux-amd64.tar.gz
 
 
 RUN tar -C /usr/local -xzf go.tgz
 RUN tar -C /usr/local -xzf go.tgz
 
 

+ 1 - 1
docker/Dockerfile-netclient-doks-uspace

@@ -3,7 +3,7 @@ FROM debian:buster as builder
 
 
 RUN apt update -y && apt install -y wget bash gcc musl-dev openssl golang git build-essential libmnl-dev iptables
 RUN apt update -y && apt install -y wget bash gcc musl-dev openssl golang git build-essential libmnl-dev iptables
 
 
-RUN wget -O go.tgz https://go.dev/dl/go1.18.linux-amd64.tar.gz
+RUN wget -O go.tgz https://go.dev/dl/go1.19.linux-amd64.tar.gz
 
 
 RUN tar -C /usr/local -xzf go.tgz
 RUN tar -C /usr/local -xzf go.tgz
 
 

+ 1 - 1
docker/Dockerfile-netclient-multiarch

@@ -9,7 +9,7 @@ ENV GO111MODULE=auto
 
 
 RUN GOOS=linux CGO_ENABLED=0 /usr/local/go/bin/go build -ldflags="-X 'main.version=${version}'" -o netclient-app netclient/main.go
 RUN GOOS=linux CGO_ENABLED=0 /usr/local/go/bin/go build -ldflags="-X 'main.version=${version}'" -o netclient-app netclient/main.go
 
 
-FROM alpine:3.15.2
+FROM alpine:3.16.2
 
 
 WORKDIR /root/
 WORKDIR /root/
 
 

+ 1 - 1
docker/Dockerfile-netclient-multiarch-userspace

@@ -24,7 +24,7 @@ RUN git clone https://git.zx2c4.com/wireguard-tools && \
     make && \
     make && \
     make install
     make install
 
 
-FROM alpine:3.13.6
+FROM alpine:3.16.2
 
 
 WORKDIR /root/
 WORKDIR /root/
 
 

+ 2 - 0
docker/mosquitto.conf

@@ -1,8 +1,10 @@
 per_listener_settings false
 per_listener_settings false
 listener 8883
 listener 8883
+protocol websockets
 allow_anonymous false
 allow_anonymous false
 
 
 listener 1883
 listener 1883
+protocol websockets
 allow_anonymous false
 allow_anonymous false
 
 
 plugin /usr/lib/mosquitto_dynamic_security.so
 plugin /usr/lib/mosquitto_dynamic_security.so

+ 2 - 2
controllers/networkusers.go → ee/ee_controllers/networkusers.go

@@ -1,4 +1,4 @@
-package controller
+package ee_controllers
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
@@ -14,7 +14,7 @@ import (
 	"github.com/gravitl/netmaker/models/promodels"
 	"github.com/gravitl/netmaker/models/promodels"
 )
 )
 
 
-func networkUsersHandlers(r *mux.Router) {
+func NetworkUsersHandlers(r *mux.Router) {
 	r.HandleFunc("/api/networkusers", logic.SecurityCheck(true, http.HandlerFunc(getAllNetworkUsers))).Methods("GET")
 	r.HandleFunc("/api/networkusers", logic.SecurityCheck(true, http.HandlerFunc(getAllNetworkUsers))).Methods("GET")
 	r.HandleFunc("/api/networkusers/{network}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkUsers))).Methods("GET")
 	r.HandleFunc("/api/networkusers/{network}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkUsers))).Methods("GET")
 	r.HandleFunc("/api/networkusers/{network}/{networkuser}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkUser))).Methods("GET")
 	r.HandleFunc("/api/networkusers/{network}/{networkuser}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkUser))).Methods("GET")

+ 2 - 2
controllers/usergroups.go → ee/ee_controllers/usergroups.go

@@ -1,4 +1,4 @@
-package controller
+package ee_controllers
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
@@ -13,7 +13,7 @@ import (
 	"github.com/gravitl/netmaker/models/promodels"
 	"github.com/gravitl/netmaker/models/promodels"
 )
 )
 
 
-func userGroupsHandlers(r *mux.Router) {
+func UserGroupsHandlers(r *mux.Router) {
 	r.HandleFunc("/api/usergroups", logic.SecurityCheck(true, http.HandlerFunc(getUserGroups))).Methods("GET")
 	r.HandleFunc("/api/usergroups", logic.SecurityCheck(true, http.HandlerFunc(getUserGroups))).Methods("GET")
 	r.HandleFunc("/api/usergroups/{usergroup}", logic.SecurityCheck(true, http.HandlerFunc(createUserGroup))).Methods("POST")
 	r.HandleFunc("/api/usergroups/{usergroup}", logic.SecurityCheck(true, http.HandlerFunc(createUserGroup))).Methods("POST")
 	r.HandleFunc("/api/usergroups/{usergroup}", logic.SecurityCheck(true, http.HandlerFunc(deleteUserGroup))).Methods("DELETE")
 	r.HandleFunc("/api/usergroups/{usergroup}", logic.SecurityCheck(true, http.HandlerFunc(deleteUserGroup))).Methods("DELETE")

+ 6 - 1
ee/initialize.go

@@ -17,7 +17,12 @@ import (
 func InitEE() {
 func InitEE() {
 	setIsEnterprise()
 	setIsEnterprise()
 	models.SetLogo(retrieveEELogo())
 	models.SetLogo(retrieveEELogo())
-	controller.HttpHandlers = append(controller.HttpHandlers, ee_controllers.MetricHandlers)
+	controller.HttpHandlers = append(
+		controller.HttpHandlers,
+		ee_controllers.MetricHandlers,
+		ee_controllers.NetworkUsersHandlers,
+		ee_controllers.UserGroupsHandlers,
+	)
 	logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {
 	logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {
 		// == License Handling ==
 		// == License Handling ==
 		ValidateLicense()
 		ValidateLicense()

+ 2 - 2
ee/license.go

@@ -8,7 +8,7 @@ import (
 	"crypto/rand"
 	"crypto/rand"
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"math"
 	"math"
 	"net/http"
 	"net/http"
 
 
@@ -200,7 +200,7 @@ func validateLicenseKey(encryptedData []byte, publicKey *[32]byte) ([]byte, erro
 			return nil, fmt.Errorf("could not validate license")
 			return nil, fmt.Errorf("could not validate license")
 		} // if you received a 200 cache the response locally
 		} // if you received a 200 cache the response locally
 
 
-		body, err = ioutil.ReadAll(validateResponse.Body)
+		body, err = io.ReadAll(validateResponse.Body)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}

+ 7 - 6
go.mod

@@ -1,21 +1,21 @@
 module github.com/gravitl/netmaker
 module github.com/gravitl/netmaker
 
 
-go 1.18
+go 1.19
 
 
 require (
 require (
-	github.com/eclipse/paho.mqtt.golang v1.4.1
+	github.com/eclipse/paho.mqtt.golang v1.4.2
 	github.com/go-playground/validator/v10 v10.11.1
 	github.com/go-playground/validator/v10 v10.11.1
 	github.com/golang-jwt/jwt/v4 v4.4.2
 	github.com/golang-jwt/jwt/v4 v4.4.2
 	github.com/google/uuid v1.3.0
 	github.com/google/uuid v1.3.0
 	github.com/gorilla/handlers v1.5.1
 	github.com/gorilla/handlers v1.5.1
 	github.com/gorilla/mux v1.8.0
 	github.com/gorilla/mux v1.8.0
 	github.com/lib/pq v1.10.7
 	github.com/lib/pq v1.10.7
-	github.com/mattn/go-sqlite3 v1.14.15
+	github.com/mattn/go-sqlite3 v1.14.16
 	github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f
 	github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
-	github.com/stretchr/testify v1.8.0
+	github.com/stretchr/testify v1.8.1
 	github.com/txn2/txeh v1.3.0
 	github.com/txn2/txeh v1.3.0
-	github.com/urfave/cli/v2 v2.20.2
+	github.com/urfave/cli/v2 v2.23.5
 	golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
 	golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
 	golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
 	golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
 	golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094
 	golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094
@@ -30,7 +30,7 @@ require (
 
 
 require (
 require (
 	filippo.io/edwards25519 v1.0.0
 	filippo.io/edwards25519 v1.0.0
-	fyne.io/fyne/v2 v2.2.3
+	fyne.io/fyne/v2 v2.2.4
 	github.com/c-robinson/iplib v1.0.3
 	github.com/c-robinson/iplib v1.0.3
 	github.com/cloverstd/tcping v0.1.1
 	github.com/cloverstd/tcping v0.1.1
 	github.com/go-ping/ping v1.1.0
 	github.com/go-ping/ping v1.1.0
@@ -40,6 +40,7 @@ require (
 )
 )
 
 
 require (
 require (
+	github.com/agnivade/levenshtein v1.1.1
 	github.com/coreos/go-oidc/v3 v3.4.0
 	github.com/coreos/go-oidc/v3 v3.4.0
 	github.com/google/gopacket v1.1.19
 	github.com/google/gopacket v1.1.19
 	github.com/gorilla/websocket v1.5.0
 	github.com/gorilla/websocket v1.5.0

+ 17 - 9
go.sum

@@ -61,8 +61,8 @@ cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
 filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
 filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
 filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
-fyne.io/fyne/v2 v2.2.3 h1:Umi3vVVW8XnWWPJmMkhIWQOMU/jxB1OqpWVUmjhODD0=
-fyne.io/fyne/v2 v2.2.3/go.mod h1:MBoGuHzLLSXdQOWFAwWhIhYTEMp33zqtGCReSWhaQTA=
+fyne.io/fyne/v2 v2.2.4 h1:izyiDUjJYAB7B/MST7M9GDs+mQ0CwDgRZTiVJZQoEe4=
+fyne.io/fyne/v2 v2.2.4/go.mod h1:MBoGuHzLLSXdQOWFAwWhIhYTEMp33zqtGCReSWhaQTA=
 fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93 h1:V2IC9t0Zj9Ur6qDbfhUuzVmIvXKFyxZXRJyigUvovs4=
 fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93 h1:V2IC9t0Zj9Ur6qDbfhUuzVmIvXKFyxZXRJyigUvovs4=
 fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE=
 fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE=
 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
@@ -73,8 +73,12 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
 github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
 github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
+github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
 github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
 github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
+github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@@ -118,6 +122,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
+github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
 github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
 github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
 github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE=
 github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE=
@@ -126,8 +132,8 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh
 github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
 github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
 github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/eclipse/paho.mqtt.golang v1.4.1 h1:tUSpviiL5G3P9SZZJPC4ZULZJsxQKXxfENpMvdbAXAI=
-github.com/eclipse/paho.mqtt.golang v1.4.1/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
+github.com/eclipse/paho.mqtt.golang v1.4.2 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6rNA5PM12m4=
+github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -340,8 +346,8 @@ github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPK
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
 github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
-github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
-github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
+github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
+github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
 github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo=
 github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo=
 github.com/mdlayher/genetlink v1.2.0 h1:4yrIkRV5Wfk1WfpWTcoOlGmsWgQj3OtQN9ZsbrE+XtU=
 github.com/mdlayher/genetlink v1.2.0 h1:4yrIkRV5Wfk1WfpWTcoOlGmsWgQj3OtQN9ZsbrE+XtU=
 github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ=
 github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ=
@@ -437,6 +443,7 @@ github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 h1:m59mIOBO4kfcNCE
 github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
 github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -445,8 +452,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
 github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
-github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA=
 github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA=
 github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg=
 github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg=
@@ -455,8 +463,8 @@ github.com/txn2/txeh v1.3.0/go.mod h1:O7M6gUTPeMF+vsa4c4Ipx3JDkOYrruB1Wry8QRsMcw
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/cli/v2 v2.4.0/go.mod h1:NX9W0zmTvedE5oDoOMs2RTC8RvdK98NTYZE5LbaEYPg=
 github.com/urfave/cli/v2 v2.4.0/go.mod h1:NX9W0zmTvedE5oDoOMs2RTC8RvdK98NTYZE5LbaEYPg=
-github.com/urfave/cli/v2 v2.20.2 h1:dKA0LUjznZpwmmbrc0pOgcLTEilnHeM8Av9Yng77gHM=
-github.com/urfave/cli/v2 v2.20.2/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
+github.com/urfave/cli/v2 v2.23.5 h1:xbrU7tAYviSpqeR3X4nEFWUdB/uDZ6DE+HxmRU7Xtyw=
+github.com/urfave/cli/v2 v2.23.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
 github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
 github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
 github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=

+ 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.16.2
+        image: gravitl/netclient:v0.17.0
         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.16.2
+        image: gravitl/netclient:v0.17.0
         env:
         env:
         - name: TOKEN
         - name: TOKEN
           value: "TOKEN_VALUE"
           value: "TOKEN_VALUE"

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

@@ -83,7 +83,7 @@ spec:
           value: "Kubernetes"
           value: "Kubernetes"
         - name: VERBOSITY
         - name: VERBOSITY
           value: "3"
           value: "3"
-        image: gravitl/netmaker:v0.16.2
+        image: gravitl/netmaker:v0.17.0
         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.16.2
+        image: gravitl/netmaker-ui:v0.17.0
         ports:
         ports:
         - containerPort: 443
         - containerPort: 443
         env:
         env:

+ 3 - 0
logic/auth.go

@@ -282,6 +282,9 @@ func UpdateUser(userchange models.User, user models.User) (models.User, error) {
 
 
 		user.Password = userchange.Password
 		user.Password = userchange.Password
 	}
 	}
+	if userchange.IsAdmin != user.IsAdmin {
+		user.IsAdmin = userchange.IsAdmin
+	}
 
 
 	err := ValidateUser(user)
 	err := ValidateUser(user)
 	if err != nil {
 	if err != nil {

+ 1 - 1
logic/gateway.go

@@ -324,7 +324,7 @@ func firewallNFTCommandsCreateIngress(networkInterface string) (string, string)
 	postUp += "nft add rule ip filter FORWARD oifname " + networkInterface + " counter accept ; "
 	postUp += "nft add rule ip filter FORWARD oifname " + networkInterface + " counter accept ; "
 	postUp += "nft add table nat ; "
 	postUp += "nft add table nat ; "
 	postUp += "nft add chain nat postrouting ; "
 	postUp += "nft add chain nat postrouting ; "
-	postUp += "nft add rule ip nat postrouting oifname " + networkInterface + " counter masquerade"
+	postUp += "nft add rule ip nat postrouting oifname " + networkInterface + " counter masquerade ; "
 
 
 	// doesn't remove potentially empty tables or chains
 	// doesn't remove potentially empty tables or chains
 	postDown := "nft flush table filter ; "
 	postDown := "nft flush table filter ; "

+ 5 - 3
logic/networks.go

@@ -267,14 +267,16 @@ func UniqueAddress6(networkName string, reverse bool) (string, error) {
 		return "666", err
 		return "666", err
 	}
 	}
 	net6 := iplib.Net6FromStr(network.AddressRange6)
 	net6 := iplib.Net6FromStr(network.AddressRange6)
-	newAddrs := net6.FirstAddress()
 
 
+	newAddrs, err := net6.NextIP(net6.FirstAddress())
 	if reverse {
 	if reverse {
-		newAddrs = net6.LastAddress()
+		newAddrs, err = net6.PreviousIP(net6.LastAddress())
+	}
+	if err != nil {
+		return "", err
 	}
 	}
 
 
 	for {
 	for {
-
 		if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, true) &&
 		if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, true) &&
 			IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, true) {
 			IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, true) {
 			return newAddrs.String(), nil
 			return newAddrs.String(), nil

+ 0 - 2
logic/peers.go

@@ -257,8 +257,6 @@ func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
 				if peer.LocalListenPort != 0 {
 				if peer.LocalListenPort != 0 {
 					peer.ListenPort = peer.LocalListenPort
 					peer.ListenPort = peer.LocalListenPort
 				}
 				}
-			} else {
-				continue
 			}
 			}
 		}
 		}
 
 

+ 1 - 1
logic/security.go

@@ -162,7 +162,7 @@ func UserPermissions(reqAdmin bool, netname string, token string) ([]string, str
 	if len(netname) > 0 && (!authenticateNetworkUser(netname, userNetworks) || len(userNetworks) == 0) {
 	if len(netname) > 0 && (!authenticateNetworkUser(netname, userNetworks) || len(userNetworks) == 0) {
 		return nil, username, Unauthorized_Err
 		return nil, username, Unauthorized_Err
 	}
 	}
-	if !pro.IsUserNetAdmin(netname, username) {
+	if isEE && !pro.IsUserNetAdmin(netname, username) {
 		return nil, "", Unauthorized_Err
 		return nil, "", Unauthorized_Err
 	}
 	}
 	return userNetworks, username, nil
 	return userNetworks, username, nil

+ 1 - 1
logic/zombie.go

@@ -63,7 +63,7 @@ func ManageZombies(ctx context.Context) {
 				logger.Log(3, "no zombies found")
 				logger.Log(3, "no zombies found")
 			}
 			}
 		case <-time.After(time.Second * ZOMBIE_TIMEOUT):
 		case <-time.After(time.Second * ZOMBIE_TIMEOUT):
-			logger.Log(0, "checking for zombie nodes")
+			logger.Log(3, "checking for zombie nodes")
 			if len(zombies) > 0 {
 			if len(zombies) > 0 {
 				for i := len(zombies) - 1; i >= 0; i-- {
 				for i := len(zombies) - 1; i >= 0; i-- {
 					node, err := GetNodeByID(zombies[i])
 					node, err := GetNodeByID(zombies[i])

+ 1 - 1
main.go

@@ -69,7 +69,7 @@ func initialize() { // Client Mode Prereq Check
 	}
 	}
 
 
 	if err = database.InitializeDatabase(); err != nil {
 	if err = database.InitializeDatabase(); err != nil {
-		logger.FatalLog("Error connecting to database")
+		logger.FatalLog("Error connecting to database: ", err.Error())
 	}
 	}
 	logger.Log(0, "database successfully connected")
 	logger.Log(0, "database successfully connected")
 	if err = logic.AddServerIDIfNotPresent(); err != nil {
 	if err = logic.AddServerIDIfNotPresent(); err != nil {

+ 11 - 3
netclient/bin-maker.sh

@@ -19,8 +19,17 @@ function build
     if [ "$_goarch" == "arm" ] && [ "$_goarm" == "" ]; then
     if [ "$_goarch" == "arm" ] && [ "$_goarm" == "" ]; then
 	    build $_goarch $_goose 5 && build $_goarch $_goose 6 && build $_goarch $_goose 7
 	    build $_goarch $_goose 5 && build $_goarch $_goose 6 && build $_goarch $_goose 7
     else
     else
-        echo $_out
-        GOARM=$_goarm GOARCH=$_goarch GOOS=$_goose GOHOSTARCH=$__HOST_ARCH CGO_ENABLED=0 go build -ldflags="-X 'main.version=$VERSION'" -o $_out
+        
+        if [[ $_goarch == mips* ]]; then
+            #At present GOMIPS64 based binaries are not generated through this script, more details about GOMIPS environment variables in https://go.dev/doc/asm#mips .
+            echo $_out-softfloat
+            GOARM=$_goarm GOMIPS=softfloat GOARCH=$_goarch GOOS=$_goose GOHOSTARCH=$__HOST_ARCH CGO_ENABLED=0 go build -ldflags="-X 'main.version=$VERSION'" -o $_out-softfloat
+            echo $_out
+            GOARM=$_goarm GOARCH=$_goarch GOOS=$_goose GOHOSTARCH=$__HOST_ARCH CGO_ENABLED=0 go build -ldflags="-X 'main.version=$VERSION'" -o $_out
+        else
+            echo $_out
+            GOARM=$_goarm GOARCH=$_goarch GOOS=$_goose GOHOSTARCH=$__HOST_ARCH CGO_ENABLED=0 go build -ldflags="-X 'main.version=$VERSION'" -o $_out
+        fi
     fi
     fi
 }
 }
 
 
@@ -31,4 +40,3 @@ for arch in ${__freebsd[*]}; do build "$arch" "freebsd"; done
 for arch in ${__darwin[*]}; do build "$arch" "darwin"; done
 for arch in ${__darwin[*]}; do build "$arch" "darwin"; done
 
 
 for arch in ${__windows[*]}; do build "$arch" "windows"; done
 for arch in ${__windows[*]}; do build "$arch" "windows"; done
-

+ 2 - 2
netclient/functions/daemon.go

@@ -290,7 +290,7 @@ func setupMQTTSingleton(cfg *config.ClientConfig) error {
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("could not read secrets file %w", err)
 		return fmt.Errorf("could not read secrets file %w", err)
 	}
 	}
-	opts.AddBroker("mqtts://" + server + ":" + port)
+	opts.AddBroker("wss://" + server + ":" + port)
 	opts.SetUsername(cfg.Node.ID)
 	opts.SetUsername(cfg.Node.ID)
 	opts.SetPassword(string(pass))
 	opts.SetPassword(string(pass))
 	mqclient = mqtt.NewClient(opts)
 	mqclient = mqtt.NewClient(opts)
@@ -317,7 +317,7 @@ func setupMQTT(cfg *config.ClientConfig) error {
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("could not read secrets file %w", err)
 		return fmt.Errorf("could not read secrets file %w", err)
 	}
 	}
-	opts.AddBroker(fmt.Sprintf("mqtts://%s:%s", server, port))
+	opts.AddBroker(fmt.Sprintf("wss://%s:%s", server, port))
 	opts.SetUsername(cfg.Node.ID)
 	opts.SetUsername(cfg.Node.ID)
 	opts.SetPassword(string(pass))
 	opts.SetPassword(string(pass))
 	opts.SetClientID(ncutils.MakeRandomString(23))
 	opts.SetClientID(ncutils.MakeRandomString(23))

+ 37 - 1
netclient/gui/gui.go

@@ -10,6 +10,8 @@ import (
 	"fyne.io/fyne/v2/container"
 	"fyne.io/fyne/v2/container"
 	"fyne.io/fyne/v2/theme"
 	"fyne.io/fyne/v2/theme"
 	"fyne.io/fyne/v2/widget"
 	"fyne.io/fyne/v2/widget"
+	"github.com/agnivade/levenshtein"
+
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/netclient/functions"
 	"github.com/gravitl/netmaker/netclient/functions"
 	"github.com/gravitl/netmaker/netclient/gui/components"
 	"github.com/gravitl/netmaker/netclient/gui/components"
@@ -48,15 +50,48 @@ func Run(networks []string) error {
 	views.SetView(views.NetDetails, netDetailsViews)
 	views.SetView(views.NetDetails, netDetailsViews)
 	window.SetFixedSize(false)
 	window.SetFixedSize(false)
 
 
+	searchBar := widget.NewEntry()
+	searchBar.PlaceHolder = "Search a Network ..."
+	searchBar.TextStyle = fyne.TextStyle{
+		Italic: true,
+	}
+	searchBar.OnChanged = func(text string) {
+		if text == "" {
+			networkView = container.NewVScroll(views.GetNetworksView(networks))
+			networkView.SetMinSize(fyne.NewSize(400, 300))
+			views.RefreshComponent(views.Networks, networkView)
+			views.ShowView(views.Networks)
+			return
+		}
+
+		opts := []string{}
+		for _, n := range networks {
+			r := levenshtein.ComputeDistance(text, n)
+			if r <= 2 {
+				opts = append(opts, n)
+			}
+		}
+
+		// fmt.Println(opts)
+		networkView = container.NewVScroll(views.GetNetworksView(opts))
+		networkView.SetMinSize(fyne.NewSize(400, 300))
+		views.RefreshComponent(views.Networks, networkView)
+		views.ShowView(views.Networks)
+		opts = nil
+	}
+
 	toolbar := container.NewCenter(widget.NewToolbar(
 	toolbar := container.NewCenter(widget.NewToolbar(
 		components.NewToolbarLabelButton("Networks", theme.HomeIcon(), func() {
 		components.NewToolbarLabelButton("Networks", theme.HomeIcon(), func() {
+			searchBar.Show()
 			views.ShowView(views.Networks)
 			views.ShowView(views.Networks)
 			views.ClearNotification()
 			views.ClearNotification()
 		}, components.Blue_color),
 		}, components.Blue_color),
 		components.NewToolbarLabelButton("Join new", theme.ContentAddIcon(), func() {
 		components.NewToolbarLabelButton("Join new", theme.ContentAddIcon(), func() {
+			searchBar.Hide()
 			views.ShowView(views.Join)
 			views.ShowView(views.Join)
 		}, components.Gravitl_color),
 		}, components.Gravitl_color),
 		components.NewToolbarLabelButton("Uninstall", theme.ErrorIcon(), func() {
 		components.NewToolbarLabelButton("Uninstall", theme.ErrorIcon(), func() {
+			searchBar.Hide()
 			confirmView := views.GetConfirmation("Confirm Netclient uninstall?", func() {
 			confirmView := views.GetConfirmation("Confirm Netclient uninstall?", func() {
 				views.ShowView(views.Networks)
 				views.ShowView(views.Networks)
 			}, func() {
 			}, func() {
@@ -96,8 +131,9 @@ func Run(networks []string) error {
 	views.CurrentContent = container.NewVBox()
 	views.CurrentContent = container.NewVBox()
 
 
 	views.CurrentContent.Add(container.NewGridWithRows(
 	views.CurrentContent.Add(container.NewGridWithRows(
-		1,
+		2,
 		toolbar,
 		toolbar,
+		searchBar,
 	))
 	))
 	views.CurrentContent.Add(views.GetView(views.Networks))
 	views.CurrentContent.Add(views.GetView(views.Networks))
 	views.CurrentContent.Add(views.GetView(views.NetDetails))
 	views.CurrentContent.Add(views.GetView(views.NetDetails))

+ 1 - 1
netclient/netclient.exe.manifest.xml

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
     <assemblyIdentity
     <assemblyIdentity
-            version="0.16.2.0"
+            version="0.17.0.0"
             processorArchitecture="*"
             processorArchitecture="*"
             name="netclient.exe"
             name="netclient.exe"
             type="win32"
             type="win32"

+ 5 - 5
netclient/versioninfo.json

@@ -2,14 +2,14 @@
     "FixedFileInfo": {
     "FixedFileInfo": {
         "FileVersion": {
         "FileVersion": {
             "Major": 0,
             "Major": 0,
-            "Minor": 16,
-            "Patch": 2,
+            "Minor": 17,
+            "Patch": 0,
             "Build": 0
             "Build": 0
         },
         },
         "ProductVersion": {
         "ProductVersion": {
             "Major": 0,
             "Major": 0,
-            "Minor": 16,
-            "Patch": 2,
+            "Minor": 17,
+            "Patch": 0,
             "Build": 0
             "Build": 0
         },
         },
         "FileFlagsMask": "3f",
         "FileFlagsMask": "3f",
@@ -29,7 +29,7 @@
         "OriginalFilename": "",
         "OriginalFilename": "",
         "PrivateBuild": "",
         "PrivateBuild": "",
         "ProductName": "Netclient",
         "ProductName": "Netclient",
-        "ProductVersion": "v0.16.2.0",
+        "ProductVersion": "v0.17.0.0",
         "SpecialBuild": ""
         "SpecialBuild": ""
     },
     },
     "VarFileInfo": {
     "VarFileInfo": {

+ 18 - 10
netclient/wireguard/common.go

@@ -462,21 +462,29 @@ func UpdateWgInterface(file, privateKey, nameserver string, node models.Node) er
 	//}
 	//}
 	//need to split postup/postdown because ini lib adds a quotes which breaks freebsd
 	//need to split postup/postdown because ini lib adds a quotes which breaks freebsd
 	if node.PostUp != "" {
 	if node.PostUp != "" {
-		parts := strings.Split(node.PostUp, " ; ")
-		for i, part := range parts {
-			if i == 0 {
-				wireguard.Section(section_interface).Key("PostUp").SetValue(part)
+		if node.OS == "freebsd" {
+			parts := strings.Split(node.PostUp, " ; ")
+			for i, part := range parts {
+				if i == 0 {
+					wireguard.Section(section_interface).Key("PostUp").SetValue(part)
+				}
+				wireguard.Section(section_interface).Key("PostUp").AddShadow(part)
 			}
 			}
-			wireguard.Section(section_interface).Key("PostUp").AddShadow(part)
+		} else {
+			wireguard.Section(section_interface).Key("PostUp").SetValue(node.PostUp)
 		}
 		}
 	}
 	}
 	if node.PostDown != "" {
 	if node.PostDown != "" {
-		parts := strings.Split(node.PostDown, " ; ")
-		for i, part := range parts {
-			if i == 0 {
-				wireguard.Section(section_interface).Key("PostDown").SetValue(part)
+		if node.OS == "freebsd" {
+			parts := strings.Split(node.PostDown, " ; ")
+			for i, part := range parts {
+				if i == 0 {
+					wireguard.Section(section_interface).Key("PostDown").SetValue(part)
+				}
+				wireguard.Section(section_interface).Key("PostDown").AddShadow(part)
 			}
 			}
-			wireguard.Section(section_interface).Key("PostDown").AddShadow(part)
+		} else {
+			wireguard.Section(section_interface).Key("PostDown").SetValue(node.PostDown)
 		}
 		}
 	}
 	}
 	if node.MTU != 0 {
 	if node.MTU != 0 {

+ 6 - 2
nm-proxy/common/common.go

@@ -14,12 +14,16 @@ var IsIngressGateway bool
 var IsRelayed bool
 var IsRelayed bool
 var IsServer bool
 var IsServer bool
 var InterfaceName string
 var InterfaceName string
+var BehindNAT bool
 
 
-var WgIFaceMap = make(map[string]models.WgIfaceConf)
+var WgIfaceMap = models.WgIfaceConf{
+	Iface:   nil,
+	PeerMap: make(map[string]*models.Conn),
+}
 
 
 var PeerKeyHashMap = make(map[string]models.RemotePeer)
 var PeerKeyHashMap = make(map[string]models.RemotePeer)
 
 
-var WgIfaceKeyMap = make(map[string]models.RemotePeer)
+//var WgIfaceKeyMap = make(map[string]models.RemotePeer)
 
 
 var RelayPeerMap = make(map[string]map[string]models.RemotePeer)
 var RelayPeerMap = make(map[string]map[string]models.RemotePeer)
 
 

+ 22 - 0
nm-proxy/common/functions.go

@@ -0,0 +1,22 @@
+package common
+
+import (
+	"github.com/gravitl/netmaker/nm-proxy/models"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+)
+
+func GetPeer(peerKey wgtypes.Key) (*models.Conn, bool) {
+	var peerInfo *models.Conn
+	var found bool
+	peerInfo, found = WgIfaceMap.PeerMap[peerKey.String()]
+	peerInfo.Mutex.RLock()
+	defer peerInfo.Mutex.RUnlock()
+	return peerInfo, found
+
+}
+
+func UpdatePeer(peer *models.Conn) {
+	peer.Mutex.Lock()
+	defer peer.Mutex.Unlock()
+	WgIfaceMap.PeerMap[peer.Key.String()] = peer
+}

+ 40 - 56
nm-proxy/manager/manager.go

@@ -13,7 +13,6 @@ import (
 	"github.com/gravitl/netmaker/nm-proxy/common"
 	"github.com/gravitl/netmaker/nm-proxy/common"
 	"github.com/gravitl/netmaker/nm-proxy/models"
 	"github.com/gravitl/netmaker/nm-proxy/models"
 	peerpkg "github.com/gravitl/netmaker/nm-proxy/peer"
 	peerpkg "github.com/gravitl/netmaker/nm-proxy/peer"
-	"github.com/gravitl/netmaker/nm-proxy/proxy"
 	"github.com/gravitl/netmaker/nm-proxy/wg"
 	"github.com/gravitl/netmaker/nm-proxy/wg"
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 )
 )
@@ -99,8 +98,8 @@ func (m *ManagerAction) DeleteInterface() {
 			return
 			return
 		}
 		}
 	}
 	}
-	if wgProxyConf, ok := common.WgIFaceMap[m.Payload.InterfaceName]; ok {
-		cleanUpInterface(wgProxyConf)
+	if common.WgIfaceMap.Iface.Name == m.Payload.InterfaceName {
+		cleanUpInterface()
 	}
 	}
 
 
 }
 }
@@ -139,12 +138,15 @@ func (m *ManagerAction) RelayPeers() {
 	}
 	}
 }
 }
 
 
-func cleanUpInterface(ifaceConf models.WgIfaceConf) {
-	log.Println("########------------>  CLEANING UP: ", ifaceConf.Iface.Name)
-	for _, peerI := range ifaceConf.PeerMap {
+func cleanUpInterface() {
+	log.Println("########------------>  CLEANING UP: ", common.WgIfaceMap.Iface.Name)
+	for _, peerI := range common.WgIfaceMap.PeerMap {
+		peerI.Mutex.Lock()
 		peerI.StopConn()
 		peerI.StopConn()
+		peerI.Mutex.Unlock()
+		delete(common.WgIfaceMap.PeerMap, peerI.Key.String())
 	}
 	}
-	delete(common.WgIFaceMap, ifaceConf.Iface.Name)
+	common.WgIfaceMap.PeerMap = make(map[string]*models.Conn)
 }
 }
 
 
 func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
 func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
@@ -169,9 +171,8 @@ func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
 		log.Println("Failed init new interface: ", err)
 		log.Println("Failed init new interface: ", err)
 		return nil, err
 		return nil, err
 	}
 	}
-	var wgProxyConf models.WgIfaceConf
-	var ok bool
-	if wgProxyConf, ok = common.WgIFaceMap[m.Payload.InterfaceName]; !ok {
+
+	if common.WgIfaceMap.Iface == nil {
 		for i := len(m.Payload.Peers) - 1; i >= 0; i-- {
 		for i := len(m.Payload.Peers) - 1; i >= 0; i-- {
 			if !m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].Proxy {
 			if !m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].Proxy {
 				log.Println("-----------> skipping peer, proxy is off: ", m.Payload.Peers[i].PublicKey)
 				log.Println("-----------> skipping peer, proxy is off: ", m.Payload.Peers[i].PublicKey)
@@ -182,8 +183,11 @@ func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
 				continue
 				continue
 			}
 			}
 		}
 		}
+		common.WgIfaceMap.Iface = wgIface.Device
+		common.WgIfaceMap.IfaceKeyHash = fmt.Sprintf("%x", md5.Sum([]byte(wgIface.Device.PublicKey.String())))
 		return wgIface, nil
 		return wgIface, nil
 	}
 	}
+	wgProxyConf := common.WgIfaceMap
 	if m.Payload.IsRelay {
 	if m.Payload.IsRelay {
 		m.RelayPeers()
 		m.RelayPeers()
 	}
 	}
@@ -191,7 +195,7 @@ func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
 	// check if node is getting relayed
 	// check if node is getting relayed
 	if common.IsRelayed != m.Payload.IsRelayed {
 	if common.IsRelayed != m.Payload.IsRelayed {
 		common.IsRelayed = m.Payload.IsRelayed
 		common.IsRelayed = m.Payload.IsRelayed
-		cleanUpInterface(wgProxyConf)
+		cleanUpInterface()
 		return wgIface, nil
 		return wgIface, nil
 	}
 	}
 
 
@@ -199,7 +203,7 @@ func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
 	// check if listen port has changed
 	// check if listen port has changed
 	if wgIface.Device.ListenPort != wgProxyConf.Iface.ListenPort {
 	if wgIface.Device.ListenPort != wgProxyConf.Iface.ListenPort {
 		// reset proxy for this interface
 		// reset proxy for this interface
-		cleanUpInterface(wgProxyConf)
+		cleanUpInterface()
 		return wgIface, nil
 		return wgIface, nil
 	}
 	}
 	// check device conf different from proxy
 	// check device conf different from proxy
@@ -208,6 +212,7 @@ func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
 	for _, currPeerI := range wgProxyConf.Iface.Peers {
 	for _, currPeerI := range wgProxyConf.Iface.Peers {
 		if _, ok := m.Payload.PeerMap[currPeerI.PublicKey.String()]; !ok {
 		if _, ok := m.Payload.PeerMap[currPeerI.PublicKey.String()]; !ok {
 			if val, ok := wgProxyConf.PeerMap[currPeerI.PublicKey.String()]; ok {
 			if val, ok := wgProxyConf.PeerMap[currPeerI.PublicKey.String()]; ok {
+				val.Mutex.Lock()
 				if val.IsAttachedExtClient {
 				if val.IsAttachedExtClient {
 					log.Println("------> Deleting ExtClient Watch Thread: ", currPeerI.PublicKey.String())
 					log.Println("------> Deleting ExtClient Watch Thread: ", currPeerI.PublicKey.String())
 					if val, ok := common.ExtClientsWaitTh[currPeerI.PublicKey.String()]; ok {
 					if val, ok := common.ExtClientsWaitTh[currPeerI.PublicKey.String()]; ok {
@@ -215,9 +220,11 @@ func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
 						delete(common.ExtClientsWaitTh, currPeerI.PublicKey.String())
 						delete(common.ExtClientsWaitTh, currPeerI.PublicKey.String())
 					}
 					}
 					log.Println("-----> Deleting Ext Client from Src Ip Map: ", currPeerI.PublicKey.String())
 					log.Println("-----> Deleting Ext Client from Src Ip Map: ", currPeerI.PublicKey.String())
-					delete(common.ExtSourceIpMap, val.PeerConf.Endpoint.String())
+					delete(common.ExtSourceIpMap, val.Config.PeerConf.Endpoint.String())
 				}
 				}
 				val.StopConn()
 				val.StopConn()
+				val.Mutex.Unlock()
+				delete(wgProxyConf.PeerMap, currPeerI.PublicKey.String())
 			}
 			}
 
 
 			// delete peer from interface
 			// delete peer from interface
@@ -225,14 +232,15 @@ func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
 			if err := wgIface.RemovePeer(currPeerI.PublicKey.String()); err != nil {
 			if err := wgIface.RemovePeer(currPeerI.PublicKey.String()); err != nil {
 				log.Println("failed to remove peer: ", currPeerI.PublicKey.String(), err)
 				log.Println("failed to remove peer: ", currPeerI.PublicKey.String(), err)
 			}
 			}
+
 			delete(common.PeerKeyHashMap, fmt.Sprintf("%x", md5.Sum([]byte(currPeerI.PublicKey.String()))))
 			delete(common.PeerKeyHashMap, fmt.Sprintf("%x", md5.Sum([]byte(currPeerI.PublicKey.String()))))
-			delete(wgProxyConf.PeerMap, currPeerI.PublicKey.String())
 
 
 		}
 		}
 	}
 	}
 	for i := len(m.Payload.Peers) - 1; i >= 0; i-- {
 	for i := len(m.Payload.Peers) - 1; i >= 0; i-- {
 
 
 		if currentPeer, ok := wgProxyConf.PeerMap[m.Payload.Peers[i].PublicKey.String()]; ok {
 		if currentPeer, ok := wgProxyConf.PeerMap[m.Payload.Peers[i].PublicKey.String()]; ok {
+			currentPeer.Mutex.Lock()
 			if currentPeer.IsAttachedExtClient {
 			if currentPeer.IsAttachedExtClient {
 				m.Payload.Peers = append(m.Payload.Peers[:i], m.Payload.Peers[i+1:]...)
 				m.Payload.Peers = append(m.Payload.Peers[:i], m.Payload.Peers[i+1:]...)
 				continue
 				continue
@@ -242,7 +250,7 @@ func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
 
 
 				// cleanup proxy connections for the peer
 				// cleanup proxy connections for the peer
 				currentPeer.StopConn()
 				currentPeer.StopConn()
-				delete(wgProxyConf.PeerMap, currentPeer.Key)
+				delete(wgProxyConf.PeerMap, currentPeer.Key.String())
 				// update the peer with actual endpoint
 				// update the peer with actual endpoint
 				if err := wgIface.Update(m.Payload.Peers[i], false); err != nil {
 				if err := wgIface.Update(m.Payload.Peers[i], false); err != nil {
 					log.Println("falied to update peer: ", err)
 					log.Println("falied to update peer: ", err)
@@ -252,13 +260,13 @@ func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
 
 
 			}
 			}
 			// check if peer is not connected to proxy
 			// check if peer is not connected to proxy
-			devPeer, err := wg.GetPeer(m.Payload.InterfaceName, currentPeer.Key)
+			devPeer, err := wg.GetPeer(m.Payload.InterfaceName, currentPeer.Key.String())
 			if err == nil {
 			if err == nil {
-				log.Printf("---------> COMAPRING ENDPOINT: DEV: %s, Proxy: %s", devPeer.Endpoint.String(), currentPeer.LocalConn.LocalAddr().String())
-				if devPeer.Endpoint.String() != currentPeer.LocalConn.LocalAddr().String() {
+				log.Printf("---------> COMAPRING ENDPOINT: DEV: %s, Proxy: %s", devPeer.Endpoint.String(), currentPeer.Config.LocalConnAddr.String())
+				if devPeer.Endpoint.String() != currentPeer.Config.LocalConnAddr.String() {
 					log.Println("---------> endpoint is not set to proxy: ", currentPeer.Key)
 					log.Println("---------> endpoint is not set to proxy: ", currentPeer.Key)
 					currentPeer.StopConn()
 					currentPeer.StopConn()
-					delete(wgProxyConf.PeerMap, currentPeer.Key)
+					delete(wgProxyConf.PeerMap, currentPeer.Key.String())
 					continue
 					continue
 				}
 				}
 			}
 			}
@@ -266,7 +274,7 @@ func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
 			if currentPeer.IsRelayed != m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].IsRelayed {
 			if currentPeer.IsRelayed != m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].IsRelayed {
 				log.Println("---------> peer relay status has been changed: ", currentPeer.Key)
 				log.Println("---------> peer relay status has been changed: ", currentPeer.Key)
 				currentPeer.StopConn()
 				currentPeer.StopConn()
-				delete(wgProxyConf.PeerMap, currentPeer.Key)
+				delete(wgProxyConf.PeerMap, currentPeer.Key.String())
 				continue
 				continue
 			}
 			}
 			// check if relay endpoint has been changed
 			// check if relay endpoint has been changed
@@ -275,28 +283,29 @@ func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
 				currentPeer.RelayedEndpoint.String() != m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].RelayedTo.String() {
 				currentPeer.RelayedEndpoint.String() != m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].RelayedTo.String() {
 				log.Println("---------> peer relay endpoint has been changed: ", currentPeer.Key)
 				log.Println("---------> peer relay endpoint has been changed: ", currentPeer.Key)
 				currentPeer.StopConn()
 				currentPeer.StopConn()
-				delete(wgProxyConf.PeerMap, currentPeer.Key)
+				delete(wgProxyConf.PeerMap, currentPeer.Key.String())
 				continue
 				continue
 			}
 			}
-			if !reflect.DeepEqual(m.Payload.Peers[i], *currentPeer.PeerConf) {
-				if currentPeer.RemoteConn.IP.String() != m.Payload.Peers[i].Endpoint.IP.String() {
+			if !reflect.DeepEqual(m.Payload.Peers[i], *currentPeer.Config.PeerConf) {
+				if currentPeer.Config.RemoteConnAddr.IP.String() != m.Payload.Peers[i].Endpoint.IP.String() {
 					log.Println("----------> Resetting proxy for Peer: ", currentPeer.Key, m.Payload.InterfaceName)
 					log.Println("----------> Resetting proxy for Peer: ", currentPeer.Key, m.Payload.InterfaceName)
 					currentPeer.StopConn()
 					currentPeer.StopConn()
-					delete(wgProxyConf.PeerMap, currentPeer.Key)
-
+					currentPeer.Mutex.Unlock()
+					delete(wgProxyConf.PeerMap, currentPeer.Key.String())
+					continue
 				} else {
 				} else {
 
 
 					log.Println("----->##### Updating Peer on Interface: ", m.Payload.InterfaceName, currentPeer.Key)
 					log.Println("----->##### Updating Peer on Interface: ", m.Payload.InterfaceName, currentPeer.Key)
 					updatePeerConf := m.Payload.Peers[i]
 					updatePeerConf := m.Payload.Peers[i]
-					localUdpAddr, err := net.ResolveUDPAddr("udp", currentPeer.LocalConn.LocalAddr().String())
+					localUdpAddr, err := net.ResolveUDPAddr("udp", currentPeer.Config.LocalConnAddr.String())
 					if err == nil {
 					if err == nil {
 						updatePeerConf.Endpoint = localUdpAddr
 						updatePeerConf.Endpoint = localUdpAddr
 					}
 					}
 					if err := wgIface.Update(updatePeerConf, true); err != nil {
 					if err := wgIface.Update(updatePeerConf, true); err != nil {
 						log.Println("failed to update peer: ", currentPeer.Key, err)
 						log.Println("failed to update peer: ", currentPeer.Key, err)
 					}
 					}
-					currentPeer.PeerConf = &m.Payload.Peers[i]
-					wgProxyConf.PeerMap[currentPeer.Key] = currentPeer
+					currentPeer.Config.PeerConf = &m.Payload.Peers[i]
+					wgProxyConf.PeerMap[currentPeer.Key.String()] = currentPeer
 					// delete the peer from the list
 					// delete the peer from the list
 					log.Println("-----------> deleting peer from list: ", m.Payload.Peers[i].PublicKey)
 					log.Println("-----------> deleting peer from list: ", m.Payload.Peers[i].PublicKey)
 					m.Payload.Peers = append(m.Payload.Peers[:i], m.Payload.Peers[i+1:]...)
 					m.Payload.Peers = append(m.Payload.Peers[:i], m.Payload.Peers[i+1:]...)
@@ -308,6 +317,7 @@ func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
 				log.Println("-----------> No updates observed so deleting peer: ", m.Payload.Peers[i].PublicKey)
 				log.Println("-----------> No updates observed so deleting peer: ", m.Payload.Peers[i].PublicKey)
 				m.Payload.Peers = append(m.Payload.Peers[:i], m.Payload.Peers[i+1:]...)
 				m.Payload.Peers = append(m.Payload.Peers[:i], m.Payload.Peers[i+1:]...)
 			}
 			}
+			currentPeer.Mutex.Unlock()
 
 
 		} else if !m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].Proxy && !m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].IsAttachedExtClient {
 		} else if !m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].Proxy && !m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].IsAttachedExtClient {
 			log.Println("-----------> skipping peer, proxy is off: ", m.Payload.Peers[i].PublicKey)
 			log.Println("-----------> skipping peer, proxy is off: ", m.Payload.Peers[i].PublicKey)
@@ -320,7 +330,7 @@ func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
 
 
 	// sync dev peers with new update
 	// sync dev peers with new update
 
 
-	common.WgIFaceMap[m.Payload.InterfaceName] = wgProxyConf
+	common.WgIfaceMap = wgProxyConf
 
 
 	log.Println("CLEANED UP..........")
 	log.Println("CLEANED UP..........")
 	return wgIface, nil
 	return wgIface, nil
@@ -335,16 +345,6 @@ func (m *ManagerAction) AddInterfaceToProxy() error {
 	}
 	}
 
 
 	log.Printf("wg: %+v\n", wgInterface)
 	log.Printf("wg: %+v\n", wgInterface)
-	wgListenAddr, err := proxy.GetInterfaceListenAddr(wgInterface.Port)
-	if err != nil {
-		log.Println("failed to get wg listen addr: ", err)
-		return err
-	}
-	common.WgIfaceKeyMap[fmt.Sprintf("%x", md5.Sum([]byte(wgInterface.Device.PublicKey.String())))] = models.RemotePeer{
-		PeerKey:   wgInterface.Device.PublicKey.String(),
-		Interface: wgInterface.Name,
-		Endpoint:  wgListenAddr,
-	}
 	for _, peerI := range m.Payload.Peers {
 	for _, peerI := range m.Payload.Peers {
 
 
 		peerConf := m.Payload.PeerMap[peerI.PublicKey.String()]
 		peerConf := m.Payload.PeerMap[peerI.PublicKey.String()]
@@ -382,13 +382,6 @@ func (m *ManagerAction) AddInterfaceToProxy() error {
 				defer func() {
 				defer func() {
 					if addExtClient {
 					if addExtClient {
 						log.Println("GOT ENDPOINT for Extclient adding peer...")
 						log.Println("GOT ENDPOINT for Extclient adding peer...")
-						common.PeerKeyHashMap[fmt.Sprintf("%x", md5.Sum([]byte(peer.PublicKey.String())))] = models.RemotePeer{
-							Interface:           wgInterface.Name,
-							PeerKey:             peer.PublicKey.String(),
-							IsExtClient:         peerConf.IsExtClient,
-							IsAttachedExtClient: peerConf.IsAttachedExtClient,
-							Endpoint:            peer.Endpoint,
-						}
 
 
 						common.ExtSourceIpMap[peer.Endpoint.String()] = models.RemotePeer{
 						common.ExtSourceIpMap[peer.Endpoint.String()] = models.RemotePeer{
 							Interface:           wgInterface.Name,
 							Interface:           wgInterface.Name,
@@ -422,19 +415,10 @@ func (m *ManagerAction) AddInterfaceToProxy() error {
 			}(wgInterface, &peerI, isRelayed, relayedTo, peerConf, m.Payload.WgAddr)
 			}(wgInterface, &peerI, isRelayed, relayedTo, peerConf, m.Payload.WgAddr)
 			continue
 			continue
 		}
 		}
-		common.PeerKeyHashMap[fmt.Sprintf("%x", md5.Sum([]byte(peerI.PublicKey.String())))] = models.RemotePeer{
-			Interface:           m.Payload.InterfaceName,
-			PeerKey:             peerI.PublicKey.String(),
-			IsExtClient:         peerConf.IsExtClient,
-			Endpoint:            peerI.Endpoint,
-			IsAttachedExtClient: peerConf.IsAttachedExtClient,
-		}
 
 
 		peerpkg.AddNewPeer(wgInterface, &peerI, peerConf.Address, isRelayed,
 		peerpkg.AddNewPeer(wgInterface, &peerI, peerConf.Address, isRelayed,
 			peerConf.IsExtClient, peerConf.IsAttachedExtClient, relayedTo)
 			peerConf.IsExtClient, peerConf.IsAttachedExtClient, relayedTo)
+
 	}
 	}
-	log.Printf("------> PEERHASHMAP: %+v\n", common.PeerKeyHashMap)
-	log.Printf("-------> WgKeyHashMap: %+v\n", common.WgIfaceKeyMap)
-	log.Printf("-------> WgIFaceMap: %+v\n", common.WgIFaceMap)
 	return nil
 	return nil
 }
 }

+ 56 - 0
nm-proxy/metrics/metrics.go

@@ -0,0 +1,56 @@
+package metrics
+
+import (
+	"encoding/json"
+	"os"
+	"sync"
+	"time"
+)
+
+/*
+1. Create metrics packet--> packet with identifier to track latency, errors.
+
+*/
+
+type Metric struct {
+	LastRecordedLatency uint64
+	ConnectionStatus    bool
+	TrafficSent         float64
+	TrafficRecieved     float64
+}
+
+type MetricsPayload struct {
+	MetricType MetricsUpdateType
+	Value      interface{}
+}
+
+type MetricsUpdateType uint32
+
+const (
+	LatencyUpdate         MetricsUpdateType = 1
+	TrafficSentUpdate     MetricsUpdateType = 2
+	TrafficRecievedUpdate MetricsUpdateType = 3
+)
+
+var MetricsMapLock = &sync.RWMutex{}
+
+var MetricsMap = make(map[string]Metric)
+
+func init() {
+	go func() {
+		for {
+			time.Sleep(1 * time.Minute)
+			PrintMetrics()
+		}
+	}()
+}
+
+func PrintMetrics() {
+
+	data, err := json.MarshalIndent(MetricsMap, "", " ")
+	if err != nil {
+		return
+	}
+	os.WriteFile("/tmp/metrics.json", data, 0755)
+
+}

+ 65 - 0
nm-proxy/models/models.go

@@ -0,0 +1,65 @@
+package models
+
+import (
+	"context"
+	"net"
+	"sync"
+	"time"
+
+	"github.com/gravitl/netmaker/nm-proxy/wg"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+)
+
+const (
+	NmProxyPort = 51722
+	DefaultCIDR = "127.0.0.1/8"
+)
+
+type ProxyConfig struct {
+	RemoteKey           wgtypes.Key
+	LocalKey            wgtypes.Key
+	WgInterface         *wg.WGIface
+	IsExtClient         bool
+	PersistentKeepalive *time.Duration
+	RecieverChan        chan []byte
+	PeerConf            *wgtypes.PeerConfig
+	PeerEndpoint        *net.UDPAddr
+	RemoteConnAddr      *net.UDPAddr
+	LocalConnAddr       *net.UDPAddr
+}
+
+// Conn is a peer Connection configuration
+type Conn struct {
+
+	// Key is a public key of a remote peer
+	Key                 wgtypes.Key
+	IsExtClient         bool
+	IsRelayed           bool
+	RelayedEndpoint     *net.UDPAddr
+	IsAttachedExtClient bool
+	Config              ProxyConfig
+	StopConn            func()
+	ResetConn           func()
+	LocalConn           net.Conn
+	Mutex               *sync.RWMutex
+}
+
+type RemotePeer struct {
+	PeerKey             string
+	Interface           string
+	Endpoint            *net.UDPAddr
+	IsExtClient         bool
+	IsAttachedExtClient bool
+	LocalConn           net.Conn
+}
+
+type ExtClientPeer struct {
+	CancelFunc context.CancelFunc
+	CommChan   chan *net.UDPAddr
+}
+
+type WgIfaceConf struct {
+	Iface        *wgtypes.Device
+	IfaceKeyHash string
+	PeerMap      map[string]*Conn
+}

+ 0 - 46
nm-proxy/models/peer.go

@@ -1,46 +0,0 @@
-package models
-
-import (
-	"context"
-	"net"
-
-	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
-)
-
-const (
-	NmProxyPort = 51722
-	DefaultCIDR = "127.0.0.1/8"
-)
-
-// ConnConfig is a peer Connection configuration
-type ConnConfig struct {
-
-	// Key is a public key of a remote peer
-	Key                 string
-	IsExtClient         bool
-	IsRelayed           bool
-	RelayedEndpoint     *net.UDPAddr
-	IsAttachedExtClient bool
-	PeerConf            *wgtypes.PeerConfig
-	StopConn            context.CancelFunc
-	RemoteConn          *net.UDPAddr
-	LocalConn           net.Conn
-}
-
-type RemotePeer struct {
-	PeerKey             string
-	Interface           string
-	Endpoint            *net.UDPAddr
-	IsExtClient         bool
-	IsAttachedExtClient bool
-}
-
-type ExtClientPeer struct {
-	CancelFunc context.CancelFunc
-	CommChan   chan *net.UDPAddr
-}
-
-type WgIfaceConf struct {
-	Iface   *wgtypes.Device
-	PeerMap map[string]*ConnConfig
-}

+ 6 - 5
nm-proxy/nm-proxy.go

@@ -12,12 +12,13 @@ import (
 	"github.com/gravitl/netmaker/nm-proxy/stun"
 	"github.com/gravitl/netmaker/nm-proxy/stun"
 )
 )
 
 
-// Comm Channel to configure proxy
-/* Actions -
-   1. Add - new interface and its peers
-   2. Delete - remove close all conns for the interface,cleanup
-
+/*
+	TODO:
+		1. Mutex locks for maps
+		2. CRUD funcs on Maps
+		3. Comments
 */
 */
+
 func Start(ctx context.Context, mgmChan chan *manager.ManagerAction, apiServerAddr string) {
 func Start(ctx context.Context, mgmChan chan *manager.ManagerAction, apiServerAddr string) {
 	log.Println("Starting Proxy...")
 	log.Println("Starting Proxy...")
 	common.IsHostNetwork = (os.Getenv("HOST_NETWORK") == "" || os.Getenv("HOST_NETWORK") == "on")
 	common.IsHostNetwork = (os.Getenv("HOST_NETWORK") == "" || os.Getenv("HOST_NETWORK") == "on")

+ 153 - 15
nm-proxy/packet/packet.go

@@ -1,36 +1,174 @@
 package packet
 package packet
 
 
 import (
 import (
+	"bytes"
 	"crypto/md5"
 	"crypto/md5"
+	"encoding/base64"
+	"encoding/binary"
+	"errors"
 	"fmt"
 	"fmt"
+	"log"
+	"net"
+	"time"
+
+	"github.com/gravitl/netmaker/nm-proxy/common"
+	"golang.org/x/crypto/blake2s"
+	"golang.org/x/crypto/chacha20poly1305"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 )
 )
 
 
-var udpHeaderLen = 8
+func ConsumeHandshakeInitiationMsg(initiator bool, buf []byte, src *net.UDPAddr, devicePubKey NoisePublicKey, devicePrivKey NoisePrivateKey) error {
+
+	var (
+		hash     [blake2s.Size]byte
+		chainKey [blake2s.Size]byte
+	)
+	var err error
+	var msg MessageInitiation
+	reader := bytes.NewReader(buf[:])
+	err = binary.Read(reader, binary.LittleEndian, &msg)
+	if err != nil {
+		log.Println("Failed to decode initiation message")
+		return err
+	}
+
+	if msg.Type != MessageInitiationType {
+		return errors.New("not handshake initiation message")
+	}
+	log.Println("-----> ConsumeHandshakeInitiationMsg, Intitator:  ", initiator)
+	mixHash(&hash, &InitialHash, devicePubKey[:])
+	mixHash(&hash, &hash, msg.Ephemeral[:])
+	mixKey(&chainKey, &InitialChainKey, msg.Ephemeral[:])
+
+	// decrypt static key
+	var peerPK NoisePublicKey
+	var key [chacha20poly1305.KeySize]byte
+	ss := sharedSecret(&devicePrivKey, msg.Ephemeral)
+	if isZero(ss[:]) {
+		return errors.New("no secret")
+	}
+	KDF2(&chainKey, &key, chainKey[:], ss[:])
+	aead, _ := chacha20poly1305.New(key[:])
+	_, err = aead.Open(peerPK[:0], ZeroNonce[:], msg.Static[:], hash[:])
+	if err != nil {
+		return err
+	}
+	log.Println("--------> Got HandShake from peer: ", base64.StdEncoding.EncodeToString(peerPK[:]), src)
+	if val, ok := common.ExtClientsWaitTh[base64.StdEncoding.EncodeToString(peerPK[:])]; ok {
+		val.CommChan <- src
+		time.Sleep(time.Second * 3)
+	}
+
+	setZero(hash[:])
+	setZero(chainKey[:])
+	return nil
+}
+
+func CreateProxyUpdatePacket(msg *ProxyUpdateMessage) ([]byte, error) {
+	var buff [MessageProxyUpdateSize]byte
+	writer := bytes.NewBuffer(buff[:0])
+	err := binary.Write(writer, binary.LittleEndian, msg)
+	if err != nil {
+		return nil, err
+	}
+	packet := writer.Bytes()
+	return packet, nil
+
+}
+
+func ConsumeProxyUpdateMsg(buf []byte) (*ProxyUpdateMessage, error) {
+	var msg ProxyUpdateMessage
+	reader := bytes.NewReader(buf[:])
+	err := binary.Read(reader, binary.LittleEndian, &msg)
+	if err != nil {
+		log.Println("Failed to decode proxy update message")
+		return nil, err
+	}
+
+	if msg.Type != MessageProxyUpdateType {
+		return nil, errors.New("not proxy update message")
+	}
+	return &msg, nil
+}
+
+func CreateMetricPacket(id uint32, sender, reciever wgtypes.Key) ([]byte, error) {
+	msg := MetricMessage{
+		Type:      MessageMetricsType,
+		ID:        id,
+		Sender:    sender,
+		Reciever:  reciever,
+		TimeStamp: time.Now().UnixMilli(),
+	}
+	log.Printf("----------> $$$$$$ CREATED PACKET: %+v\n", msg)
+	var buff [MessageMetricSize]byte
+	writer := bytes.NewBuffer(buff[:0])
+	err := binary.Write(writer, binary.LittleEndian, msg)
+	if err != nil {
+		return nil, err
+	}
+	packet := writer.Bytes()
+	return packet, nil
+}
+
+func ConsumeMetricPacket(buf []byte) (*MetricMessage, error) {
+	var msg MetricMessage
+	var err error
+	reader := bytes.NewReader(buf[:])
+	err = binary.Read(reader, binary.LittleEndian, &msg)
+	if err != nil {
+		log.Println("Failed to decode metric message")
+		return nil, err
+	}
+
+	if msg.Type != MessageMetricsType {
+		return nil, errors.New("not  metric message")
+	}
+	return &msg, nil
+}
 
 
 func ProcessPacketBeforeSending(buf []byte, n int, srckey, dstKey string) ([]byte, int, string, string) {
 func ProcessPacketBeforeSending(buf []byte, n int, srckey, dstKey string) ([]byte, int, string, string) {
 
 
 	srcKeymd5 := md5.Sum([]byte(srckey))
 	srcKeymd5 := md5.Sum([]byte(srckey))
 	dstKeymd5 := md5.Sum([]byte(dstKey))
 	dstKeymd5 := md5.Sum([]byte(dstKey))
-	if n > len(buf)-len(srcKeymd5)-len(dstKeymd5) {
-		buf = append(buf, srcKeymd5[:]...)
-		buf = append(buf, dstKeymd5[:]...)
+	m := ProxyMessage{
+		Type:     MessageProxyType,
+		Sender:   srcKeymd5,
+		Reciever: dstKeymd5,
+	}
+	var msgBuffer [MessageProxySize]byte
+	writer := bytes.NewBuffer(msgBuffer[:0])
+	err := binary.Write(writer, binary.LittleEndian, m)
+	if err != nil {
+		log.Println(err)
+	}
+	if n > len(buf)-MessageProxySize {
+		buf = append(buf, msgBuffer[:]...)
+
 	} else {
 	} else {
-		copy(buf[n:n+len(srcKeymd5)], srcKeymd5[:])
-		copy(buf[n+len(srcKeymd5):n+len(srcKeymd5)+len(dstKeymd5)], dstKeymd5[:])
+		copy(buf[n:n+MessageProxySize], msgBuffer[:])
 	}
 	}
-	n += len(srcKeymd5)
-	n += len(dstKeymd5)
+	n += MessageProxySize
 
 
 	return buf, n, fmt.Sprintf("%x", srcKeymd5), fmt.Sprintf("%x", dstKeymd5)
 	return buf, n, fmt.Sprintf("%x", srcKeymd5), fmt.Sprintf("%x", dstKeymd5)
 }
 }
 
 
-func ExtractInfo(buffer []byte, n int) (int, string, string) {
+func ExtractInfo(buffer []byte, n int) (int, string, string, error) {
 	data := buffer[:n]
 	data := buffer[:n]
-	if len(data) < 32 {
-		return 0, "", ""
+	if len(data) < MessageProxySize {
+		return n, "", "", errors.New("proxy message not found")
+	}
+	var msg ProxyMessage
+	var err error
+	reader := bytes.NewReader(buffer[n-MessageProxySize:])
+	err = binary.Read(reader, binary.LittleEndian, &msg)
+	if err != nil {
+		log.Println("Failed to decode proxy message")
+		return n, "", "", err
+	}
+
+	if msg.Type != MessageProxyType {
+		return n, "", "", errors.New("not a proxy message")
 	}
 	}
-	srcKeyHash := data[n-32 : n-16]
-	dstKeyHash := data[n-16:]
-	n -= 32
-	return n, fmt.Sprintf("%x", srcKeyHash), fmt.Sprintf("%x", dstKeyHash)
+	n -= MessageProxySize
+	return n, fmt.Sprintf("%x", msg.Sender), fmt.Sprintf("%x", msg.Reciever), nil
 }
 }

+ 20 - 53
nm-proxy/packet/packet_helper.go

@@ -1,19 +1,11 @@
 package packet
 package packet
 
 
 import (
 import (
-	"bytes"
-	"encoding/base64"
-	"encoding/binary"
-	"errors"
-	"log"
-	"net"
-	"time"
-
-	"github.com/gravitl/netmaker/nm-proxy/common"
 	"golang.org/x/crypto/blake2s"
 	"golang.org/x/crypto/blake2s"
 	"golang.org/x/crypto/chacha20poly1305"
 	"golang.org/x/crypto/chacha20poly1305"
 	"golang.org/x/crypto/poly1305"
 	"golang.org/x/crypto/poly1305"
 	"golang.zx2c4.com/wireguard/tai64n"
 	"golang.zx2c4.com/wireguard/tai64n"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 )
 )
 
 
 var (
 var (
@@ -28,7 +20,7 @@ func init() {
 }
 }
 
 
 type MessageInitiation struct {
 type MessageInitiation struct {
-	Type      uint32
+	Type      MessageType
 	Sender    uint32
 	Sender    uint32
 	Ephemeral NoisePublicKey
 	Ephemeral NoisePublicKey
 	Static    [NoisePublicKeySize + poly1305.TagSize]byte
 	Static    [NoisePublicKeySize + poly1305.TagSize]byte
@@ -37,49 +29,24 @@ type MessageInitiation struct {
 	MAC2      [blake2s.Size128]byte
 	MAC2      [blake2s.Size128]byte
 }
 }
 
 
-func ConsumeHandshakeInitiationMsg(initiator bool, buf []byte, src *net.UDPAddr, devicePubKey NoisePublicKey, devicePrivKey NoisePrivateKey) error {
-
-	var (
-		hash     [blake2s.Size]byte
-		chainKey [blake2s.Size]byte
-	)
-	var err error
-	var msg MessageInitiation
-	reader := bytes.NewReader(buf[:])
-	err = binary.Read(reader, binary.LittleEndian, &msg)
-	if err != nil {
-		log.Println("Failed to decode initiation message")
-		return err
-	}
-
-	if msg.Type != MessageInitiationType {
-		return errors.New("not handshake initiation message")
-	}
-	log.Println("-----> ConsumeHandshakeInitiationMsg, Intitator:  ", initiator)
-	mixHash(&hash, &InitialHash, devicePubKey[:])
-	mixHash(&hash, &hash, msg.Ephemeral[:])
-	mixKey(&chainKey, &InitialChainKey, msg.Ephemeral[:])
+type MetricMessage struct {
+	Type      MessageType
+	ID        uint32
+	Sender    wgtypes.Key
+	Reciever  wgtypes.Key
+	TimeStamp int64
+}
 
 
-	// decrypt static key
-	var peerPK NoisePublicKey
-	var key [chacha20poly1305.KeySize]byte
-	ss := sharedSecret(&devicePrivKey, msg.Ephemeral)
-	if isZero(ss[:]) {
-		return errors.New("no secret")
-	}
-	KDF2(&chainKey, &key, chainKey[:], ss[:])
-	aead, _ := chacha20poly1305.New(key[:])
-	_, err = aead.Open(peerPK[:0], ZeroNonce[:], msg.Static[:], hash[:])
-	if err != nil {
-		return err
-	}
-	log.Println("--------> Got HandShake from peer: ", base64.StdEncoding.EncodeToString(peerPK[:]), src)
-	if val, ok := common.ExtClientsWaitTh[base64.StdEncoding.EncodeToString(peerPK[:])]; ok {
-		val.CommChan <- src
-		time.Sleep(time.Second * 3)
-	}
+type ProxyMessage struct {
+	Type     MessageType
+	Sender   [16]byte
+	Reciever [16]byte
+}
 
 
-	setZero(hash[:])
-	setZero(chainKey[:])
-	return nil
+type ProxyUpdateMessage struct {
+	Type       MessageType
+	Action     ProxyActionType
+	Sender     wgtypes.Key
+	Reciever   wgtypes.Key
+	ListenPort uint32
 }
 }

+ 16 - 1
nm-proxy/packet/utils.go

@@ -10,12 +10,27 @@ import (
 	"golang.org/x/crypto/curve25519"
 	"golang.org/x/crypto/curve25519"
 )
 )
 
 
+type MessageType uint32
+type ProxyActionType uint32
+
 const (
 const (
-	MessageInitiationType = 1
+	MessageInitiationType  MessageType = 1
+	MessageMetricsType     MessageType = 5
+	MessageProxyType       MessageType = 6
+	MessageProxyUpdateType MessageType = 7
+)
 
 
+const (
+	UpdateListenPort ProxyActionType = 1
+)
+const (
 	NoisePublicKeySize  = 32
 	NoisePublicKeySize  = 32
 	NoisePrivateKeySize = 32
 	NoisePrivateKeySize = 32
 
 
+	MessageMetricSize      = 148
+	MessageProxyUpdateSize = 148
+	MessageProxySize       = 36
+
 	NoiseConstruction = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
 	NoiseConstruction = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
 	WGIdentifier      = "WireGuard v1 zx2c4 [email protected]"
 	WGIdentifier      = "WireGuard v1 zx2c4 [email protected]"
 	WGLabelMAC1       = "mac1----"
 	WGLabelMAC1       = "mac1----"

+ 34 - 33
nm-proxy/peer/peer.go

@@ -1,10 +1,13 @@
 package peer
 package peer
 
 
 import (
 import (
+	"crypto/md5"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"log"
 	"log"
 	"net"
 	"net"
+	"sync"
+	"time"
 
 
 	"github.com/gravitl/netmaker/nm-proxy/common"
 	"github.com/gravitl/netmaker/nm-proxy/common"
 	"github.com/gravitl/netmaker/nm-proxy/models"
 	"github.com/gravitl/netmaker/nm-proxy/models"
@@ -15,14 +18,18 @@ import (
 
 
 func AddNewPeer(wgInterface *wg.WGIface, peer *wgtypes.PeerConfig, peerAddr string,
 func AddNewPeer(wgInterface *wg.WGIface, peer *wgtypes.PeerConfig, peerAddr string,
 	isRelayed, isExtClient, isAttachedExtClient bool, relayTo *net.UDPAddr) error {
 	isRelayed, isExtClient, isAttachedExtClient bool, relayTo *net.UDPAddr) error {
-
-	c := proxy.Config{
-		Port:        peer.Endpoint.Port,
-		LocalKey:    wgInterface.Device.PublicKey.String(),
-		RemoteKey:   peer.PublicKey.String(),
-		WgInterface: wgInterface,
-		IsExtClient: isExtClient,
-		PeerConf:    peer,
+	if peer.PersistentKeepaliveInterval == nil {
+		d := time.Second * 25
+		peer.PersistentKeepaliveInterval = &d
+	}
+	c := models.ProxyConfig{
+		LocalKey:            wgInterface.Device.PublicKey,
+		RemoteKey:           peer.PublicKey,
+		WgInterface:         wgInterface,
+		IsExtClient:         isExtClient,
+		PeerConf:            peer,
+		PersistentKeepalive: peer.PersistentKeepaliveInterval,
+		RecieverChan:        make(chan []byte, 1000),
 	}
 	}
 	p := proxy.NewProxy(c)
 	p := proxy.NewProxy(c)
 	peerPort := models.NmProxyPort
 	peerPort := models.NmProxyPort
@@ -30,53 +37,47 @@ func AddNewPeer(wgInterface *wg.WGIface, peer *wgtypes.PeerConfig, peerAddr stri
 		peerPort = peer.Endpoint.Port
 		peerPort = peer.Endpoint.Port
 
 
 	}
 	}
-	peerEndpoint := peer.Endpoint.IP.String()
+	peerEndpointIP := peer.Endpoint.IP
 	if isRelayed {
 	if isRelayed {
 		//go server.NmProxyServer.KeepAlive(peer.Endpoint.IP.String(), common.NmProxyPort)
 		//go server.NmProxyServer.KeepAlive(peer.Endpoint.IP.String(), common.NmProxyPort)
 		if relayTo == nil {
 		if relayTo == nil {
 			return errors.New("relay endpoint is nil")
 			return errors.New("relay endpoint is nil")
 		}
 		}
-		peerEndpoint = relayTo.IP.String()
+		peerEndpointIP = relayTo.IP
 	}
 	}
-
-	remoteConn, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", peerEndpoint, peerPort))
+	peerEndpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", peerEndpointIP, peerPort))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	log.Printf("----> Established Remote Conn with RPeer: %s, ----> RAddr: %s", peer.PublicKey, remoteConn.String())
+	p.Config.PeerEndpoint = peerEndpoint
 
 
-	// if !(isExtClient && isAttachedExtClient) {
 	log.Printf("Starting proxy for Peer: %s\n", peer.PublicKey.String())
 	log.Printf("Starting proxy for Peer: %s\n", peer.PublicKey.String())
-	err = p.Start(remoteConn)
+	err = p.Start()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	// } else {
-	// 	log.Println("Not Starting Proxy for Attached ExtClient...")
-	// }
 
 
-	connConf := models.ConnConfig{
-		Key:                 peer.PublicKey.String(),
+	connConf := models.Conn{
+		Mutex:               &sync.RWMutex{},
+		Key:                 peer.PublicKey,
 		IsRelayed:           isRelayed,
 		IsRelayed:           isRelayed,
 		RelayedEndpoint:     relayTo,
 		RelayedEndpoint:     relayTo,
 		IsAttachedExtClient: isAttachedExtClient,
 		IsAttachedExtClient: isAttachedExtClient,
-		PeerConf:            peer,
-		StopConn:            p.Cancel,
-		RemoteConn:          remoteConn,
+		Config:              p.Config,
+		StopConn:            p.Close,
+		ResetConn:           p.Reset,
 		LocalConn:           p.LocalConn,
 		LocalConn:           p.LocalConn,
 	}
 	}
 
 
-	if _, ok := common.WgIFaceMap[wgInterface.Name]; ok {
-		common.WgIFaceMap[wgInterface.Name].PeerMap[peer.PublicKey.String()] = &connConf
-	} else {
-		ifaceConf := models.WgIfaceConf{
-			Iface:   wgInterface.Device,
-			PeerMap: make(map[string]*models.ConnConfig),
-		}
+	common.WgIfaceMap.PeerMap[peer.PublicKey.String()] = &connConf
 
 
-		common.WgIFaceMap[wgInterface.Name] = ifaceConf
-		common.WgIFaceMap[wgInterface.Name].PeerMap[peer.PublicKey.String()] = &connConf
+	common.PeerKeyHashMap[fmt.Sprintf("%x", md5.Sum([]byte(peer.PublicKey.String())))] = models.RemotePeer{
+		Interface:           wgInterface.Name,
+		PeerKey:             peer.PublicKey.String(),
+		IsExtClient:         isExtClient,
+		Endpoint:            peerEndpoint,
+		IsAttachedExtClient: isAttachedExtClient,
+		LocalConn:           p.LocalConn,
 	}
 	}
-
 	return nil
 	return nil
 }
 }

+ 65 - 36
nm-proxy/proxy/proxy.go

@@ -4,59 +4,88 @@ import (
 	"context"
 	"context"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"log"
 	"net"
 	"net"
+	"runtime"
 
 
 	"github.com/gravitl/netmaker/nm-proxy/common"
 	"github.com/gravitl/netmaker/nm-proxy/common"
-	"github.com/gravitl/netmaker/nm-proxy/wg"
-	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+	"github.com/gravitl/netmaker/nm-proxy/models"
 )
 )
 
 
-const (
-	defaultBodySize = 10000
-	defaultPort     = 51722
-)
-
-type Config struct {
-	Port        int
-	BodySize    int
-	Addr        string
-	RemoteKey   string
-	LocalKey    string
-	WgInterface *wg.WGIface
-	IsExtClient bool
-	PeerConf    *wgtypes.PeerConfig
-}
-
 // Proxy -  WireguardProxy proxies
 // Proxy -  WireguardProxy proxies
 type Proxy struct {
 type Proxy struct {
 	Ctx        context.Context
 	Ctx        context.Context
 	Cancel     context.CancelFunc
 	Cancel     context.CancelFunc
-	Config     Config
+	Config     models.ProxyConfig
 	RemoteConn *net.UDPAddr
 	RemoteConn *net.UDPAddr
 	LocalConn  net.Conn
 	LocalConn  net.Conn
 }
 }
 
 
-func GetInterfaceIpv4Addr(interfaceName string) (addr string, err error) {
-	var (
-		ief      *net.Interface
-		addrs    []net.Addr
-		ipv4Addr net.IP
-	)
-	if ief, err = net.InterfaceByName(interfaceName); err != nil { // get interface
-		return
+func (p *Proxy) Start() error {
+
+	var err error
+	p.RemoteConn = p.Config.PeerEndpoint
+	log.Printf("----> Established Remote Conn with RPeer: %s, ----> RAddr: %s", p.Config.RemoteKey.String(), p.RemoteConn.String())
+	addr, err := GetFreeIp(models.DefaultCIDR, p.Config.WgInterface.Port)
+	if err != nil {
+		log.Println("Failed to get freeIp: ", err)
+		return err
 	}
 	}
-	if addrs, err = ief.Addrs(); err != nil { // get addresses
-		return
+	wgListenAddr, err := GetInterfaceListenAddr(p.Config.WgInterface.Port)
+	if err != nil {
+		log.Println("failed to get wg listen addr: ", err)
+		return err
 	}
 	}
-	for _, addr := range addrs { // get ipv4 address
-		if ipv4Addr = addr.(*net.IPNet).IP.To4(); ipv4Addr != nil {
-			break
-		}
+	if runtime.GOOS == "darwin" {
+		wgListenAddr.IP = net.ParseIP(addr)
+	}
+	p.LocalConn, err = net.DialUDP("udp", &net.UDPAddr{
+		IP:   net.ParseIP(addr),
+		Port: models.NmProxyPort,
+	}, wgListenAddr)
+	if err != nil {
+		log.Printf("failed dialing to local Wireguard port,Err: %v\n", err)
+		return err
+	}
+
+	log.Printf("Dialing to local Wireguard port %s --> %s\n", p.LocalConn.LocalAddr().String(), p.LocalConn.RemoteAddr().String())
+	err = p.updateEndpoint()
+	if err != nil {
+		log.Printf("error while updating Wireguard peer endpoint [%s] %v\n", p.Config.RemoteKey, err)
+		return err
+	}
+	localAddr, err := net.ResolveUDPAddr("udp", p.LocalConn.LocalAddr().String())
+	if err != nil {
+		log.Println("failed to resolve local addr: ", err)
+		return err
 	}
 	}
-	if ipv4Addr == nil {
-		return "", errors.New(fmt.Sprintf("interface %s don't have an ipv4 address\n", interfaceName))
+	p.Config.LocalConnAddr = localAddr
+	p.Config.RemoteConnAddr = p.RemoteConn
+	go p.ProxyPeer()
+
+	return nil
+}
+
+func (p *Proxy) Close() {
+	log.Println("------> Closing Proxy for ", p.Config.RemoteKey.String())
+	p.Cancel()
+	p.LocalConn.Close()
+	if runtime.GOOS == "darwin" {
+		host, _, err := net.SplitHostPort(p.LocalConn.LocalAddr().String())
+		if err != nil {
+			log.Println("Failed to split host: ", p.LocalConn.LocalAddr().String(), err)
+			return
+		}
+
+		if host != "127.0.0.1" {
+			_, err = common.RunCmd(fmt.Sprintf("ifconfig lo0 -alias %s 255.255.255.255", host), true)
+			if err != nil {
+				log.Println("Failed to add alias: ", err)
+			}
+		}
+
 	}
 	}
-	return ipv4Addr.String(), nil
+	close(p.Config.RecieverChan)
 }
 }
 
 
 func GetInterfaceListenAddr(port int) (*net.UDPAddr, error) {
 func GetInterfaceListenAddr(port int) (*net.UDPAddr, error) {

+ 245 - 0
nm-proxy/proxy/proxy_helper.go

@@ -0,0 +1,245 @@
+package proxy
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"log"
+	"net"
+	"runtime"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/c-robinson/iplib"
+	"github.com/google/uuid"
+	"github.com/gravitl/netmaker/nm-proxy/common"
+	"github.com/gravitl/netmaker/nm-proxy/metrics"
+	"github.com/gravitl/netmaker/nm-proxy/models"
+	"github.com/gravitl/netmaker/nm-proxy/packet"
+	"github.com/gravitl/netmaker/nm-proxy/server"
+	"github.com/gravitl/netmaker/nm-proxy/stun"
+	"github.com/gravitl/netmaker/nm-proxy/wg"
+)
+
+func NewProxy(config models.ProxyConfig) *Proxy {
+	p := &Proxy{Config: config}
+	p.Ctx, p.Cancel = context.WithCancel(context.Background())
+	return p
+}
+
+func (p *Proxy) proxyToRemote(wg *sync.WaitGroup) {
+	ticker := time.NewTicker(time.Minute)
+	defer ticker.Stop()
+	buf := make([]byte, 65000)
+	defer wg.Done()
+	for {
+		select {
+		case <-p.Ctx.Done():
+			return
+		default:
+
+			n, err := p.LocalConn.Read(buf)
+			if err != nil {
+				log.Println("ERRR READ: ", err)
+				continue
+			}
+
+			// if _, found := common.GetPeer(p.Config.RemoteKey); !found {
+			// 	log.Printf("Peer: %s not found in config\n", p.Config.RemoteKey)
+			// 	p.Close()
+			// 	return
+			// }
+			go func(n int, peerKey string) {
+				metrics.MetricsMapLock.Lock()
+				metric := metrics.MetricsMap[peerKey]
+				metric.TrafficSent += float64(n) / (1 << 20)
+				metrics.MetricsMap[peerKey] = metric
+				metrics.MetricsMapLock.Unlock()
+			}(n, p.Config.RemoteKey.String())
+
+			//var srcPeerKeyHash, dstPeerKeyHash string
+			if !p.Config.IsExtClient {
+				buf, n, _, _ = packet.ProcessPacketBeforeSending(buf, n, p.Config.WgInterface.Device.PublicKey.String(), p.Config.RemoteKey.String())
+				if err != nil {
+					log.Println("failed to process pkt before sending: ", err)
+				}
+			}
+
+			// log.Printf("PROXING TO REMOTE!!!---> %s >>>>> %s >>>>> %s [[ SrcPeerHash: %s, DstPeerHash: %s ]]\n",
+			// 	p.LocalConn.LocalAddr(), server.NmProxyServer.Server.LocalAddr().String(), p.RemoteConn.String(), srcPeerKeyHash, dstPeerKeyHash)
+
+			_, err = server.NmProxyServer.Server.WriteToUDP(buf[:n], p.RemoteConn)
+			if err != nil {
+				log.Println("Failed to send to remote: ", err)
+			}
+
+		}
+	}
+
+}
+
+func (p *Proxy) Reset() {
+	p.Close()
+	if err := p.pullLatestConfig(); err != nil {
+		log.Println("couldn't perform reset: ", err)
+		return
+	}
+	p.Start()
+
+}
+
+func (p *Proxy) pullLatestConfig() error {
+	peer, found := common.GetPeer(p.Config.RemoteKey)
+	if found {
+		p.Config.PeerEndpoint.Port = peer.Config.PeerEndpoint.Port
+	} else {
+		return errors.New("peer not found")
+	}
+	return nil
+
+}
+
+func (p *Proxy) startMetricsThread(wg *sync.WaitGroup, rTicker *time.Ticker) {
+	ticker := time.NewTicker(time.Minute)
+	defer ticker.Stop()
+	defer wg.Done()
+	for {
+		select {
+		case <-p.Ctx.Done():
+			return
+		case <-ticker.C:
+			metrics.MetricsMapLock.Lock()
+			metric := metrics.MetricsMap[p.Config.RemoteKey.String()]
+			if metric.ConnectionStatus {
+				rTicker.Reset(*p.Config.PersistentKeepalive)
+			}
+			metric.ConnectionStatus = false
+			metrics.MetricsMap[p.Config.RemoteKey.String()] = metric
+			metrics.MetricsMapLock.Unlock()
+			pkt, err := packet.CreateMetricPacket(uuid.New().ID(), p.Config.LocalKey, p.Config.RemoteKey)
+			if err == nil {
+				log.Printf("-----------> ##### $$$$$ SENDING METRIC PACKET TO: %s\n", p.RemoteConn.String())
+				_, err = server.NmProxyServer.Server.WriteToUDP(pkt, p.RemoteConn)
+				if err != nil {
+					log.Println("Failed to send to metric pkt: ", err)
+				}
+
+			}
+		}
+	}
+}
+
+func (p *Proxy) peerUpdates(wg *sync.WaitGroup, ticker *time.Ticker) {
+	defer wg.Done()
+	for {
+		select {
+		case <-p.Ctx.Done():
+			return
+		case <-ticker.C:
+			// send listen port packet
+			m := &packet.ProxyUpdateMessage{
+				Type:       packet.MessageProxyType,
+				Action:     packet.UpdateListenPort,
+				Sender:     p.Config.LocalKey,
+				Reciever:   p.Config.RemoteKey,
+				ListenPort: uint32(stun.Host.PrivPort),
+			}
+			pkt, err := packet.CreateProxyUpdatePacket(m)
+			if err == nil {
+				log.Printf("-----------> ##### $$$$$ SENDING Proxy Update PACKET TO: %s\n", p.RemoteConn.String())
+				_, err = server.NmProxyServer.Server.WriteToUDP(pkt, p.RemoteConn)
+				if err != nil {
+					log.Println("Failed to send to metric pkt: ", err)
+				}
+
+			}
+		}
+	}
+}
+
+// ProxyPeer proxies everything from Wireguard to the RemoteKey peer and vice-versa
+func (p *Proxy) ProxyPeer() {
+	ticker := time.NewTicker(*p.Config.PersistentKeepalive)
+	defer ticker.Stop()
+	wg := &sync.WaitGroup{}
+	wg.Add(1)
+	go p.proxyToRemote(wg)
+	// if common.BehindNAT {
+	wg.Add(1)
+	go p.startMetricsThread(wg, ticker)
+	wg.Add(1)
+	go p.peerUpdates(wg, ticker)
+	// }
+	wg.Wait()
+
+}
+func test(n int, buffer []byte) {
+	data := buffer[:n]
+	srcKeyHash := data[n-32 : n-16]
+	dstKeyHash := data[n-16:]
+	log.Printf("--------> TEST PACKET [ SRCKEYHASH: %x ], [ DSTKEYHASH: %x ] \n", srcKeyHash, dstKeyHash)
+}
+
+func (p *Proxy) updateEndpoint() error {
+	udpAddr, err := net.ResolveUDPAddr("udp", p.LocalConn.LocalAddr().String())
+	if err != nil {
+		return err
+	}
+	// add local proxy connection as a Wireguard peer
+	log.Printf("---> ####### Updating Peer:  %+v\n", p.Config.PeerConf)
+	err = p.Config.WgInterface.UpdatePeer(p.Config.RemoteKey.String(), p.Config.PeerConf.AllowedIPs, wg.DefaultWgKeepAlive,
+		udpAddr, p.Config.PeerConf.PresharedKey)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func GetFreeIp(cidrAddr string, dstPort int) (string, error) {
+	//ensure AddressRange is valid
+	if dstPort == 0 {
+		return "", errors.New("dst port should be set")
+	}
+	if _, _, err := net.ParseCIDR(cidrAddr); err != nil {
+		log.Println("UniqueAddress encountered  an error")
+		return "", err
+	}
+	net4 := iplib.Net4FromStr(cidrAddr)
+	newAddrs := net4.FirstAddress()
+	for {
+		if runtime.GOOS == "darwin" {
+			_, err := common.RunCmd(fmt.Sprintf("ifconfig lo0 alias %s 255.255.255.255", newAddrs.String()), true)
+			if err != nil {
+				log.Println("Failed to add alias: ", err)
+			}
+		}
+
+		conn, err := net.DialUDP("udp", &net.UDPAddr{
+			IP:   net.ParseIP(newAddrs.String()),
+			Port: models.NmProxyPort,
+		}, &net.UDPAddr{
+			IP:   net.ParseIP("127.0.0.1"),
+			Port: dstPort,
+		})
+		if err != nil {
+			log.Println("----> GetFreeIP ERR: ", err)
+			if strings.Contains(err.Error(), "can't assign requested address") ||
+				strings.Contains(err.Error(), "address already in use") || strings.Contains(err.Error(), "cannot assign requested address") {
+				var nErr error
+				newAddrs, nErr = net4.NextIP(newAddrs)
+				if nErr != nil {
+					return "", nErr
+				}
+			} else {
+				return "", err
+			}
+		}
+		if err == nil {
+			conn.Close()
+			return newAddrs.String(), nil
+		}
+
+	}
+}

+ 0 - 231
nm-proxy/proxy/wireguard.go

@@ -1,231 +0,0 @@
-package proxy
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"log"
-	"net"
-	"runtime"
-	"strings"
-
-	"github.com/c-robinson/iplib"
-	"github.com/gravitl/netmaker/nm-proxy/common"
-	"github.com/gravitl/netmaker/nm-proxy/models"
-	"github.com/gravitl/netmaker/nm-proxy/packet"
-	"github.com/gravitl/netmaker/nm-proxy/server"
-	"github.com/gravitl/netmaker/nm-proxy/wg"
-)
-
-func NewProxy(config Config) *Proxy {
-	p := &Proxy{Config: config}
-	p.Ctx, p.Cancel = context.WithCancel(context.Background())
-	return p
-}
-
-// proxyToRemote proxies everything from Wireguard to the RemoteKey peer
-func (p *Proxy) ProxyToRemote() {
-
-	go func() {
-		<-p.Ctx.Done()
-		log.Println("Closing connection for: ", p.LocalConn.LocalAddr().String())
-		p.LocalConn.Close()
-	}()
-	buf := make([]byte, 65000)
-	for {
-		select {
-		case <-p.Ctx.Done():
-			log.Printf("----------> stopped proxying to remote peer %s due to closed connection\n", p.Config.RemoteKey)
-			if runtime.GOOS == "darwin" {
-				host, _, err := net.SplitHostPort(p.LocalConn.LocalAddr().String())
-				if err != nil {
-					log.Println("Failed to split host: ", p.LocalConn.LocalAddr().String(), err)
-					return
-				}
-
-				if host != "127.0.0.1" {
-					_, err = common.RunCmd(fmt.Sprintf("ifconfig lo0 -alias %s 255.255.255.255", host), true)
-					if err != nil {
-						log.Println("Failed to add alias: ", err)
-					}
-				}
-
-			}
-
-			return
-		default:
-
-			n, err := p.LocalConn.Read(buf)
-			if err != nil {
-				log.Println("ERRR READ: ", err)
-				continue
-			}
-
-			//go func(buf []byte, n int) {
-			ifaceConf := common.WgIFaceMap[p.Config.WgInterface.Name]
-			if peerI, ok := ifaceConf.PeerMap[p.Config.RemoteKey]; ok {
-				var srcPeerKeyHash, dstPeerKeyHash string
-				if !p.Config.IsExtClient {
-					buf, n, srcPeerKeyHash, dstPeerKeyHash = packet.ProcessPacketBeforeSending(buf, n, ifaceConf.Iface.PublicKey.String(), peerI.Key)
-					if err != nil {
-						log.Println("failed to process pkt before sending: ", err)
-					}
-				} else {
-					// unknown peer to proxy -> check if extclient and handle it
-					// consume handshake message for ext clients
-					// msgType := binary.LittleEndian.Uint32(buf[:n])
-					// switch msgType {
-					// case models.MessageInitiationType:
-
-					// 	devPriv, devPubkey, err := packet.GetDeviceKeys(common.InterfaceName)
-					// 	if err == nil {
-					// 		err := packet.ConsumeHandshakeInitiationMsg(true, buf[:n], p.RemoteConn, devPubkey, devPriv)
-					// 		if err != nil {
-					// 			log.Println("---------> @@@ failed to decode HS: ", err)
-					// 		}
-					// 	} else {
-					// 		log.Println("failed to get device keys: ", err)
-					// 	}
-					// case models.MessageResponseType:
-					// 	devPriv, devPubkey, err := packet.GetDeviceKeys(common.InterfaceName)
-					// 	if err == nil {
-					// 		err := packet.ConsumeMessageResponse(true, buf[:n], p.RemoteConn, devPubkey, devPriv)
-					// 		if err != nil {
-					// 			log.Println("---------> @@@ failed to decode HS: ", err)
-					// 		}
-					// 	} else {
-					// 		log.Println("failed to get device keys: ", err)
-					// 	}
-
-					// }
-				}
-
-				log.Printf("PROXING TO REMOTE!!!---> %s >>>>> %s >>>>> %s [[ SrcPeerHash: %s, DstPeerHash: %s ]]\n",
-					p.LocalConn.LocalAddr(), server.NmProxyServer.Server.LocalAddr().String(), p.RemoteConn.String(), srcPeerKeyHash, dstPeerKeyHash)
-			} else {
-				log.Printf("Peer: %s not found in config\n", p.Config.RemoteKey)
-				p.Cancel()
-				return
-			}
-			//test(n, buf)
-
-			_, err = server.NmProxyServer.Server.WriteToUDP(buf[:n], p.RemoteConn)
-			if err != nil {
-				log.Println("Failed to send to remote: ", err)
-			}
-			//}(buf, n)
-
-		}
-	}
-}
-func test(n int, buffer []byte) {
-	data := buffer[:n]
-	srcKeyHash := data[n-32 : n-16]
-	dstKeyHash := data[n-16:]
-	log.Printf("--------> TEST PACKET [ SRCKEYHASH: %x ], [ DSTKEYHASH: %x ] \n", srcKeyHash, dstKeyHash)
-}
-
-func (p *Proxy) updateEndpoint() error {
-	udpAddr, err := net.ResolveUDPAddr("udp", p.LocalConn.LocalAddr().String())
-	if err != nil {
-		return err
-	}
-	// add local proxy connection as a Wireguard peer
-	log.Printf("---> ####### Updating Peer:  %+v\n", p.Config.PeerConf)
-	err = p.Config.WgInterface.UpdatePeer(p.Config.RemoteKey, p.Config.PeerConf.AllowedIPs, wg.DefaultWgKeepAlive,
-		udpAddr, p.Config.PeerConf.PresharedKey)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func (p *Proxy) Start(remoteConn *net.UDPAddr) error {
-	p.RemoteConn = remoteConn
-
-	var err error
-
-	//log.Printf("----> WGIFACE: %+v\n", p.Config.WgInterface)
-	addr, err := GetFreeIp(models.DefaultCIDR, p.Config.WgInterface.Port)
-	if err != nil {
-		log.Println("Failed to get freeIp: ", err)
-		return err
-	}
-	wgListenAddr, err := GetInterfaceListenAddr(p.Config.WgInterface.Port)
-	if err != nil {
-		log.Println("failed to get wg listen addr: ", err)
-		return err
-	}
-	if runtime.GOOS == "darwin" {
-		wgListenAddr.IP = net.ParseIP(addr)
-	}
-	//log.Println("--------->#### Wg Listen Addr: ", wgListenAddr.String())
-	p.LocalConn, err = net.DialUDP("udp", &net.UDPAddr{
-		IP:   net.ParseIP(addr),
-		Port: models.NmProxyPort,
-	}, wgListenAddr)
-	if err != nil {
-		log.Printf("failed dialing to local Wireguard port,Err: %v\n", err)
-		return err
-	}
-
-	log.Printf("Dialing to local Wireguard port %s --> %s\n", p.LocalConn.LocalAddr().String(), p.LocalConn.RemoteAddr().String())
-	err = p.updateEndpoint()
-	if err != nil {
-		log.Printf("error while updating Wireguard peer endpoint [%s] %v\n", p.Config.RemoteKey, err)
-		return err
-	}
-
-	go p.ProxyToRemote()
-
-	return nil
-}
-
-func GetFreeIp(cidrAddr string, dstPort int) (string, error) {
-	//ensure AddressRange is valid
-	if dstPort == 0 {
-		return "", errors.New("dst port should be set")
-	}
-	if _, _, err := net.ParseCIDR(cidrAddr); err != nil {
-		log.Println("UniqueAddress encountered  an error")
-		return "", err
-	}
-	net4 := iplib.Net4FromStr(cidrAddr)
-	newAddrs := net4.FirstAddress()
-	log.Println("COUNT: ", net4.Count())
-	for {
-		if runtime.GOOS == "darwin" {
-			_, err := common.RunCmd(fmt.Sprintf("ifconfig lo0 alias %s 255.255.255.255", newAddrs.String()), true)
-			if err != nil {
-				log.Println("Failed to add alias: ", err)
-			}
-		}
-
-		conn, err := net.DialUDP("udp", &net.UDPAddr{
-			IP:   net.ParseIP(newAddrs.String()),
-			Port: models.NmProxyPort,
-		}, &net.UDPAddr{
-			IP:   net.ParseIP("127.0.0.1"),
-			Port: dstPort,
-		})
-		if err != nil {
-			log.Println("----> GetFreeIP ERR: ", err)
-			if strings.Contains(err.Error(), "can't assign requested address") ||
-				strings.Contains(err.Error(), "address already in use") || strings.Contains(err.Error(), "cannot assign requested address") {
-				var nErr error
-				newAddrs, nErr = net4.NextIP(newAddrs)
-				if nErr != nil {
-					return "", nErr
-				}
-			} else {
-				return "", err
-			}
-		}
-		if err == nil {
-			conn.Close()
-			return newAddrs.String(), nil
-		}
-
-	}
-}

+ 161 - 99
nm-proxy/server/server.go

@@ -9,6 +9,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/gravitl/netmaker/nm-proxy/common"
 	"github.com/gravitl/netmaker/nm-proxy/common"
+	"github.com/gravitl/netmaker/nm-proxy/metrics"
 	"github.com/gravitl/netmaker/nm-proxy/models"
 	"github.com/gravitl/netmaker/nm-proxy/models"
 	"github.com/gravitl/netmaker/nm-proxy/packet"
 	"github.com/gravitl/netmaker/nm-proxy/packet"
 )
 )
@@ -34,142 +35,203 @@ type ProxyServer struct {
 	Server *net.UDPConn
 	Server *net.UDPConn
 }
 }
 
 
+func (p *ProxyServer) Close() {
+	log.Println("--------->### Shutting down Proxy.....")
+	// clean up proxy connections
+	for _, peerI := range common.WgIfaceMap.PeerMap {
+		peerI.Mutex.Lock()
+		peerI.StopConn()
+		peerI.Mutex.Unlock()
+	}
+	// close server connection
+	NmProxyServer.Server.Close()
+}
+
 // Proxy.Listen - begins listening for packets
 // Proxy.Listen - begins listening for packets
 func (p *ProxyServer) Listen(ctx context.Context) {
 func (p *ProxyServer) Listen(ctx context.Context) {
 
 
 	// Buffer with indicated body size
 	// Buffer with indicated body size
-	buffer := make([]byte, 65032)
+	buffer := make([]byte, 65036)
 	for {
 	for {
 
 
 		select {
 		select {
 		case <-ctx.Done():
 		case <-ctx.Done():
-			log.Println("--------->### Shutting down Proxy.....")
-			// clean up proxy connections
-			for iface, ifaceConf := range common.WgIFaceMap {
-				log.Println("########------------>  CLEANING UP: ", iface)
-				for _, peerI := range ifaceConf.PeerMap {
-					peerI.StopConn()
-				}
-			}
-			// close server connection
-			NmProxyServer.Server.Close()
+			p.Close()
 			return
 			return
 		default:
 		default:
 			// Read Packet
 			// Read Packet
 
 
 			n, source, err := p.Server.ReadFromUDP(buffer)
 			n, source, err := p.Server.ReadFromUDP(buffer)
-			if err != nil { // in future log errors?
+			if err != nil || source == nil { // in future log errors?
 				log.Println("RECV ERROR: ", err)
 				log.Println("RECV ERROR: ", err)
 				continue
 				continue
 			}
 			}
 			//go func(buffer []byte, source *net.UDPAddr, n int) {
 			//go func(buffer []byte, source *net.UDPAddr, n int) {
-			origBufferLen := n
+			proxyTransportMsg := true
 			var srcPeerKeyHash, dstPeerKeyHash string
 			var srcPeerKeyHash, dstPeerKeyHash string
-			n, srcPeerKeyHash, dstPeerKeyHash = packet.ExtractInfo(buffer, n)
-			//log.Printf("--------> RECV PKT , [SRCKEYHASH: %s], SourceIP: [%s] \n", srcPeerKeyHash, source.IP.String())
-
-			if _, ok := common.WgIfaceKeyMap[dstPeerKeyHash]; !ok {
-				// if common.IsIngressGateway {
-				// 	log.Println("----> fowarding PKT to EXT client...")
-				// 	if val, ok := common.PeerKeyHashMap[dstPeerKeyHash]; ok && val.IsAttachedExtClient {
-
-				// 		log.Printf("-------->Forwarding the pkt to extClient  [ SourceIP: %s ], [ SourceKeyHash: %s ], [ DstIP: %s ], [ DstHashKey: %s ] \n",
-				// 			source.String(), srcPeerKeyHash, val.Endpoint.String(), dstPeerKeyHash)
-				// 		_, err = NmProxyServer.Server.WriteToUDP(buffer[:n], val.Endpoint)
-				// 		if err != nil {
-				// 			log.Println("Failed to send to remote: ", err)
-				// 		}
-				// 		continue
-
-				// 	}
-				// }
-
-				if common.IsRelay {
-
-					log.Println("----------> Relaying######")
-					// check for routing map and forward to right proxy
-					if remoteMap, ok := common.RelayPeerMap[srcPeerKeyHash]; ok {
-						if conf, ok := remoteMap[dstPeerKeyHash]; ok {
-							log.Printf("--------> Relaying PKT [ SourceIP: %s:%d ], [ SourceKeyHash: %s ], [ DstIP: %s:%d ], [ DstHashKey: %s ] \n",
-								source.IP.String(), source.Port, srcPeerKeyHash, conf.Endpoint.String(), conf.Endpoint.Port, dstPeerKeyHash)
-							_, err = NmProxyServer.Server.WriteToUDP(buffer[:n+32], conf.Endpoint)
-							if err != nil {
-								log.Println("Failed to send to remote: ", err)
-							}
-							//continue
-						}
-					} else {
-						if remoteMap, ok := common.RelayPeerMap[dstPeerKeyHash]; ok {
-							if conf, ok := remoteMap[dstPeerKeyHash]; ok {
-								log.Printf("--------> Relaying BACK TO RELAYED NODE PKT [ SourceIP: %s ], [ SourceKeyHash: %s ], [ DstIP: %s ], [ DstHashKey: %s ] \n",
-									source.String(), srcPeerKeyHash, conf.Endpoint.String(), dstPeerKeyHash)
-								_, err = NmProxyServer.Server.WriteToUDP(buffer[:n+32], conf.Endpoint)
-								if err != nil {
-									log.Println("Failed to send to remote: ", err)
-								}
-								//continue
-							}
-						}
-
-					}
-
+			n, srcPeerKeyHash, dstPeerKeyHash, err = packet.ExtractInfo(buffer, n)
+			if err != nil {
+				log.Println("proxy transport message not found: ", err)
+				proxyTransportMsg = false
+			}
+			if proxyTransportMsg {
+				p.proxyIncomingPacket(buffer[:], source, n, srcPeerKeyHash, dstPeerKeyHash)
+				continue
+			} else {
+				// unknown peer to proxy -> check if extclient and handle it
+				if handleExtClients(buffer[:], n, source) {
+					continue
 				}
 				}
 
 
 			}
 			}
+			handleMsgs(buffer, n, source)
 
 
-			if peerInfo, ok := common.PeerKeyHashMap[srcPeerKeyHash]; ok {
-				if ifaceConf, ok := common.WgIFaceMap[peerInfo.Interface]; ok {
-					if peerI, ok := ifaceConf.PeerMap[peerInfo.PeerKey]; ok {
-						log.Printf("PROXING TO LOCAL!!!---> %s <<<< %s <<<<<<<< %s   [[ RECV PKT [SRCKEYHASH: %s], [DSTKEYHASH: %s], SourceIP: [%s] ]]\n",
-							peerI.LocalConn.RemoteAddr(), peerI.LocalConn.LocalAddr(),
-							fmt.Sprintf("%s:%d", source.IP.String(), source.Port), srcPeerKeyHash, dstPeerKeyHash, source.IP.String())
-						_, err = peerI.LocalConn.Write(buffer[:n])
-						if err != nil {
-							log.Println("Failed to proxy to Wg local interface: ", err)
-							//continue
-						}
-						continue
+		}
+	}
+}
 
 
-					}
+func handleMsgs(buffer []byte, n int, source *net.UDPAddr) {
+
+	msgType := binary.LittleEndian.Uint32(buffer[:4])
+	switch packet.MessageType(msgType) {
+	case packet.MessageMetricsType:
+		metricMsg, err := packet.ConsumeMetricPacket(buffer[:n])
+		// calc latency
+		if err == nil {
+			log.Printf("------->$$$$$ Recieved Metric Pkt: %+v, FROM:%s\n", metricMsg, source.String())
+			if metricMsg.Sender == common.WgIfaceMap.Iface.PublicKey {
+				latency := time.Now().UnixMilli() - metricMsg.TimeStamp
+				metrics.MetricsMapLock.Lock()
+				metric := metrics.MetricsMap[metricMsg.Reciever.String()]
+				metric.LastRecordedLatency = uint64(latency)
+				metric.ConnectionStatus = true
+				metric.TrafficRecieved += float64(n) / (1 << 20)
+				metrics.MetricsMap[metricMsg.Reciever.String()] = metric
+				metrics.MetricsMapLock.Unlock()
+			} else if metricMsg.Reciever == common.WgIfaceMap.Iface.PublicKey {
+				// proxy it back to the sender
+				log.Println("------------> $$$ SENDING back the metric pkt to the source: ", source.String())
+				_, err = NmProxyServer.Server.WriteToUDP(buffer[:n], source)
+				if err != nil {
+					log.Println("Failed to send metric packet to remote: ", err)
 				}
 				}
+				metrics.MetricsMapLock.Lock()
+				metric := metrics.MetricsMap[metricMsg.Sender.String()]
+				metric.ConnectionStatus = true
+				metric.TrafficRecieved += float64(n) / (1 << 20)
+				metrics.MetricsMap[metricMsg.Sender.String()] = metric
+				metrics.MetricsMapLock.Unlock()
+			}
+		}
+	case packet.MessageProxyUpdateType:
+		msg, err := packet.ConsumeProxyUpdateMsg(buffer[:n])
+		if err == nil {
+			switch msg.Action {
+			case packet.UpdateListenPort:
+				if peer, ok := common.WgIfaceMap.PeerMap[msg.Sender.String()]; ok {
+					peer.Mutex.Lock()
+					if peer.Config.PeerEndpoint.Port != int(msg.ListenPort) {
+						// update peer conn
+						peer.Config.PeerEndpoint.Port = int(msg.ListenPort)
+						common.WgIfaceMap.PeerMap[msg.Sender.String()] = peer
+						log.Println("--------> Resetting Proxy Conn For Peer ", msg.Sender.String())
+						peer.Mutex.Unlock()
+						peer.ResetConn()
+						return
+					}
+					peer.Mutex.Unlock()
 
 
+				}
 			}
 			}
-			if peerInfo, ok := common.ExtSourceIpMap[source.String()]; ok {
-				if ifaceConf, ok := common.WgIFaceMap[peerInfo.Interface]; ok {
-					if peerI, ok := ifaceConf.PeerMap[peerInfo.PeerKey]; ok {
-						log.Printf("PROXING TO LOCAL!!!---> %s <<<< %s <<<<<<<< %s   [[ RECV PKT [SRCKEYHASH: %s], [DSTKEYHASH: %s], SourceIP: [%s] ]]\n",
-							peerI.LocalConn.RemoteAddr(), peerI.LocalConn.LocalAddr(),
-							fmt.Sprintf("%s:%d", source.IP.String(), source.Port), srcPeerKeyHash, dstPeerKeyHash, source.IP.String())
-						_, err = peerI.LocalConn.Write(buffer[:origBufferLen])
-						if err != nil {
-							log.Println("Failed to proxy to Wg local interface: ", err)
-							//continue
-						}
-						continue
+		}
+	// consume handshake message for ext clients
+	case packet.MessageInitiationType:
 
 
-					}
+		err := packet.ConsumeHandshakeInitiationMsg(false, buffer[:n], source,
+			packet.NoisePublicKey(common.WgIfaceMap.Iface.PublicKey), packet.NoisePrivateKey(common.WgIfaceMap.Iface.PrivateKey))
+		if err != nil {
+			log.Println("---------> @@@ failed to decode HS: ", err)
+		}
+	}
+}
+
+func handleExtClients(buffer []byte, n int, source *net.UDPAddr) bool {
+	isExtClient := false
+	if peerInfo, ok := common.ExtSourceIpMap[source.String()]; ok {
+		if peerI, ok := common.WgIfaceMap.PeerMap[peerInfo.PeerKey]; ok {
+			peerI.Mutex.RLock()
+			peerI.Config.RecieverChan <- buffer[:n]
+			metrics.MetricsMapLock.Lock()
+			metric := metrics.MetricsMap[peerInfo.PeerKey]
+			metric.TrafficRecieved += float64(n) / (1 << 20)
+			metric.ConnectionStatus = true
+			metrics.MetricsMap[peerInfo.PeerKey] = metric
+			metrics.MetricsMapLock.Unlock()
+			peerI.Mutex.RUnlock()
+			isExtClient = true
+		}
+
+	}
+	return isExtClient
+}
+
+func (p *ProxyServer) proxyIncomingPacket(buffer []byte, source *net.UDPAddr, n int, srcPeerKeyHash, dstPeerKeyHash string) {
+	var err error
+	//log.Printf("--------> RECV PKT , [SRCKEYHASH: %s], SourceIP: [%s] \n", srcPeerKeyHash, source.IP.String())
+
+	if common.WgIfaceMap.IfaceKeyHash != dstPeerKeyHash && common.IsRelay {
+
+		log.Println("----------> Relaying######")
+		// check for routing map and forward to right proxy
+		if remoteMap, ok := common.RelayPeerMap[srcPeerKeyHash]; ok {
+			if conf, ok := remoteMap[dstPeerKeyHash]; ok {
+				log.Printf("--------> Relaying PKT [ SourceIP: %s:%d ], [ SourceKeyHash: %s ], [ DstIP: %s:%d ], [ DstHashKey: %s ] \n",
+					source.IP.String(), source.Port, srcPeerKeyHash, conf.Endpoint.String(), conf.Endpoint.Port, dstPeerKeyHash)
+				_, err = p.Server.WriteToUDP(buffer[:n+packet.MessageProxySize], conf.Endpoint)
+				if err != nil {
+					log.Println("Failed to send to remote: ", err)
 				}
 				}
+				return
 			}
 			}
-			// unknown peer to proxy -> check if extclient and handle it
-			// consume handshake message for ext clients
-			msgType := binary.LittleEndian.Uint32(buffer[:4])
-			switch msgType {
-			case packet.MessageInitiationType:
-
-				devPriv, devPubkey, err := packet.GetDeviceKeys(common.InterfaceName)
-				if err == nil {
-					err := packet.ConsumeHandshakeInitiationMsg(false, buffer[:origBufferLen], source, devPubkey, devPriv)
+		} else {
+			if remoteMap, ok := common.RelayPeerMap[dstPeerKeyHash]; ok {
+				if conf, ok := remoteMap[dstPeerKeyHash]; ok {
+					log.Printf("--------> Relaying BACK TO RELAYED NODE PKT [ SourceIP: %s ], [ SourceKeyHash: %s ], [ DstIP: %s ], [ DstHashKey: %s ] \n",
+						source.String(), srcPeerKeyHash, conf.Endpoint.String(), dstPeerKeyHash)
+					_, err = p.Server.WriteToUDP(buffer[:n+packet.MessageProxySize], conf.Endpoint)
 					if err != nil {
 					if err != nil {
-						log.Println("---------> @@@ failed to decode HS: ", err)
+						log.Println("Failed to send to remote: ", err)
 					}
 					}
-				} else {
-					log.Println("failed to get device keys: ", err)
+					return
 				}
 				}
 			}
 			}
 
 
 		}
 		}
+	}
+
+	if peerInfo, ok := common.PeerKeyHashMap[srcPeerKeyHash]; ok {
+
+		log.Printf("PROXING TO LOCAL!!!---> %s <<<< %s <<<<<<<< %s   [[ RECV PKT [SRCKEYHASH: %s], [DSTKEYHASH: %s], SourceIP: [%s] ]]\n",
+			peerInfo.LocalConn.RemoteAddr(), peerInfo.LocalConn.LocalAddr(),
+			fmt.Sprintf("%s:%d", source.IP.String(), source.Port), srcPeerKeyHash, dstPeerKeyHash, source.IP.String())
+		_, err = peerInfo.LocalConn.Write(buffer[:n])
+		if err != nil {
+			log.Println("Failed to proxy to Wg local interface: ", err)
+			//continue
+		}
+
+		go func(n int, peerKey string) {
+			metrics.MetricsMapLock.Lock()
+			metric := metrics.MetricsMap[peerKey]
+			metric.TrafficRecieved += float64(n) / (1 << 20)
+			metric.ConnectionStatus = true
+			metrics.MetricsMap[peerKey] = metric
+			metrics.MetricsMapLock.Unlock()
+		}(n, peerInfo.PeerKey)
+		return
 
 
 	}
 	}
+
 }
 }
 
 
 // Create - creats a proxy listener
 // Create - creats a proxy listener

+ 14 - 3
scripts/netclient-install.sh

@@ -148,9 +148,19 @@ case $(uname | tr A-Z a-z) in
 			arm*)
 			arm*)
 				dist=netclient-$CPU_ARCH
 				dist=netclient-$CPU_ARCH
 			;;
 			;;
-            mipsle)
+			mipsle)
                 dist=netclient-mipsle
                 dist=netclient-mipsle
 			;;
 			;;
+			mips)
+			    #If binary in the below condition is not compatible with your hardware, retry with other netclient-mips* binaries.
+				if [[ `printf '\0\1' | hexdump -e '/2 "%04x"'` -eq 0100 ]]; then
+					#Little Endian, tested and confirmed in GL-MT1300 OS "OpenWrt 19.07.8"
+					dist=netclient-mipsle-softfloat
+				else
+					#Big Endian, tested and confirmed in DSL-2750U OS "OpenWrt 22.03.2"
+					dist=netclient-mips-softfloat
+				fi
+			;;
 			*)
 			*)
 				fatal "$CPU_ARCH : cpu architecture not supported"
 				fatal "$CPU_ARCH : cpu architecture not supported"
     		esac
     		esac
@@ -240,6 +250,8 @@ if [ "${OS}" = "OpenWRT" ] || [ "${OS}" = "TurrisOS" ]; then
 		else
 		else
 			wget $curl_opts -O netclient.service.tmp https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/openwrt-daemon.sh
 			wget $curl_opts -O netclient.service.tmp https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/openwrt-daemon.sh
 		fi
 		fi
+	elif [ "${OS}" = "OpenWRT" ] && [ "$CPU_ARCH" = "mips" ]; then
+		wget $curl_opts -O netclient.service.tmp https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/openwrt-daemon.sh
 	else
 	else
 		cat << 'END_OF_FILE' > ./netclient.service.tmp
 		cat << 'END_OF_FILE' > ./netclient.service.tmp
 #!/bin/sh /etc/rc.common
 #!/bin/sh /etc/rc.common
@@ -292,5 +304,4 @@ END_OF_FILE
 	/etc/init.d/netclient start
 	/etc/init.d/netclient start
 else 
 else 
 	rm -f netclient
 	rm -f netclient
-fi
-
+fi

+ 291 - 186
scripts/nm-quick-interactive.sh

@@ -1,7 +1,5 @@
 #!/bin/bash
 #!/bin/bash
 
 
-set -e
-
 cat << "EOF"
 cat << "EOF"
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -19,111 +17,306 @@ cat << "EOF"
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 EOF
 EOF
 
 
+if [ $(id -u) -ne 0 ]; then
+   echo "This script must be run as root"
+   exit 1
+fi
+
+if [ -z "$1" ]; then
+	echo "-----------------------------------------------------"
+	echo "Would you like to install Netmaker Community Edition (CE), or Netmaker Enterprise Edition (EE)?"
+	echo "EE will require you to create an account at https://dashboard.license.netmaker.io"
+	echo "-----------------------------------------------------"
+	select install_option in "Community Edition" "Enterprise Edition"; do
+	case $REPLY in
+		1)
+		echo "installing Netmaker CE"
+		INSTALL_TYPE="ce"
+		break
+		;;      
+		2)
+		echo "installing Netmaker EE"
+		INSTALL_TYPE="ee"
+		break
+		;;
+		*) echo "invalid option $REPLY";;
+	esac
+	done
+elif [ "$1" = "ce" ]; then
+	echo "installing Netmaker CE"
+	INSTALL_TYPE="ce"
+elif [ "$1" = "ee" ]; then
+	echo "installing Netmaker EE"
+	INSTALL_TYPE="ee"
+else
+	echo "install type invalid (options: 'ce, ee')"
+	exit 1
+fi
+
+wait_seconds() {(
+  for ((a=1; a <= $1; a++))
+  do
+    echo ". . ."
+    sleep 1
+  done
+)}
+
+confirm() {(
+  while true; do
+      read -p 'Does everything look right? [y/n]: ' yn
+      case $yn in
+          [Yy]* ) override="true"; break;;
+          [Nn]* ) echo "exiting..."; exit 1;;
+          * ) echo "Please answer yes or no.";;
+      esac
+  done
+)}
+
+echo "checking dependencies..."
+
+OS=$(uname)
+
+if [ -f /etc/debian_version ]; then
+	dependencies="wireguard wireguard-tools jq docker.io docker-compose"
+	update_cmd='apt update'
+	install_cmd='apt-get install -y'
+elif [ -f /etc/alpine-release ]; then
+	dependencies="wireguard jq docker.io docker-compose"
+	update_cmd='apk update'
+	install_cmd='apk --update add'
+elif [ -f /etc/centos-release ]; then
+	dependencies="wireguard jq docker.io docker-compose"
+	update_cmd='yum update'
+	install_cmd='yum install -y'
+elif [ -f /etc/fedora-release ]; then
+	dependencies="wireguard jq docker.io docker-compose"
+	update_cmd='dnf update'
+	install_cmd='dnf install -y'
+elif [ -f /etc/redhat-release ]; then
+	dependencies="wireguard jq docker.io docker-compose"
+	update_cmd='yum update'
+	install_cmd='yum install -y'
+elif [ -f /etc/arch-release ]; then
+    	dependecies="wireguard-tools jq docker.io docker-compose"
+	update_cmd='pacman -Sy'
+	install_cmd='pacman -S --noconfirm'
+elif [ "${OS}" = "FreeBSD" ]; then
+	dependencies="wireguard wget jq docker.io docker-compose"
+	update_cmd='pkg update'
+	install_cmd='pkg install -y'
+elif [ -f /etc/turris-version ]; then
+	dependencies="wireguard-tools bash jq docker.io docker-compose"
+	OS="TurrisOS"
+	update_cmd='opkg update'	
+	install_cmd='opkg install'
+elif [ -f /etc/openwrt_release ]; then
+	dependencies="wireguard-tools bash jq docker.io docker-compose"
+	OS="OpenWRT"
+	update_cmd='opkg update'	
+	install_cmd='opkg install'
+else
+	install_cmd=''
+fi
+
+if [ -z "${install_cmd}" ]; then
+        echo "OS unsupported for automatic dependency install"
+	exit 1
+fi
+
+set -- $dependencies
+
+${update_cmd}
+
+while [ -n "$1" ]; do
+	if [ "${OS}" = "FreeBSD" ]; then
+		is_installed=$(pkg check -d $1 | grep "Checking" | grep "done")
+		if [ "$is_installed" != "" ]; then
+			echo "  " $1 is installed
+		else
+			echo "  " $1 is not installed. Attempting install.
+			${install_cmd} $1
+			sleep 5
+			is_installed=$(pkg check -d $1 | grep "Checking" | grep "done")
+			if [ "$is_installed" != "" ]; then
+				echo "  " $1 is installed
+			elif [ -x "$(command -v $1)" ]; then
+				echo "  " $1 is installed
+			else
+				echo "  " FAILED TO INSTALL $1
+				echo "  " This may break functionality.
+			fi
+		fi	
+	else
+		if [ "${OS}" = "OpenWRT" ] || [ "${OS}" = "TurrisOS" ]; then
+			is_installed=$(opkg list-installed $1 | grep $1)
+		else
+			is_installed=$(dpkg-query -W --showformat='${Status}\n' $1 | grep "install ok installed")
+		fi
+		if [ "${is_installed}" != "" ]; then
+			echo "    " $1 is installed
+		else
+			echo "    " $1 is not installed. Attempting install.
+			${install_cmd} $1
+			sleep 5
+			if [ "${OS}" = "OpenWRT" ] || [ "${OS}" = "TurrisOS" ]; then
+				is_installed=$(opkg list-installed $1 | grep $1)
+			else
+				is_installed=$(dpkg-query -W --showformat='${Status}\n' $1 | grep "install ok installed")
+			fi
+			if [ "${is_installed}" != "" ]; then
+				echo "    " $1 is installed
+			elif [ -x "$(command -v $1)" ]; then
+				echo "  " $1 is installed
+			else
+				echo "  " FAILED TO INSTALL $1
+				echo "  " This may break functionality.
+			fi
+		fi
+	fi
+	shift
+done
+
+echo "-----------------------------------------------------"
+echo "dependency check complete"
+echo "-----------------------------------------------------"
+
+wait_seconds 3
+
+set -e
+
 NETMAKER_BASE_DOMAIN=nm.$(curl -s ifconfig.me | tr . -).nip.io
 NETMAKER_BASE_DOMAIN=nm.$(curl -s ifconfig.me | tr . -).nip.io
 COREDNS_IP=$(ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p')
 COREDNS_IP=$(ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p')
 SERVER_PUBLIC_IP=$(curl -s ifconfig.me)
 SERVER_PUBLIC_IP=$(curl -s ifconfig.me)
 MASTER_KEY=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 30 ; echo '')
 MASTER_KEY=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 30 ; echo '')
-EMAIL="$(echo $RANDOM | md5sum  | head -c 16)@email.com"
-
-echo "Default Base Domain: $NETMAKER_BASE_DOMAIN"
-echo "To Override, add a Wildcard (*.netmaker.example.com) DNS record pointing to $SERVER_PUBLIC_IP"
-echo "Or, add three DNS records pointing to $SERVER_PUBLIC_IP for the following (Replacing 'netmaker.example.com' with the domain of your choice):"
-echo "   dashboard.netmaker.example.com"
-echo "         api.netmaker.example.com"
-echo "        grpc.netmaker.example.com"
+MQ_PASSWORD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 30 ; echo '')
+DOMAIN_TYPE=""
+
+echo "-----------------------------------------------------"
+echo "Would you like to use your own domain for netmaker, or an auto-generated domain?"
+echo "To use your own domain, add a Wildcard DNS record (e.x: *.netmaker.example.com) pointing to $SERVER_PUBLIC_IP"
 echo "-----------------------------------------------------"
 echo "-----------------------------------------------------"
-read -p "Domain (Hit 'enter' to use $NETMAKER_BASE_DOMAIN): " domain
-read -p "Email for LetsEncrypt (Hit 'enter' to use $EMAIL): " email
+select domain_option in "Auto Generated ($NETMAKER_BASE_DOMAIN)" "Custom Domain (e.x: netmaker.example.com)"; do
+  case $REPLY in
+    1)
+      echo "using $NETMAKER_BASE_DOMAIN for base domain"
+      DOMAIN_TYPE="auto"
+	  break
+      ;;      
+    2)
+      read -p "Enter Custom Domain (make sure  *.domain points to $SERVER_PUBLIC_IP first): " domain
+      NETMAKER_BASE_DOMAIN=$domain
+      echo "using $NETMAKER_BASE_DOMAIN"
+      DOMAIN_TYPE="custom"
+      break
+      ;;
+    *) echo "invalid option $REPLY";;
+  esac
+done
 
 
-if [ -n "$domain" ]; then
-  NETMAKER_BASE_DOMAIN=$domain
-fi
-if [ -n "$email" ]; then
-  EMAIL=$email
+wait_seconds 2
+
+echo "-----------------------------------------------------"
+echo "The following subdomains will be used:"
+echo "          dashboard.$NETMAKER_BASE_DOMAIN"
+echo "                api.$NETMAKER_BASE_DOMAIN"
+echo "             broker.$NETMAKER_BASE_DOMAIN"
+
+if [ "$INSTALL_TYPE" = "ee" ]; then
+	echo "         prometheus.$NETMAKER_BASE_DOMAIN"
+	echo "  netmaker-exporter.$NETMAKER_BASE_DOMAIN"
+	echo "            grafana.$NETMAKER_BASE_DOMAIN"
 fi
 fi
 
 
-while true; do
-    read -p 'Configure a default network automatically? [y/n]: ' yn
-    case $yn in
-        [Yy]* ) MESH_SETUP="true"; break;;
-        [Nn]* ) MESH_SETUP="false"; break;;
-        * ) echo "Please answer yes or no.";;
-    esac
-done
+echo "-----------------------------------------------------"
 
 
-while true; do
-    read -p 'Configure a VPN gateway automatically? [y/n]: ' yn
-    case $yn in
-        [Yy]* ) VPN_SETUP="true"; break;;
-        [Nn]* ) VPN_SETUP="false"; break;;
-        * ) echo "Please answer yes or no.";;
-    esac
-done
+if [[ "$DOMAIN_TYPE" == "custom" ]]; then
+	echo "before continuing, confirm DNS is configured correctly, with records pointing to $SERVER_PUBLIC_IP"
+	confirm
+fi
+
+wait_seconds 1
+
+if [ "$INSTALL_TYPE" = "ee" ]; then
+
+	echo "-----------------------------------------------------"
+	echo "Provide Details for EE installation:"
+	echo "    1. Log into https://dashboard.license.netmaker.io"
+	echo "    2. Copy License Key Value: https://dashboard.license.netmaker.io/license-keys"
+	echo "    3. Retrieve Account ID: https://dashboard.license.netmaker.io/user"
+	echo "    4. note email address"
+	echo "-----------------------------------------------------"
+	unset LICENSE_KEY
+	while [ -z "$LICENSE_KEY" ]; do
+		read -p "License Key: " LICENSE_KEY
+	done
+	unset ACCOUNT_ID
+	while [ -z ${ACCOUNT_ID} ]; do
+		read -p "Account ID: " ACCOUNT_ID
+	done
 
 
-if [ "${VPN_SETUP}" == "true" ]; then
-while :; do
-    read -ep '# of VPN clients to configure by default: ' num_clients
-    [[ $num_clients =~ ^[[:digit:]]+$ ]] || continue
-    (( ( (num_clients=(10#$num_clients)) <= 200 ) && num_clients >= 0 )) || continue
-    break
-done
 fi
 fi
 
 
-if [ -n "$num_clients" ]; then
-  NUM_CLIENTS=$num_clients
+unset GET_EMAIL
+unset RAND_EMAIL
+RAND_EMAIL="$(echo $RANDOM | md5sum  | head -c 16)@email.com"
+read -p "Email Address for Domain Registration (click 'enter' to use $RAND_EMAIL): " GET_EMAIL
+if [ -z "$GET_EMAIL" ]; then
+  echo "using rand email"
+  EMAIL="$RAND_EMAIL"
+else
+  EMAIL="$GET_EMAIL"
 fi
 fi
 
 
+wait_seconds 2
+
 echo "-----------------------------------------------------------------"
 echo "-----------------------------------------------------------------"
 echo "                SETUP ARGUMENTS"
 echo "                SETUP ARGUMENTS"
 echo "-----------------------------------------------------------------"
 echo "-----------------------------------------------------------------"
 echo "        domain: $NETMAKER_BASE_DOMAIN"
 echo "        domain: $NETMAKER_BASE_DOMAIN"
 echo "         email: $EMAIL"
 echo "         email: $EMAIL"
 echo "     public ip: $SERVER_PUBLIC_IP"
 echo "     public ip: $SERVER_PUBLIC_IP"
-echo "   setup mesh?: $MESH_SETUP"
-echo "    setup vpn?: $VPN_SETUP"
-if [ "${VPN_SETUP}" == "true" ]; then
-echo "     # clients: $NUM_CLIENTS"
+if [ "$INSTALL_TYPE" = "ee" ]; then
+	echo "       license: $LICENSE_KEY"
+	echo "    account id: $ACCOUNT_ID"
 fi
 fi
+echo "-----------------------------------------------------------------"
+echo "Confirm Settings for Installation"
+echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
 
 
-while true; do
-    read -p 'Does everything look right? [y/n]: ' yn
-    case $yn in
-        [Yy]* ) override="true"; break;;
-        [Nn]* ) echo "exiting..."; exit;;
-        * ) echo "Please answer yes or no.";;
-    esac
-done
-
+confirm
 
 
-echo "Beginning installation in 5 seconds..."
 
 
-sleep 5
+echo "-----------------------------------------------------------------"
+echo "Beginning installation..."
+echo "-----------------------------------------------------------------"
 
 
-if [ -f "/root/docker-compose.yml" ]; then
-    echo "Using existing docker compose"
-else 
-    echo "Pulling docker compose"
-    wget -q -O /root/docker-compose.yml https://raw.githubusercontent.com/gravitl/netmaker/master/compose/docker-compose.yml
-fi
+wait_seconds 3
 
 
+echo "Pulling config files..."
 
 
-if [ -f "/root/mosquitto.conf" ]; then
-    echo "Using existing mosquitto config"
-else
-    echo "Pulling mosquitto config"
-    wget -q -O /root/mosquitto.conf https://raw.githubusercontent.com/gravitl/netmaker/master/docker/mosquitto.conf
+COMPOSE_URL="https://raw.githubusercontent.com/gravitl/netmaker/master/compose/docker-compose.yml" 
+CADDY_URL="https://raw.githubusercontent.com/gravitl/netmaker/master/docker/Caddyfile"
+if [ "$INSTALL_TYPE" = "ee" ]; then
+	COMPOSE_URL="https://raw.githubusercontent.com/gravitl/netmaker/master/compose/docker-compose.ee.yml" 
+	CADDY_URL="https://raw.githubusercontent.com/gravitl/netmaker/master/docker/Caddyfile-EE"
 fi
 fi
 
 
+wget -O /root/docker-compose.yml $COMPOSE_URL && wget -O /root/mosquitto.conf https://raw.githubusercontent.com/gravitl/netmaker/master/docker/mosquitto.conf && wget -O /root/Caddyfile $CADDY_URL && wget -q -O /root/wait.sh https://raw.githubusercontent.com/gravitl/netmaker/master/docker/wait.sh && chmod +x /root/wait.sh
 
 
 mkdir -p /etc/netmaker
 mkdir -p /etc/netmaker
 
 
-echo "Setting docker-compose..."
+echo "Setting docker-compose and Caddyfile..."
 
 
-sed -i "s/NETMAKER_BASE_DOMAIN/$NETMAKER_BASE_DOMAIN/g" /root/docker-compose.yml
 sed -i "s/SERVER_PUBLIC_IP/$SERVER_PUBLIC_IP/g" /root/docker-compose.yml
 sed -i "s/SERVER_PUBLIC_IP/$SERVER_PUBLIC_IP/g" /root/docker-compose.yml
+sed -i "s/NETMAKER_BASE_DOMAIN/$NETMAKER_BASE_DOMAIN/g" /root/Caddyfile
+sed -i "s/NETMAKER_BASE_DOMAIN/$NETMAKER_BASE_DOMAIN/g" /root/docker-compose.yml
 sed -i "s/REPLACE_MASTER_KEY/$MASTER_KEY/g" /root/docker-compose.yml
 sed -i "s/REPLACE_MASTER_KEY/$MASTER_KEY/g" /root/docker-compose.yml
-sed -i "s/YOUR_EMAIL/$EMAIL/g" /root/docker-compose.yml
-
+sed -i "s/YOUR_EMAIL/$EMAIL/g" /root/Caddyfile
+sed -i "s/REPLACE_MQ_ADMIN_PASSWORD/$MQ_PASSWORD/g" /root/docker-compose.yml 
+if [ "$INSTALL_TYPE" = "ee" ]; then
+	sed -i "s~YOUR_LICENSE_KEY~$LICENSE_KEY~g" /root/docker-compose.yml 
+	sed -i "s/YOUR_ACCOUNT_ID/$ACCOUNT_ID/g" /root/docker-compose.yml 
+fi
 echo "Starting containers..."
 echo "Starting containers..."
 
 
 docker-compose -f /root/docker-compose.yml up -d
 docker-compose -f /root/docker-compose.yml up -d
@@ -132,13 +325,13 @@ sleep 2
 
 
 test_connection() {
 test_connection() {
 
 
-echo "Testing Traefik setup (please be patient, this may take 1-2 minutes)"
+echo "Testing Caddy setup (please be patient, this may take 1-2 minutes)"
 for i in 1 2 3 4 5 6
 for i in 1 2 3 4 5 6
 do
 do
 curlresponse=$(curl -vIs https://api.${NETMAKER_BASE_DOMAIN} 2>&1)
 curlresponse=$(curl -vIs https://api.${NETMAKER_BASE_DOMAIN} 2>&1)
 
 
 if [[ "$i" == 6 ]]; then
 if [[ "$i" == 6 ]]; then
-  echo "    Traefik is having an issue setting up certificates, please investigate (docker logs traefik)"
+  echo "    Caddy is having an issue setting up certificates, please investigate (docker logs caddy)"
   echo "    Exiting..."
   echo "    Exiting..."
   exit 1
   exit 1
 elif [[ "$curlresponse" == *"failed to verify the legitimacy of the server"* ]]; then
 elif [[ "$curlresponse" == *"failed to verify the legitimacy of the server"* ]]; then
@@ -157,140 +350,52 @@ done
 
 
 
 
 setup_mesh() {( set -e
 setup_mesh() {( set -e
-sleep 5
+
+wait_seconds 5
+
 echo "Creating netmaker network (10.101.0.0/16)"
 echo "Creating netmaker network (10.101.0.0/16)"
 
 
 curl -s -o /dev/null -d '{"addressrange":"10.101.0.0/16","netid":"netmaker"}' -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://api.${NETMAKER_BASE_DOMAIN}/api/networks
 curl -s -o /dev/null -d '{"addressrange":"10.101.0.0/16","netid":"netmaker"}' -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://api.${NETMAKER_BASE_DOMAIN}/api/networks
 
 
-sleep 5
+wait_seconds 5
 
 
 echo "Creating netmaker access key"
 echo "Creating netmaker access key"
 
 
 curlresponse=$(curl -s -d '{"uses":99999,"name":"netmaker-key"}' -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://api.${NETMAKER_BASE_DOMAIN}/api/networks/netmaker/keys)
 curlresponse=$(curl -s -d '{"uses":99999,"name":"netmaker-key"}' -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://api.${NETMAKER_BASE_DOMAIN}/api/networks/netmaker/keys)
 ACCESS_TOKEN=$(jq -r '.accessstring' <<< ${curlresponse})
 ACCESS_TOKEN=$(jq -r '.accessstring' <<< ${curlresponse})
 
 
-sleep 5
+wait_seconds 3
 
 
 echo "Configuring netmaker server as ingress gateway"
 echo "Configuring netmaker server as ingress gateway"
 
 
-curlresponse=$(curl -s -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://api.${NETMAKER_BASE_DOMAIN}/api/nodes/netmaker)
-SERVER_ID=$(jq -r '.[0].id' <<< ${curlresponse})
+for i in 1 2 3 4 5 6
+do
+	echo "    waiting for server node to become available"
+	wait_seconds 5
+	curlresponse=$(curl -s -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://api.${NETMAKER_BASE_DOMAIN}/api/nodes/netmaker)
+	SERVER_ID=$(jq -r '.[0].id' <<< ${curlresponse})
+	if [[ "$i" == 6 && -z "$SERVER_ID" ]]; then
+		echo "    Netmaker is having issues configuring itself, please investigate (docker logs netmaker)"
+		echo "    Exiting..."
+		exit 1
+	elif [ -z "$SERVER_ID" ]; then
+		echo "    server node not yet configured, retrying..."
+	else
+		echo "    server node is now availble, continuing"
+		break
+	fi
+done
 
 
 curl -o /dev/null -s -X POST -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://api.${NETMAKER_BASE_DOMAIN}/api/nodes/netmaker/$SERVER_ID/createingress
 curl -o /dev/null -s -X POST -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://api.${NETMAKER_BASE_DOMAIN}/api/nodes/netmaker/$SERVER_ID/createingress
 
 
-sleep 5
-)}
-
-mesh_connect_logs() {
-sleep 5
-echo "-----------------------------------------------------------------"
-echo "-----------------------------------------------------------------"
-echo "DEFAULT NETWORK CLIENT INSTALL INSTRUCTIONS:"
-echo "-----------------------------------------------------------------"
-echo "-----------------------------------------------------------------"
-sleep 5
-echo "For Linux and Mac clients, install with the following command:"
-echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
-echo "curl -sfL https://raw.githubusercontent.com/gravitl/netmaker/develop/scripts/netclient-install.sh | sudo KEY=$VPN_ACCESS_TOKEN sh -"
-sleep 5
-echo "-----------------------------------------------------------------"
-echo "-----------------------------------------------------------------"
-echo "For Windows clients, perform the following from powershell, as administrator:"
-echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
-echo "1. Make sure WireGuardNT is installed - https://download.wireguard.com/windows-client/wireguard-installer.exe"
-echo "2. Download netclient.exe - wget https://github.com/gravitl/netmaker/releases/download/latest/netclient.exe"
-echo "3. Install Netclient - powershell.exe .\\netclient.exe join -t $VPN_ACCESS_TOKEN"
-echo "4. Whitelist C:\ProgramData\Netclient in Windows Defender"
-sleep 5
-echo "-----------------------------------------------------------------"
-echo "-----------------------------------------------------------------"
-echo "For Android and iOS clients, perform the following steps:"
-echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
-echo "1. Log into UI at dashboard.$NETMAKER_BASE_DOMAIN"
-echo "2. Navigate to \"EXTERNAL CLIENTS\" tab"
-echo "3. Select the gateway and create clients"
-echo "4. Scan the QR Code from WireGuard app in iOS or Android"
-echo "-----------------------------------------------------------------"
-echo "-----------------------------------------------------------------"
-sleep 5
-}
-
-setup_vpn() {( set -e
-
-echo "Creating vpn network (10.201.0.0/16)"
-
-sleep 5
-curl -s -o /dev/null -d '{"addressrange":"10.201.0.0/16","netid":"vpn","defaultextclientdns":"8.8.8.8"}' -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://api.${NETMAKER_BASE_DOMAIN}/api/networks
-
-sleep 5
-
-echo "Configuring netmaker server as vpn inlet..."
-
-curlresponse=$(curl -s -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://api.${NETMAKER_BASE_DOMAIN}/api/nodes/vpn)
-SERVER_ID=$(jq -r '.[0].id' <<< ${curlresponse})
-
-curl -s -o /dev/null -X POST -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://api.${NETMAKER_BASE_DOMAIN}/api/nodes/vpn/$SERVER_ID/createingress
-
-echo "Waiting 10 seconds for server to apply configuration..."
-
-sleep 10
-
-
-echo "Configuring netmaker server vpn gateway..."
-
-[ -z "$GATEWAY_IFACE" ] && GATEWAY_IFACE=$(ip -4 route ls | grep default | grep -Po '(?<=dev )(\S+)')
-
-echo "Gateway iface: $GATEWAY_IFACE"
-
-curlresponse=$(curl -s -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://api.${NETMAKER_BASE_DOMAIN}/api/nodes/vpn)
-SERVER_ID=$(jq -r '.[0].id' <<< ${curlresponse})
-
-EGRESS_JSON=$( jq -n \
-                  --arg gw "$GATEWAY_IFACE" \
-                  '{ranges: ["0.0.0.0/0","::/0"], interface: $gw}' )
-
-echo "Egress json: $EGRESS_JSON"
-curl -s -o /dev/null -X POST -d "$EGRESS_JSON" -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://api.${NETMAKER_BASE_DOMAIN}/api/nodes/vpn/$SERVER_ID/creategateway
-
-echo "Creating client configs..."
-
-for ((a=1; a <= $NUM_CLIENTS; a++))
-do
-        CLIENT_JSON=$( jq -n \
-                  --arg clientid "vpnclient-$a" \
-                  '{clientid: $clientid}' )
-
-        curl -s -o /dev/null -d "$CLIENT_JSON" -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://api.${NETMAKER_BASE_DOMAIN}/api/extclients/vpn/$SERVER_ID
-done
-sleep 5
 )}
 )}
 
 
-vpn_connect_logs() {
-sleep 5
-echo "-----------------------------------------------------------------"
-echo "-----------------------------------------------------------------"
-echo "VPN GATEWAY CLIENT INSTALL INSTRUCTIONS:"
-echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
-echo "1. log into dashboard.$NETMAKER_BASE_DOMAIN"
-echo "2. Navigate to \"EXTERNAL CLIENTS\" tab"
-echo "3. Download or scan a client config (vpnclient-x) to the appropriate device"
-echo "4. Follow the steps for your system to configure WireGuard on the appropriate device"
-echo "5. Create and delete clients as necessary. Changes to netmaker server settings require regenerating ext clients."
-echo "-----------------------------------------------------------------"
-echo "-----------------------------------------------------------------"
-sleep 5
-}
-
 set +e
 set +e
 test_connection
 test_connection
 
 
-if [ "${MESH_SETUP}" != "false" ]; then
-        setup_mesh
-fi
+wait_seconds 3
 
 
-if [ "${VPN_SETUP}" == "true" ]; then
-        setup_vpn
-fi
+setup_mesh
 
 
 echo "-----------------------------------------------------------------"
 echo "-----------------------------------------------------------------"
 echo "-----------------------------------------------------------------"
 echo "-----------------------------------------------------------------"

+ 3 - 1
scripts/nm-quick.sh

@@ -128,6 +128,7 @@ sleep 5
 echo "setting mosquitto.conf..."
 echo "setting mosquitto.conf..."
 
 
 wget -q -O /root/mosquitto.conf https://raw.githubusercontent.com/gravitl/netmaker/master/docker/mosquitto.conf
 wget -q -O /root/mosquitto.conf https://raw.githubusercontent.com/gravitl/netmaker/master/docker/mosquitto.conf
+wget -q -O /root/Caddyfile https://raw.githubusercontent.com/gravitl/netmaker/master/docker/Caddyfile
 wget -q -O /root/wait.sh https://raw.githubusercontent.com/gravitl/netmaker/master/docker/wait.sh
 wget -q -O /root/wait.sh https://raw.githubusercontent.com/gravitl/netmaker/master/docker/wait.sh
 chmod +x /root/wait.sh
 chmod +x /root/wait.sh
 echo "setting docker-compose..."
 echo "setting docker-compose..."
@@ -136,10 +137,11 @@ mkdir -p /etc/netmaker
 
 
 wget -q -O /root/docker-compose.yml https://raw.githubusercontent.com/gravitl/netmaker/master/compose/docker-compose.yml
 wget -q -O /root/docker-compose.yml https://raw.githubusercontent.com/gravitl/netmaker/master/compose/docker-compose.yml
 sed -i "s/NETMAKER_BASE_DOMAIN/$NETMAKER_BASE_DOMAIN/g" /root/docker-compose.yml
 sed -i "s/NETMAKER_BASE_DOMAIN/$NETMAKER_BASE_DOMAIN/g" /root/docker-compose.yml
+sed -i "s/NETMAKER_BASE_DOMAIN/$NETMAKER_BASE_DOMAIN/g" /root/Caddyfile
 sed -i "s/SERVER_PUBLIC_IP/$SERVER_PUBLIC_IP/g" /root/docker-compose.yml
 sed -i "s/SERVER_PUBLIC_IP/$SERVER_PUBLIC_IP/g" /root/docker-compose.yml
 sed -i "s/COREDNS_IP/$COREDNS_IP/g" /root/docker-compose.yml
 sed -i "s/COREDNS_IP/$COREDNS_IP/g" /root/docker-compose.yml
 sed -i "s/REPLACE_MASTER_KEY/$MASTER_KEY/g" /root/docker-compose.yml
 sed -i "s/REPLACE_MASTER_KEY/$MASTER_KEY/g" /root/docker-compose.yml
-sed -i "s/YOUR_EMAIL/$EMAIL/g" /root/docker-compose.yml
+sed -i "s/YOUR_EMAIL/$EMAIL/g" /root/Caddyfile
 sed -i "s/REPLACE_MQ_ADMIN_PASSWORD/$MQ_ADMIN_PASSWORD/g" /root/docker-compose.yml
 sed -i "s/REPLACE_MQ_ADMIN_PASSWORD/$MQ_ADMIN_PASSWORD/g" /root/docker-compose.yml
 echo "starting containers..."
 echo "starting containers..."
 
 

+ 6 - 1
servercfg/serverconf.go

@@ -236,7 +236,12 @@ func GetMessageQueueEndpoint() (string, bool) {
 	} else if config.Config.Server.MQHOST != "" {
 	} else if config.Config.Server.MQHOST != "" {
 		host = config.Config.Server.MQHOST
 		host = config.Config.Server.MQHOST
 	}
 	}
-	secure := strings.Contains(host, "mqtts") || strings.Contains(host, "ssl")
+	secure := strings.Contains(host, "wss") || strings.Contains(host, "ssl")
+	if secure {
+		host = "wss://" + host
+	} else {
+		host = "ws://" + host
+	}
 	return host + ":" + GetMQServerPort(), secure
 	return host + ":" + GetMQServerPort(), secure
 }
 }
 
 

+ 1 - 1
servercfg/sqlconf.go

@@ -36,7 +36,7 @@ func GetSQLPort() int32 {
 	return port
 	return port
 }
 }
 func GetSQLUser() string {
 func GetSQLUser() string {
-	user := "posgres"
+	user := "postgres"
 	if os.Getenv("SQL_USER") != "" {
 	if os.Getenv("SQL_USER") != "" {
 		user = os.Getenv("SQL_USER")
 		user = os.Getenv("SQL_USER")
 	} else if config.Config.SQL.Username != "" {
 	} else if config.Config.SQL.Username != "" {

+ 1 - 30
swagger.yaml

@@ -746,7 +746,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.16.2
+    version: 0.17.0
 paths:
 paths:
     /api/dns:
     /api/dns:
         get:
         get:
@@ -1272,24 +1272,6 @@ paths:
             summary: Update keys for a network.
             summary: Update keys for a network.
             tags:
             tags:
                 - networks
                 - networks
-    /api/networks/{networkname}/nodelimit:
-        put:
-            operationId: updateNetworkNodeLimit
-            parameters:
-                - description: Network Name
-                  in: path
-                  name: networkname
-                  required: true
-                  type: string
-                  x-go-name: NetworkName
-            responses:
-                "200":
-                    $ref: '#/responses/networkBodyResponse'
-            schemes:
-                - https
-            summary: Update a network's node limit.
-            tags:
-                - networks
     /api/nodes:
     /api/nodes:
         get:
         get:
             operationId: getAllNodes
             operationId: getAllNodes
@@ -1602,17 +1584,6 @@ paths:
             summary: Authenticate to make further API calls related to a network.
             summary: Authenticate to make further API calls related to a network.
             tags:
             tags:
                 - nodes
                 - nodes
-    /api/nodes/adm/{network}/lastmodified:
-        get:
-            operationId: getLastModified
-            responses:
-                "200":
-                    $ref: '#/responses/nodeLastModifiedResponse'
-            schemes:
-                - https
-            summary: Get the time that a network of nodes was last modified.
-            tags:
-                - nodes
     /api/oauth/login:
     /api/oauth/login:
         get:
         get:
             operationId: HandleAuthLogin
             operationId: HandleAuthLogin