Browse Source

Merge pull request #1901 from gravitl/netclient_refactor_latest

Netclient refactor latest
dcarns 2 years ago
parent
commit
4c0a846435
100 changed files with 3145 additions and 7648 deletions
  1. 0 566
      .github/workflows/buildandrelease.yml
  2. 0 51
      .github/workflows/publish-netclient-docker-userspace.yml
  3. 0 99
      .github/workflows/publish-netclient-docker.yml
  4. 1 0
      auth/auth.go
  5. 6 1
      compose/docker-compose.yml
  6. 4 0
      config/config.go
  7. 1 0
      controllers/controller.go
  8. 37 49
      controllers/dns.go
  9. 1 1
      controllers/dns_test.go
  10. 17 18
      controllers/docs.go
  11. 23 8
      controllers/ext_client.go
  12. 190 0
      controllers/hosts.go
  13. 16 60
      controllers/network.go
  14. 1 1
      controllers/network_test.go
  15. 355 377
      controllers/node.go
  16. 11 11
      controllers/node_test.go
  17. 19 14
      controllers/relay.go
  18. 68 90
      database/database.go
  19. 6 0
      docker/Caddyfile
  20. 5 0
      docker/Caddyfile-EE
  21. 8 8
      ee/ee_controllers/metrics.go
  22. 4 4
      ee/ee_controllers/networkusers.go
  23. 14 13
      ee/logic/failover.go
  24. 0 1
      ee/util.go
  25. 23 56
      go.mod
  26. 22 231
      go.sum
  27. 1 1
      k8s/server/netmaker-server.yaml
  28. 15 4
      logic/extpeers.go
  29. 64 64
      logic/gateway.go
  30. 297 0
      logic/hosts.go
  31. 0 67
      logic/metrics.go
  32. 13 14
      logic/metrics/metrics.go
  33. 37 134
      logic/networks.go
  34. 248 390
      logic/nodes.go
  35. 496 161
      logic/peers.go
  36. 1 1
      logic/pro/networkuser.go
  37. 2 2
      logic/pro/networkuser_test.go
  38. 2 2
      logic/pro/proacls/nodes.go
  39. 35 18
      logic/relay.go
  40. 0 305
      logic/server.go
  41. 6 5
      logic/telemetry.go
  42. 6 26
      logic/util.go
  43. 31 0
      logic/version.go
  44. 35 0
      logic/version_test.go
  45. 5 176
      logic/wireguard.go
  46. 25 19
      logic/zombie.go
  47. 21 6
      main.go
  48. 105 0
      models/api_host.go
  49. 175 0
      models/api_node.go
  50. 2 0
      models/extclient.go
  51. 62 0
      models/host.go
  52. 12 7
      models/metrics.go
  53. 11 7
      models/mqtt.go
  54. 252 138
      models/node.go
  55. 23 3
      models/structs.go
  56. 2 2
      mq/dynsec.go
  57. 89 0
      mq/dynsec_clients.go
  58. 47 1
      mq/dynsec_helper.go
  59. 46 47
      mq/handlers.go
  60. 135 101
      mq/publishers.go
  61. 12 4
      mq/util.go
  62. 0 9
      netclient/Dockerfile
  63. 0 44
      netclient/auth/auth.go
  64. 0 42
      netclient/bin-maker.sh
  65. 0 15
      netclient/build/netclient.service
  66. 0 148
      netclient/cli_options/cmds.go
  67. 0 229
      netclient/cli_options/flags.go
  68. 0 167
      netclient/command/commands.go
  69. 0 348
      netclient/config/config.go
  70. 0 31
      netclient/config/util.go
  71. 0 94
      netclient/daemon/common.go
  72. 0 135
      netclient/daemon/freebsd.go
  73. 0 117
      netclient/daemon/macos.go
  74. 0 139
      netclient/daemon/systemd.go
  75. 0 106
      netclient/daemon/windows.go
  76. 0 78
      netclient/functions/clientconfig.go
  77. 0 425
      netclient/functions/common.go
  78. 0 60
      netclient/functions/connection.go
  79. 0 350
      netclient/functions/daemon.go
  80. 0 19
      netclient/functions/install.go
  81. 0 412
      netclient/functions/join.go
  82. 0 148
      netclient/functions/list.go
  83. 0 63
      netclient/functions/localport.go
  84. 0 50
      netclient/functions/localport_freebsd.go
  85. 0 333
      netclient/functions/mqhandlers.go
  86. 0 306
      netclient/functions/mqpublish.go
  87. 0 97
      netclient/functions/pull.go
  88. 0 13
      netclient/functions/upgrades/types.go
  89. 0 25
      netclient/functions/upgrades/upgrades.go
  90. 0 24
      netclient/functions/upgrades/v0-14-5.go
  91. 0 25
      netclient/functions/upgrades/v0-14-6.go
  92. 0 23
      netclient/functions/upgrades/v0-16-0.go
  93. 0 24
      netclient/functions/upgrades/v0-16-1.go
  94. 0 22
      netclient/functions/upgrades/v0-16-2.go
  95. 0 33
      netclient/gui/components/buttons.go
  96. 0 22
      netclient/gui/components/colors.go
  97. 0 23
      netclient/gui/components/text.go
  98. 0 39
      netclient/gui/components/toolbar.go
  99. 0 21
      netclient/gui/components/views/confirm.go
  100. 0 25
      netclient/gui/components/views/content.go

+ 0 - 566
.github/workflows/buildandrelease.yml

@@ -71,569 +71,3 @@ jobs:
           prerelease: true
           asset_name: netmaker
 
-  nmctl:
-    runs-on: ubuntu-latest
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v3
-      - name: Setup go
-        uses: actions/setup-go@v3
-        with:
-          go-version: 1.19
-      - name: Build
-        run: |
-          cd cli
-          CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o nmctl-linux-amd64
-          CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o nmctl-darwin-amd64
-          CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o nmctl-darwin-arm64
-          CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o nmctl-windows-amd64
-      - name: Upload nmctl-linux-amd64 to Release
-        continue-on-error: true
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: cli/nmctl-linux-amd64
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: nmctl-linux-amd64
-      - name: Upload nmctl-darwin-amd64 to Release
-        continue-on-error: true
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: cli/nmctl-darwin-amd64
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: nmctl-darwin-amd64
-      - name: Upload nmctl-darwin-arm64 to Release
-        continue-on-error: true
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: cli/nmctl-darwin-arm64
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: nmctl-darwin-arm64
-      - name: Upload nmctl-windows-amd64 to Release
-        continue-on-error: true
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: cli/nmctl-windows-amd64
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: nmctl-windows-amd64
-  netclient-x86:
-    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@v3
-        with:
-          go-version: 1.19
-
-      - name: Build cli
-        run: |
-          cd netclient
-          env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient main.go
-
-      - name: Upload netclient x86 to Release
-        continue-on-error: true
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient
-
-      - name: build gui
-        run: |
-          sudo apt-get update
-          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 .
-
-      - name: Upload netclient x86 gui to Release
-        continue-on-error: true
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-gui
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-gui
-
-      - name: Package x86 deb
-        continue-on-error: true
-        uses: gravitl/github-action-fpm@master
-        with:
-          fpm_args: './netclient/build/netclient=/sbin/netclient ./netclient/build/netclient.service=/lib/systemd/system/netclient.service'
-          fpm_opts: '-s dir -t deb --architecture amd64 --version ${{ env.PACKAGE_VERSION }}'
-
-      - name: Upload x86 deb to Release
-        continue-on-error: true
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient_${{ env.PACKAGE_VERSION }}_amd64.deb
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient_${{ env.PACKAGE_VERSION }}_amd64.deb
-
-      - name: Package x86 rpm
-        continue-on-error: true
-        uses: gravitl/github-action-fpm@master
-        with:
-          fpm_args: './netclient/build/netclient=/sbin/netclient ./netclient/build/netclient.service=/lib/systemd/system/netclient.service'
-          fpm_opts: '-s dir -t rpm --architecture amd64 --version ${{ env.PACKAGE_VERSION }}'
-
-      - name: Upload x86 rpm to Release
-        continue-on-error: true
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient-${{ env.PACKAGE_VERSION }}-1.x86_64.rpm
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-${{ env.PACKAGE_VERSION }}-1.x86_64.rpm
-
-      - name: Package x86 pacman
-        continue-on-error: true
-        uses: gravitl/github-action-fpm@master
-        with:
-          # arch has particular path requirements --- cannot write to a symbolic link e.g. /sbin and /lib
-          fpm_args: './netclient/build/netclient=/usr/bin/netclient ./netclient/build/netclient.service=/usr/lib/systemd/system/netclient.service'
-          fpm_opts: '-s dir -t pacman --architecture amd64 --version ${{ env.PACKAGE_VERSION }}'
-
-      - name: Upload x86 pacman to Release
-        continue-on-error: true
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient-${{ env.PACKAGE_VERSION }}-1-x86_64.pkg.tar.zst
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-${{ env.PACKAGE_VERSION }}-1-x86_64.pkg.tar.zst
-
-  netclient-arm:
-    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@v3
-        with:
-          go-version: 1.19
-      - name: Build
-        run: |
-          cd netclient
-          env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-arm5/netclient main.go
-          env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-arm6/netclient main.go
-          env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-arm7/netclient main.go
-          env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-arm64/netclient main.go
-
-      - name: Upload arm5 to Release
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-arm5/netclient
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-arm5
-
-      - name: Upload arm6 to Release
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-arm6/netclient
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-arm6
-
-      - name: Upload arm7 to Release
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-arm7/netclient
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-arm7
-
-      - name: Upload arm64 to Release
-        continue-on-error: true
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-arm64/netclient
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-arm64
-
-      - name: Package arm64 deb
-        continue-on-error: true
-        uses: gravitl/github-action-fpm@master
-        with:
-          fpm_args: './netclient/build/netclient-arm64/netclient=/sbin/netclient ./netclient/build/netclient.service=/lib/systemd/netclient.service'
-          fpm_opts: '-s dir -t deb --architecture arm64 --version ${{ env.PACKAGE_VERSION }}'
-      - name: Upload arm deb to Release
-        continue-on-error: true
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient_${{ env.PACKAGE_VERSION }}_arm64.deb
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient_${{ env.PACKAGE_VERSION }}_arm64.deb
-
-      - name: Package arm64 rpm
-        continue-on-error: true
-        uses: gravitl/github-action-fpm@master
-        with:
-          fpm_args: './netclient/build/netclient-arm64/netclient=/sbin/netclient ./netclient/build/netclient.service=/lib/systemd/netclient.service'
-          fpm_opts: '-s dir -t rpm --architecture arm64 --version ${{ env.PACKAGE_VERSION }}'
-
-      - name: Upload arm64 rpm to Release
-        continue-on-error: true
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient-${{ env.PACKAGE_VERSION }}-1.aarch64.rpm
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-${{ env.PACKAGE_VERSION }}-1.aarch64.rpm
-
-  netclient-mipsle:
-    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@v3
-        with:
-          go-version: 1.19
-      - name: Build
-        run: |
-          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-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
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-mipsle/netclient-mipsle
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          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@v3
-        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:
-    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@v3
-        with:
-          go-version: 1.19
-      - name: Build
-        run: |
-          cd netclient
-          env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-freebsd/netclient .
-          env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm GOARM=5 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-freebsd-arm5/netclient .
-          env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm GOARM=6 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-freebsd-arm6/netclient .
-          env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm GOARM=7 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-freebsd-arm7/netclient .
-            env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-freebsd-arm64/netclient .
-
-      - name: Upload freebsd to Release
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-freebsd/netclient
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-freebsd
-
-      - name: Upload freebsd-arm5 to Release
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-freebsd-arm5/netclient
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-freebsd-arm5
-
-      - name: Upload freebsd-arm6 to Release
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-freebsd-arm6/netclient
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-freebsd-arm6
-
-      - name: Upload freebsd-arm7 to Release
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-freebsd-arm7/netclient
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-freebsd-arm7
-
-      - name: Upload freebsd-arm64 to Release
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-freebsd-arm64/netclient
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-freebsd-arm64
-
-  netclient-darwin:
-    runs-on: macos-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@v3
-        with:
-          go-version: 1.19
-      - name: Build
-        run: |
-          cd netclient
-          env GOOS=darwin GOARCH=amd64 go build -tags=gui -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-darwin/netclient .
-          env CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -tags=gui -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-darwin-arm64/netclient main.go
-          env GOOS=darwin GOARCH=amd64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-darwin-headless/netclient .
-      - name: Upload darwin-amd64 to Release
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-darwin/netclient
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-darwin
-
-      - name: Upload darwin-arm64 to Release
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-darwin-arm64/netclient
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-darwin-arm64
- 
-      - name: Upload darwin-headless to Release
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient-darwin-headless/netclient
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient-darwin-headless
- 
-  netclient-windows:
-    runs-on: windows-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
-        shell: bash
-      - name: Setup go
-        uses: actions/setup-go@v3
-        with:
-          go-version: 1.19
-      - name: Mysys2 setup
-        uses: msys2/setup-msys2@v2
-        with:
-          install: >-
-            git
-            mingw-w64-x86_64-toolchain
-      - name: Build
-        run: |
-          echo $(go env GOPATH)/bin >> $GITHUB_PATH
-          cd netclient
-          go get -v github.com/josephspurrier/goversioninfo
-          go install -v github.com/josephspurrier/goversioninfo/cmd/goversioninfo
-          go generate
-          go build -tags=gui -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient.exe .
-
-      - name: Upload netclient windows to Release
-        continue-on-error: true
-        uses: svenstaro/upload-release-action@v2
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          file: netclient/build/netclient.exe
-          tag: ${{ env.NETMAKER_VERSION }}
-          overwrite: true
-          prerelease: true
-          asset_name: netclient.exe
-
-  linux-packages:
-    runs-on: ubuntu-latest
-    needs: [version, netclient-x86, netclient-arm]
-    steps:
-      - name: Repository Dispatch
-        uses: peter-evans/[email protected]
-        with:
-          token: ${{ secrets.PERS_TOKEN_FOR_NETMAKER_DEVOPS}}
-          repository: gravitl/netmaker-devops
-          event-type: build-packages
-          client-payload: '{"VERSION": "${{ env.PACKAGE_VERSION }}"}'

+ 0 - 51
.github/workflows/publish-netclient-docker-userspace.yml

@@ -1,51 +0,0 @@
-name: Publish Netclient-Userspace Docker
-
-on:
-  workflow_dispatch:
-    inputs:
-      tag:
-        description: 'docker tag'
-        required: true
-  release:
-    types: [published]
-
-jobs:
-  docker:
-    runs-on: ubuntu-latest
-    steps:
-      - 
-        name: Set tag
-        run: |
-            if [[ -n "${{ github.event.inputs.tag }}" ]]; then
-              TAG=${{ github.event.inputs.tag }}
-            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/setup-buildx-action@v2
-      - 
-        name: Login to DockerHub
-        uses: docker/login-action@v2
-        with:
-          username: ${{ secrets.DOCKERHUB_USERNAME }}
-          password: ${{ secrets.DOCKERHUB_TOKEN }}
-      -
-        name: Build and push
-        uses: docker/build-push-action@v3
-        with:
-          context: .
-          platforms: linux/amd64, linux/arm64, linux/arm/v7
-          file: ./docker/Dockerfile-netclient-multiarch-userspace
-          push: true
-          tags: gravitl/netclient-go:${{ env.TAG }}, gravitl/netclient-userspace:latest
-          build-args: version=${{ env.TAG }}  

+ 0 - 99
.github/workflows/publish-netclient-docker.yml

@@ -1,99 +0,0 @@
-name: Publish Netclient Docker
-
-on:
-  workflow_dispatch:
-    inputs:
-      tag:
-        description: 'docker tag'
-        required: true
-  release:
-    types: [published]
-
-jobs:
-  docker:
-    runs-on: ubuntu-latest
-    steps:
-      - 
-        name: Set tag
-        run: |
-            if [[ -n "${{ github.event.inputs.tag }}" ]]; then
-              TAG=${{ github.event.inputs.tag }}
-            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/setup-buildx-action@v2
-      - 
-        name: Login to DockerHub
-        uses: docker/login-action@v2
-        with:
-          username: ${{ secrets.DOCKERHUB_USERNAME }}
-          password: ${{ secrets.DOCKERHUB_TOKEN }}
-      - 
-        name: Build x86 and export to Docker
-        uses: docker/build-push-action@v3
-        with:
-          context: .
-          load: true
-          platforms: linux/amd64
-          file: ./docker/Dockerfile-netclient-multiarch
-          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@v3
-        with:
-          context: .
-          load: true
-          platforms: linux/arm64
-          file: ./docker/Dockerfile-netclient-multiarch
-          tags: ${{ env.TAG }}
-          build-args: version=${{ env.TAG }}  
-      -
-        name: Test arm64
-        run: |
-            docker run --rm ${{ env.TAG }}&
-            sleep 10
-            kill %1
-      -
-        name: Build armv7l and export to Docker
-        uses: docker/build-push-action@v3
-        with:
-          context: .
-          load: true
-          platforms: linux/arm/v7
-          file: ./docker/Dockerfile-netclient-multiarch
-          tags: ${{ env.TAG }}
-          build-args: version=${{ env.TAG }}  
-      -
-        name: Test armv7l
-        run: |
-            docker run --rm ${{ env.TAG }}&
-            sleep 10
-            kill %1
-      -
-        name: Build and push
-        uses: docker/build-push-action@v3
-        with:
-          context: .
-          platforms: linux/amd64, linux/arm64, linux/arm/v7
-          file: ./docker/Dockerfile-netclient-multiarch
-          push: true
-          tags: gravitl/netclient:${{ env.TAG }}, gravitl/netclient:latest
-          build-args: version=${{ env.TAG }}  

+ 1 - 0
auth/auth.go

@@ -4,6 +4,7 @@ import (
 	"encoding/base64"
 	"encoding/json"
 	"errors"
+	"fmt"
 	"net/http"
 	"strings"
 	"time"

+ 6 - 1
compose/docker-compose.yml

@@ -19,7 +19,9 @@ services:
       - sqldata:/root/data
       - mosquitto_data:/etc/netmaker
     environment:
-      SERVER_NAME: "broker.NETMAKER_BASE_DOMAIN"
+      BROKER_NAME: "broker.NETMAKER_BASE_DOMAIN"
+      SERVER_NAME: "NETMAKER_BASE_DOMAIN"
+      STUN_DOMAIN: "stun.NETMAKER_BASE_DOMAIN"
       SERVER_HOST: "SERVER_PUBLIC_IP"
       SERVER_API_CONN_STRING: "api.NETMAKER_BASE_DOMAIN:443"
       COREDNS_ADDR: "SERVER_PUBLIC_IP"
@@ -40,8 +42,11 @@ services:
       MANAGE_IPTABLES: "on"
       PORT_FORWARD_SERVICES: "dns"
       MQ_ADMIN_PASSWORD: "REPLACE_MQ_ADMIN_PASSWORD"
+      STUN_PORT: "3478"
+      PROXY: "on"
     ports:
       - "51821-51830:51821-51830/udp"
+      - "3478:3478/udp"
   netmaker-ui:
     container_name: netmaker-ui
     image: gravitl/netmaker-ui:v0.17.1

+ 4 - 0
config/config.go

@@ -69,6 +69,7 @@ type ServerConfig struct {
 	MQPort                string `yaml:"mqport"`
 	MQServerPort          string `yaml:"mqserverport"`
 	Server                string `yaml:"server"`
+	Broker                string `yam:"broker"`
 	PublicIPService       string `yaml:"publicipservice"`
 	MQAdminPassword       string `yaml:"mqadminpassword"`
 	MetricsExporter       string `yaml:"metrics_exporter"`
@@ -76,6 +77,9 @@ type ServerConfig struct {
 	LicenseValue          string `yaml:"license_value"`
 	NetmakerAccountID     string `yaml:"netmaker_account_id"`
 	IsEE                  string `yaml:"is_ee"`
+	StunPort              int    `yaml:"stun_port"`
+	StunHost              string `yaml:"stun_host"`
+	Proxy                 string `yaml:"proxy"`
 }
 
 // SQLConfig - Generic SQL Config

+ 1 - 0
controllers/controller.go

@@ -28,6 +28,7 @@ var HttpHandlers = []interface{}{
 	extClientHandlers,
 	ipHandlers,
 	loggerHandlers,
+	hostHandlers,
 }
 
 // HandleRESTRequests - handles the rest requests

+ 37 - 49
controllers/dns.go

@@ -29,10 +29,10 @@ func dnsHandlers(r *mux.Router) {
 //
 // Gets node DNS entries associated with a network.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 func getNodeDNS(w http.ResponseWriter, r *http.Request) {
 
 	w.Header().Set("Content-Type", "application/json")
@@ -55,14 +55,13 @@ func getNodeDNS(w http.ResponseWriter, r *http.Request) {
 //
 // Gets all DNS entries.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
-//
-// 		Responses:
-//   		200: dnsResponse
+//			Security:
+//	  		oauth
 //
+//			Responses:
+//	  		200: dnsResponse
 func getAllDNS(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	dns, err := logic.GetAllDNS()
@@ -79,14 +78,13 @@ func getAllDNS(w http.ResponseWriter, r *http.Request) {
 //
 // Gets custom DNS entries associated with a network.
 //
-//		Schemes: https
-//
-// 		Security:
-//   		oauth
+//			Schemes: https
 //
-// 		Responses:
-//   		200: dnsResponse
+//			Security:
+//	  		oauth
 //
+//			Responses:
+//	  		200: dnsResponse
 func getCustomDNS(w http.ResponseWriter, r *http.Request) {
 
 	w.Header().Set("Content-Type", "application/json")
@@ -109,14 +107,13 @@ func getCustomDNS(w http.ResponseWriter, r *http.Request) {
 //
 // Gets all DNS entries associated with the network.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
-//
-// 		Responses:
-//   		200: dnsResponse
+//			Security:
+//	  		oauth
 //
+//			Responses:
+//	  		200: dnsResponse
 func getDNS(w http.ResponseWriter, r *http.Request) {
 
 	w.Header().Set("Content-Type", "application/json")
@@ -139,14 +136,13 @@ func getDNS(w http.ResponseWriter, r *http.Request) {
 //
 // Create a DNS entry.
 //
-//		Schemes: https
-//
-// 		Security:
-//   		oauth
+//			Schemes: https
 //
-// 		Responses:
-//   		200: dnsResponse
+//			Security:
+//	  		oauth
 //
+//			Responses:
+//	  		200: dnsResponse
 func createDNS(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 
@@ -180,16 +176,8 @@ func createDNS(w http.ResponseWriter, r *http.Request) {
 	}
 	logger.Log(1, "new DNS record added:", entry.Name)
 	if servercfg.IsMessageQueueBackend() {
-		serverNode, err := logic.GetNetworkServerLocal(entry.Network)
-		if err != nil {
-			logger.Log(1, "failed to find server node after DNS update on", entry.Network)
-		} else {
-			if err = logic.ServerUpdate(&serverNode, false); err != nil {
-				logger.Log(1, "failed to update server node after DNS update on", entry.Network)
-			}
-			if err = mq.PublishPeerUpdate(&serverNode, false); err != nil {
-				logger.Log(0, "failed to publish peer update after ACL update on", entry.Network)
-			}
+		if err = mq.PublishPeerUpdate(entry.Network, false); err != nil {
+			logger.Log(0, "failed to publish peer update after ACL update on", entry.Network)
 		}
 	}
 	logger.Log(2, r.Header.Get("user"),
@@ -202,14 +190,14 @@ func createDNS(w http.ResponseWriter, r *http.Request) {
 //
 // Delete a DNS entry.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 //
-//		Responses:
-//			200: stringJSONResponse
-//			*: stringJSONResponse
+//			Responses:
+//				200: stringJSONResponse
+//				*: stringJSONResponse
 func deleteDNS(w http.ResponseWriter, r *http.Request) {
 	// Set header
 	w.Header().Set("Content-Type", "application/json")
@@ -270,14 +258,14 @@ func GetDNSEntry(domain string, network string) (models.DNSEntry, error) {
 //
 // Push DNS entries to nameserver.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 //
-//		Responses:
-//			200: dnsStringJSONResponse
-//			*: dnsStringJSONResponse
+//			Responses:
+//				200: dnsStringJSONResponse
+//				*: dnsStringJSONResponse
 func pushDNS(w http.ResponseWriter, r *http.Request) {
 	// Set header
 	w.Header().Set("Content-Type", "application/json")

+ 1 - 1
controllers/dns_test.go

@@ -55,7 +55,7 @@ func TestGetNodeDNS(t *testing.T) {
 		assert.Equal(t, "10.0.0.1", dns[0].Address)
 	})
 	t.Run("MultipleNodes", func(t *testing.T) {
-		createnode := &models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.100.100.3", MacAddress: "01:02:03:04:05:07", Password: "password", Network: "skynet"}
+		createnode := &models.LegacyNode{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.100.100.3", MacAddress: "01:02:03:04:05:07", Password: "password", Network: "skynet"}
 		err := logic.CreateNode(createnode)
 		assert.Nil(t, err)
 		dns, err := logic.GetNodeDNS("skynet")

+ 17 - 18
controllers/docs.go

@@ -29,7 +29,6 @@ import (
 	serverconfigpkg "github.com/gravitl/netmaker/config"
 	"github.com/gravitl/netmaker/logic/acls"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/config"
 )
 
 var _ = useUnused() // "use" the function to prevent "unused function" errors
@@ -243,21 +242,21 @@ type aclContainerResponse struct {
 type nodeSliceResponse struct {
 	// Nodes
 	// in: body
-	Nodes []models.Node `json:"nodes"`
+	Nodes []models.LegacyNode `json:"nodes"`
 }
 
 // swagger:response nodeResponse
 type nodeResponse struct {
 	// Node
 	// in: body
-	Node models.Node `json:"node"`
+	Node models.LegacyNode `json:"node"`
 }
 
 // swagger:parameters updateNode deleteNode
 type nodeBodyParam struct {
 	// Node
 	// in: body
-	Node models.Node `json:"node"`
+	Node models.LegacyNode `json:"node"`
 }
 
 // swagger:parameters createRelay
@@ -303,18 +302,18 @@ type nodeLastModifiedResponse struct {
 }
 
 // swagger:parameters register
-type registerRequestBodyParam struct {
-	// Register Request
-	// in: body
-	RegisterRequest config.RegisterRequest `json:"register_request"`
-}
-
-// swagger:response registerResponse
-type registerResponse struct {
-	// Register Response
-	// in: body
-	RegisterResponse config.RegisterResponse `json:"register_response"`
-}
+//type registerRequestBodyParam struct {
+//	// Register Request
+//	// in: body
+//	RegisterRequest config.RegisterRequest `json:"register_request"`
+//}
+//
+//// swagger:response registerResponse
+//type registerResponse struct {
+//	// Register Response
+//	// in: body
+//	RegisterResponse config.RegisterResponse `json:"register_response"`
+//}
 
 // swagger:response boolResponse
 type boolResponse struct {
@@ -388,8 +387,8 @@ func useUnused() bool {
 	_ = serverConfigResponse{}
 	_ = nodeGetResponse{}
 	_ = nodeLastModifiedResponse{}
-	_ = registerRequestBodyParam{}
-	_ = registerResponse{}
+	//	_ = registerRequestBodyParam{}
+	//	_ = registerResponse{}
 	_ = boolResponse{}
 	_ = userBodyParam{}
 	_ = userBodyResponse{}

+ 23 - 8
controllers/ext_client.go

@@ -35,7 +35,7 @@ func checkIngressExists(nodeID string) bool {
 	if err != nil {
 		return false
 	}
-	return node.IsIngressGateway == "yes"
+	return node.IsIngressGateway
 }
 
 // swagger:route GET /api/extclients/{network} ext_client getNetworkExtClients
@@ -184,6 +184,13 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
+	host, err := logic.GetHost(gwnode.HostID.String())
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"),
+			fmt.Sprintf("failed to get host for ingress gateway node [%s] info: %v", client.IngressGatewayID, err))
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
 
 	network, err := logic.GetParentNetwork(client.Network)
 	if err != nil {
@@ -207,7 +214,7 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
 	if network.DefaultKeepalive != 0 {
 		keepalive = "PersistentKeepalive = " + strconv.Itoa(int(network.DefaultKeepalive))
 	}
-	gwendpoint := gwnode.Endpoint + ":" + strconv.Itoa(int(gwnode.ListenPort))
+	gwendpoint := host.EndpointIP.String() + ":" + strconv.Itoa(host.ListenPort)
 	newAllowedIPs := network.AddressRange
 	if newAllowedIPs != "" && network.AddressRange6 != "" {
 		newAllowedIPs += ","
@@ -226,8 +233,8 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
 	}
 
 	defaultMTU := 1420
-	if gwnode.MTU != 0 {
-		defaultMTU = int(gwnode.MTU)
+	if host.MTU != 0 {
+		defaultMTU = host.MTU
 	}
 	config := fmt.Sprintf(`[Interface]
 Address = %s
@@ -245,7 +252,7 @@ Endpoint = %s
 		client.PrivateKey,
 		defaultMTU,
 		defaultDNS,
-		gwnode.PublicKey,
+		host.PublicKey,
 		newAllowedIPs,
 		gwendpoint,
 		keepalive)
@@ -331,7 +338,15 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
-	extclient.IngressGatewayEndpoint = node.Endpoint + ":" + strconv.FormatInt(int64(node.ListenPort), 10)
+	host, err := logic.GetHost(node.HostID.String())
+	logger.Log(0, r.Header.Get("user"),
+		fmt.Sprintf("failed to get ingress gateway host for node [%s] info: %v", nodeid, err))
+	logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+	listenPort := host.LocalListenPort
+	if host.ProxyEnabled {
+		listenPort = host.ProxyListenPort
+	}
+	extclient.IngressGatewayEndpoint = host.EndpointIP.String() + ":" + strconv.FormatInt(int64(listenPort), 10)
 
 	extclient.Enabled = true
 	parentNetwork, err := logic.GetNetwork(networkName)
@@ -466,7 +481,7 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 	if changedEnabled { // need to send a peer update to the ingress node as enablement of one of it's clients has changed
 		if ingressNode, err := logic.GetNodeByID(newclient.IngressGatewayID); err == nil {
 			if err = mq.PublishExtPeerUpdate(&ingressNode); err != nil {
-				logger.Log(1, "error setting ext peers on", ingressNode.ID, ":", err.Error())
+				logger.Log(1, "error setting ext peers on", ingressNode.ID.String(), ":", err.Error())
 			}
 		}
 	}
@@ -538,7 +553,7 @@ func deleteExtClient(w http.ResponseWriter, r *http.Request) {
 
 	err = mq.PublishExtPeerUpdate(&ingressnode)
 	if err != nil {
-		logger.Log(1, "error setting ext peers on "+ingressnode.ID+": "+err.Error())
+		logger.Log(1, "error setting ext peers on "+ingressnode.ID.String()+": "+err.Error())
 	}
 
 	logger.Log(0, r.Header.Get("user"),

+ 190 - 0
controllers/hosts.go

@@ -0,0 +1,190 @@
+package controller
+
+import (
+	"encoding/json"
+	"net/http"
+
+	"github.com/gorilla/mux"
+	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mq"
+	"github.com/gravitl/netmaker/servercfg"
+)
+
+type hostNetworksUpdatePayload struct {
+	Networks []string `json:"networks"`
+}
+
+func hostHandlers(r *mux.Router) {
+	r.HandleFunc("/api/hosts", logic.SecurityCheck(true, http.HandlerFunc(getHosts))).Methods("GET")
+	r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(updateHost))).Methods("PUT")
+	r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(deleteHost))).Methods("DELETE")
+	r.HandleFunc("/api/hosts/{hostid}/networks", logic.SecurityCheck(true, http.HandlerFunc(updateHostNetworks))).Methods("PUT")
+}
+
+// swagger:route GET /api/hosts hosts getHosts
+//
+// Lists all hosts.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: getHostsSliceResponse
+func getHosts(w http.ResponseWriter, r *http.Request) {
+	currentHosts, err := logic.GetAllHosts()
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to fetch hosts: ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	// return JSON/API formatted hosts
+	apiHosts := logic.GetAllHostsAPI(currentHosts[:])
+	logger.Log(2, r.Header.Get("user"), "fetched all hosts")
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(apiHosts)
+}
+
+// swagger:route PUT /api/hosts/{hostid} hosts updateHost
+//
+// Updates a Netclient host on Netmaker server.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: updateHostResponse
+func updateHost(w http.ResponseWriter, r *http.Request) {
+	var newHostData models.ApiHost
+	err := json.NewDecoder(r.Body).Decode(&newHostData)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to update a host:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	// confirm host exists
+	currHost, err := logic.GetHost(newHostData.ID)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to update a host:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	newHost := newHostData.ConvertAPIHostToNMHost(currHost)
+
+	logic.UpdateHost(newHost, currHost) // update the in memory struct values
+	if err = logic.UpsertHost(newHost); err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to update a host:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	newNetworks := logic.GetHostNetworks(newHost.ID.String())
+	if len(newNetworks) > 0 {
+		if err = mq.ModifyClient(&mq.MqClient{
+			ID:       currHost.ID.String(),
+			Text:     currHost.Name,
+			Networks: newNetworks,
+		}); err != nil {
+			logger.Log(0, r.Header.Get("user"), "failed to update host networks roles in DynSec:", err.Error())
+		}
+	}
+
+	apiHostData := newHost.ConvertNMHostToAPI()
+	logger.Log(2, r.Header.Get("user"), "updated host", newHost.ID.String())
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(apiHostData)
+}
+
+// swagger:route DELETE /api/hosts/{hostid} hosts deleteHost
+//
+// Deletes a Netclient host from Netmaker server.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: deleteHostResponse
+func deleteHost(w http.ResponseWriter, r *http.Request) {
+	var params = mux.Vars(r)
+	hostid := params["hostid"]
+	// confirm host exists
+	currHost, err := logic.GetHost(hostid)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	if err = logic.RemoveHost(currHost); err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	if err = mq.DeleteMqClient(currHost.ID.String()); err != nil {
+		logger.Log(0, "error removing DynSec credentials for host:", currHost.Name, err.Error())
+	}
+
+	apiHostData := currHost.ConvertNMHostToAPI()
+	logger.Log(2, r.Header.Get("user"), "removed host", currHost.Name)
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(apiHostData)
+}
+
+// swagger:route PUT /api/hosts hosts updateHostNetworks
+//
+// Given a list of networks, a host is updated accordingly.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: updateHostNetworks
+func updateHostNetworks(w http.ResponseWriter, r *http.Request) {
+	var payload hostNetworksUpdatePayload
+	err := json.NewDecoder(r.Body).Decode(&payload)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to update host networks:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	// confirm host exists
+	var params = mux.Vars(r)
+	hostid := params["hostid"]
+	currHost, err := logic.GetHost(hostid)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to find host:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	if err = logic.UpdateHostNetworks(currHost, servercfg.GetServer(), payload.Networks[:]); err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to update host networks:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	if err = mq.ModifyClient(&mq.MqClient{
+		ID:       currHost.ID.String(),
+		Text:     currHost.Name,
+		Networks: payload.Networks,
+	}); err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to update host networks roles in DynSec:", err.Error())
+	}
+
+	logger.Log(2, r.Header.Get("user"), "updated host networks", currHost.Name)
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(payload)
+}

+ 16 - 60
controllers/network.go

@@ -146,11 +146,9 @@ func keyUpdate(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	for _, node := range nodes {
-		logger.Log(2, "updating node ", node.Name, " for a key update")
-		if node.IsServer != "yes" {
-			if err = mq.NodeUpdate(&node); err != nil {
-				logger.Log(1, "failed to send update to node during a network wide key update", node.Name, node.ID, err.Error())
-			}
+		logger.Log(2, "updating node ", node.ID.String(), " for a key update")
+		if err = mq.NodeUpdate(&node); err != nil {
+			logger.Log(1, "failed to send update to node during a network wide key update", node.ID.String(), err.Error())
 		}
 	}
 }
@@ -249,16 +247,6 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 	}
-	if holepunchupdate {
-		err = logic.UpdateNetworkHolePunching(network.NetID, newNetwork.DefaultUDPHolePunch)
-		if err != nil {
-			logger.Log(0, r.Header.Get("user"),
-				fmt.Sprintf("failed to update network [%s] hole punching: %v",
-					network.NetID, err.Error()))
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-			return
-		}
-	}
 	if rangeupdate4 || rangeupdate6 || localrangeupdate || holepunchupdate {
 		nodes, err := logic.GetNetworkNodes(network.NetID)
 		if err != nil {
@@ -319,19 +307,10 @@ func updateNetworkACL(w http.ResponseWriter, r *http.Request) {
 
 	// send peer updates
 	if servercfg.IsMessageQueueBackend() {
-		serverNode, err := logic.GetNetworkServerLocal(netname)
-		if err != nil {
-			logger.Log(1, "failed to find server node after ACL update on", netname)
-		} else {
-			if err = logic.ServerUpdate(&serverNode, false); err != nil {
-				logger.Log(1, "failed to update server node after ACL update on", netname)
-			}
-			if err = mq.PublishPeerUpdate(&serverNode, false); err != nil {
-				logger.Log(0, "failed to publish peer update after ACL update on", netname)
-			}
+		if err = mq.PublishPeerUpdate(netname, false); err != nil {
+			logger.Log(0, "failed to publish peer update after ACL update on", netname)
 		}
 	}
-
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(newNetACL)
 }
@@ -392,20 +371,11 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, errtype))
 		return
 	}
-	// Deletes the network role from MQ
-	event := mq.MqDynsecPayload{
-		Commands: []mq.MqDynSecCmd{
-			{
-				Command:  mq.DeleteRoleCmd,
-				RoleName: network,
-			},
-		},
-	}
 
-	if err := mq.PublishEventToDynSecTopic(event); err != nil {
-		logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
-			event.Commands, err.Error()))
+	if err := mq.DeleteNetworkRole(network); err != nil {
+		logger.Log(0, fmt.Sprintf("failed to remove network DynSec role: %v", err.Error()))
 	}
+
 	logger.Log(1, r.Header.Get("user"), "deleted network", network)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode("success")
@@ -452,33 +422,19 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 	}
-	// Create Role with acls for the network
-	event := mq.MqDynsecPayload{
-		Commands: []mq.MqDynSecCmd{
-			{
-				Command:  mq.CreateRoleCmd,
-				RoleName: network.NetID,
-				Textname: "Network wide role with Acls for nodes",
-				Acls:     mq.FetchNetworkAcls(network.NetID),
-			},
-		},
-	}
 
-	if err = mq.PublishEventToDynSecTopic(event); err != nil {
-		logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
-			event.Commands, err.Error()))
+	if err = mq.CreateNetworkRole(network.NetID); err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to create network DynSec role:",
+			err.Error())
 	}
 
-	if servercfg.IsClientMode() != "off" {
-		if _, err = logic.ServerJoin(&network); err != nil {
-			_ = logic.DeleteNetwork(network.NetID)
-			logger.Log(0, r.Header.Get("user"), "failed to create network: ",
-				err.Error())
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-			return
-		}
+	if err = logic.AddDefaultHostsToNetwork(network.NetID, servercfg.GetServer()); err != nil {
+		logger.Log(0, fmt.Sprintf("failed to add default hosts to network [%v]: %v",
+			network.NetID, err.Error()))
 	}
 
+	// TODO: Send message notifying host of new peers/network conf
+
 	logger.Log(1, r.Header.Get("user"), "created network", network.NetID)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(network)

+ 1 - 1
controllers/network_test.go

@@ -305,7 +305,7 @@ func TestIpv6Network(t *testing.T) {
 		assert.Nil(t, err)
 		assert.Equal(t, network.AddressRange6, "fde6:be04:fa5e:d076::/64")
 	})
-	node1 := models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: "testnode", Endpoint: "10.0.0.50", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet6", OS: "linux"}
+	node1 := models.LegacyNode{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: "testnode", Endpoint: "10.0.0.50", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet6", OS: "linux"}
 	nodeErr := logic.CreateNode(&node1)
 	t.Run("Test node on network IPv6", func(t *testing.T) {
 		assert.Nil(t, nodeErr)

+ 355 - 377
controllers/node.go

@@ -2,11 +2,14 @@ package controller
 
 import (
 	"encoding/json"
+	"errors"
 	"fmt"
 	"net/http"
 	"strings"
 
+	"github.com/google/uuid"
 	"github.com/gorilla/mux"
+	proxy_models "github.com/gravitl/netclient/nmproxy/models"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
@@ -24,6 +27,7 @@ func nodeHandlers(r *mux.Router) {
 	r.HandleFunc("/api/nodes/{network}", authorize(false, true, "network", http.HandlerFunc(getNetworkNodes))).Methods(http.MethodGet)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(getNode))).Methods(http.MethodGet)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(false, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPut)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/migrate", authorize(true, true, "node", http.HandlerFunc(nodeNodeUpdate))).Methods("PUT")
 	r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", authorize(false, true, "user", http.HandlerFunc(createRelay))).Methods(http.MethodPost)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", authorize(false, true, "user", http.HandlerFunc(deleteRelay))).Methods(http.MethodDelete)
@@ -31,7 +35,7 @@ func nodeHandlers(r *mux.Router) {
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", authorize(false, true, "user", http.HandlerFunc(deleteEgressGateway))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(false, http.HandlerFunc(createIngressGateway))).Methods(http.MethodPost)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}/approve", authorize(false, true, "user", http.HandlerFunc(uncordonNode))).Methods(http.MethodPost)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost)
 	r.HandleFunc("/api/nodes/{network}", nodeauth(checkFreeTierLimits(node_l, http.HandlerFunc(createNode)))).Methods(http.MethodPost)
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost)
 }
@@ -40,13 +44,13 @@ func nodeHandlers(r *mux.Router) {
 //
 // Authenticate to make further API calls related to a network.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 //
-//		Responses:
-//			200: successResponse
+//			Responses:
+//				200: successResponse
 func authenticate(response http.ResponseWriter, request *http.Request) {
 
 	var authRequest models.AuthParams
@@ -92,51 +96,22 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
 			return
 		}
 	}
-
-	err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(authRequest.Password))
+	host, err := logic.GetHost(result.HostID.String())
 	if err != nil {
 		errorResponse.Code = http.StatusBadRequest
 		errorResponse.Message = err.Error()
 		logger.Log(0, request.Header.Get("user"),
-			"error validating user password: ", err.Error())
+			"error retrieving host: ", err.Error())
 		logic.ReturnErrorResponse(response, request, errorResponse)
 		return
 	}
-	// creates network role,node client (added here to resolve any missing configuration in MQ)
-	event := mq.MqDynsecPayload{
-		Commands: []mq.MqDynSecCmd{
-
-			{
-				Command:  mq.CreateRoleCmd,
-				RoleName: result.Network,
-				Textname: "Network wide role with Acls for nodes",
-				Acls:     mq.FetchNetworkAcls(result.Network),
-			},
-			{
-				Command:  mq.CreateClientCmd,
-				Username: result.ID,
-				Password: authRequest.Password,
-				Textname: result.Name,
-				Roles: []mq.MqDynSecRole{
-					{
-						Rolename: mq.NodeRole,
-						Priority: -1,
-					},
-					{
-						Rolename: result.Network,
-						Priority: -1,
-					},
-				},
-				Groups: make([]mq.MqDynSecGroup, 0),
-			},
-		},
-	}
-
-	if err := mq.PublishEventToDynSecTopic(event); err != nil {
-		logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
-			event.Commands, err.Error()))
-		errorResponse.Code = http.StatusInternalServerError
-		errorResponse.Message = fmt.Sprintf("could not create mq client for node [%s]: %v", result.ID, err)
+	err = bcrypt.CompareHashAndPassword([]byte(host.HostPass), []byte(authRequest.Password))
+	if err != nil {
+		errorResponse.Code = http.StatusBadRequest
+		errorResponse.Message = err.Error()
+		logger.Log(0, request.Header.Get("user"),
+			"error validating user password: ", err.Error())
+		logic.ReturnErrorResponse(response, request, errorResponse)
 		return
 	}
 
@@ -171,7 +146,6 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
 	response.WriteHeader(http.StatusOK)
 	response.Header().Set("Content-Type", "application/json")
 	response.Write(successJSONResponse)
-
 }
 
 // auth middleware for api calls from nodes where node is has not yet joined the server (register, join)
@@ -343,21 +317,17 @@ func authorize(nodesAllowed, networkCheck bool, authNetwork string, next http.Ha
 //
 // Gets all nodes associated with network including pending nodes.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 //
-//		Responses:
-//			200: nodeSliceResponse
+//			Responses:
+//				200: nodeSliceResponse
 func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
-
 	w.Header().Set("Content-Type", "application/json")
-
-	var nodes []models.Node
 	var params = mux.Vars(r)
 	networkName := params["network"]
-
 	nodes, err := logic.GetNetworkNodes(networkName)
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
@@ -366,29 +336,25 @@ func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	for _, node := range nodes {
-		if len(node.NetworkSettings.AccessKeys) > 0 {
-			node.NetworkSettings.AccessKeys = []models.AccessKey{} // not to be sent back to client; client already knows how to join the network
-		}
-	}
-
-	//Returns all the nodes in JSON format
+	// returns all the nodes in JSON/API format
+	apiNodes := logic.GetAllNodesAPI(nodes[:])
 	logger.Log(2, r.Header.Get("user"), "fetched nodes on network", networkName)
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(nodes)
+	json.NewEncoder(w).Encode(apiNodes)
 }
 
 // swagger:route GET /api/nodes nodes getAllNodes
 //
 // Get all nodes across all networks.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: nodeSliceResponse
 //
-//		Responses:
-//			200: nodeSliceResponse
 // Not quite sure if this is necessary. Probably necessary based on front end but may want to review after iteration 1 if it's being used or not
 func getAllNodes(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
@@ -416,10 +382,11 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 	}
-	//Return all the nodes in JSON format
+	// return all the nodes in JSON/API format
+	apiNodes := logic.GetAllNodesAPI(nodes[:])
 	logger.Log(3, r.Header.Get("user"), "fetched all nodes they have access to")
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(nodes)
+	json.NewEncoder(w).Encode(apiNodes)
 }
 
 func getUsersNodes(user models.User) ([]models.Node, error) {
@@ -439,13 +406,13 @@ func getUsersNodes(user models.User) ([]models.Node, error) {
 //
 // Get an individual node.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 //
-//		Responses:
-//			200: nodeResponse
+//			Responses:
+//				200: nodeResponse
 func getNode(w http.ResponseWriter, r *http.Request) {
 	// set header.
 	w.Header().Set("Content-Type", "application/json")
@@ -461,29 +428,39 @@ func getNode(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
-
-	peerUpdate, err := logic.GetPeerUpdate(&node)
+	host, err := logic.GetHost(node.HostID.String())
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"),
+			fmt.Sprintf("error fetching host for node [ %s ] info: %v", nodeid, err))
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	peerUpdate, err := logic.GetPeerUpdate(&node, host)
 	if err != nil && !database.IsEmptyRecord(err) {
 		logger.Log(0, r.Header.Get("user"),
 			fmt.Sprintf("error fetching wg peers config for node [ %s ]: %v", nodeid, err))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
-
-	if len(node.NetworkSettings.AccessKeys) > 0 {
-		node.NetworkSettings.AccessKeys = []models.AccessKey{} // not to be sent back to client; client already knows how to join the network
+	server := servercfg.GetServerInfo()
+	network, err := logic.GetNetwork(node.Network)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"),
+			fmt.Sprintf("error fetching network for node [ %s ] info: %v", nodeid, err))
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
 	}
-
+	legacy := node.Legacy(host, &server, &network)
 	response := models.NodeGet{
-		Node:         node,
+		Node:         *legacy,
 		Peers:        peerUpdate.Peers,
-		ServerConfig: servercfg.GetServerInfo(),
+		ServerConfig: server,
 		PeerIDs:      peerUpdate.PeerIDs,
 	}
 
 	if servercfg.Is_EE && nodeRequest {
-		if err = logic.EnterpriseResetAllPeersFailovers(node.ID, node.Network); err != nil {
-			logger.Log(1, "failed to reset failover list during node config pull", node.Name, node.Network)
+		if err = logic.EnterpriseResetAllPeersFailovers(node.ID.String(), node.Network); err != nil {
+			logger.Log(1, "failed to reset failover list during node config pull", node.ID.String(), node.Network)
 		}
 	}
 
@@ -496,13 +473,13 @@ func getNode(w http.ResponseWriter, r *http.Request) {
 //
 // Create a node on a network.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 //
-//		Responses:
-//			200: nodeGetResponse
+//			Responses:
+//				200: nodeGetResponse
 func createNode(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 
@@ -529,54 +506,47 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	var node = models.Node{}
-
-	//get node from body of request
-	err = json.NewDecoder(r.Body).Decode(&node)
+	//get data from body of request
+	data := models.JoinData{}
+	err = json.NewDecoder(r.Body).Decode(&data)
 	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
 	}
 
-	node.Network = networkName
-
-	network, err := logic.GetNetworkByNode(&node)
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"),
-			fmt.Sprintf("failed to get network [%s] info: %v", node.Network, err))
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+	if !logic.IsVersionComptatible(data.Host.Version) {
+		err := errors.New("incomatible netclient version")
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 	}
-	node.NetworkSettings, err = logic.GetNetworkSettings(node.Network)
+
+	data.Node.Network = networkName
+
+	networkSettings, err := logic.GetNetworkSettings(networkName)
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
-			fmt.Sprintf("failed to get network [%s] settings: %v", node.Network, err))
+			fmt.Sprintf("failed to get network [%s] settings: %v", networkName, err))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
-	keyName, validKey := logic.IsKeyValid(networkName, node.AccessKey)
+	data.Node.NetworkSettings(networkSettings)
+	keyName, validKey := logic.IsKeyValid(networkName, data.Key)
 	if !validKey {
-		// Check to see if network will allow manual sign up
-		// may want to switch this up with the valid key check and avoid a DB call that way.
-		if network.AllowManualSignUp == "yes" {
-			node.IsPending = "yes"
-		} else {
-			errorResponse = models.ErrorResponse{
-				Code: http.StatusUnauthorized, Message: "W1R3: Key invalid, or none provided.",
-			}
-			logger.Log(0, r.Header.Get("user"),
-				fmt.Sprintf("failed to create node on network [%s]: %s",
-					node.Network, errorResponse.Message))
-			logic.ReturnErrorResponse(w, r, errorResponse)
-			return
+		errorResponse = models.ErrorResponse{
+			Code: http.StatusUnauthorized, Message: "W1R3: Key invalid, or none provided.",
 		}
+		logger.Log(0, r.Header.Get("user"),
+			fmt.Sprintf("failed to create node on network [%s]: %s",
+				data.Node.Network, errorResponse.Message))
+		logic.ReturnErrorResponse(w, r, errorResponse)
+		return
 	}
 	user, err := pro.GetNetworkUser(networkName, promodels.NetworkUserID(keyName))
 	if err == nil {
 		if user.ID != "" {
 			logger.Log(1, "associating new node with user", keyName)
-			node.OwnerID = string(user.ID)
+			data.Node.OwnerID = string(user.ID)
 		}
 	}
 
@@ -591,22 +561,61 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
-	if node.TrafficKeys.Mine == nil {
+	if data.Host.TrafficKeyPublic == nil {
 		logger.Log(0, "error: node traffic key is nil")
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
-	node.TrafficKeys = models.TrafficKeys{
-		Mine:   node.TrafficKeys.Mine,
-		Server: key,
-	}
+	server := servercfg.GetServerInfo()
+	server.TrafficKey = key
 	// consume password before hashing for mq client creation
-	nodePassword := node.Password
-	err = logic.CreateNode(&node)
+	hostPassword := data.Host.HostPass
+	data.Node.Server = servercfg.GetServer()
+	if err := logic.CreateHost(&data.Host); err != nil {
+		if errors.Is(err, logic.ErrHostExists) {
+			logger.Log(3, "host exists .. no need to create")
+			host, err := logic.GetHost(data.Host.ID.String())
+			if err != nil {
+				logger.Log(0, r.Header.Get("user"), "failed to find host:", err.Error())
+				logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+				return
+			}
+			logic.UpdateHost(&data.Host, host) // update the in memory struct values
+			networks := logic.GetHostNetworks(data.Host.ID.String())
+			if err := mq.ModifyClient(&mq.MqClient{
+				ID:       data.Host.ID.String(),
+				Text:     data.Host.Name,
+				Networks: networks,
+			}); err != nil {
+				logger.Log(0, fmt.Sprintf("failed to modify DynSec client: %v", err.Error()))
+				logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+				return
+			}
+
+		} else {
+			logger.Log(0, "error creating host", err.Error())
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+			return
+		}
+	} else {
+		// Create client for this host in Mq
+		if err := mq.CreateMqClient(&mq.MqClient{
+			ID:       data.Host.ID.String(),
+			Text:     data.Host.Name,
+			Password: hostPassword,
+			Networks: []string{networkName},
+		}); err != nil {
+			logger.Log(0, fmt.Sprintf("failed to create DynSec client: %v", err.Error()))
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+			return
+		}
+	}
+
+	err = logic.AssociateNodeToHost(&data.Node, &data.Host)
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 			fmt.Sprintf("failed to create node on network [%s]: %s",
-				node.Network, err))
+				networkName, err))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
@@ -616,107 +625,42 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 	// if it fails remove the node and fail request
 	if user != nil {
 		var updatedUserNode bool
-		user.Nodes = append(user.Nodes, node.ID) // add new node to user
+		user.Nodes = append(user.Nodes, data.Node.ID.String()) // add new node to user
 		if err = pro.UpdateNetworkUser(networkName, user); err == nil {
-			logger.Log(1, "added node", node.ID, node.Name, "to user", string(user.ID))
+			logger.Log(1, "added node", data.Node.ID.String(), data.Host.Name, "to user", string(user.ID))
 			updatedUserNode = true
 		}
 		if !updatedUserNode { // user was found but not updated, so delete node
 			logger.Log(0, "failed to add node to user", keyName)
-			logic.DeleteNodeByID(&node, true)
+			logic.DeleteNode(&data.Node, true)
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 			return
 		}
 	}
-
-	peerUpdate, err := logic.GetPeerUpdate(&node)
+	peerUpdate, err := logic.GetPeerUpdate(&data.Node, &data.Host)
 	if err != nil && !database.IsEmptyRecord(err) {
 		logger.Log(0, r.Header.Get("user"),
-			fmt.Sprintf("error fetching wg peers config for node [ %s ]: %v", node.ID, err))
+			fmt.Sprintf("error fetching wg peers config for node [ %s ]: %v", data.Node.ID.String(), err))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
+	data.Node.Peers = peerUpdate.Peers
 
-	// Create client for this node in Mq
-	event := mq.MqDynsecPayload{
-		Commands: []mq.MqDynSecCmd{
-			{ // delete if any client exists already
-				Command:  mq.DeleteClientCmd,
-				Username: node.ID,
-			},
-			{
-				Command:  mq.CreateRoleCmd,
-				RoleName: node.Network,
-				Textname: "Network wide role with Acls for nodes",
-				Acls:     mq.FetchNetworkAcls(node.Network),
-			},
-			{
-				Command:  mq.CreateClientCmd,
-				Username: node.ID,
-				Password: nodePassword,
-				Textname: node.Name,
-				Roles: []mq.MqDynSecRole{
-					{
-						Rolename: mq.NodeRole,
-						Priority: -1,
-					},
-					{
-						Rolename: node.Network,
-						Priority: -1,
-					},
-				},
-				Groups: make([]mq.MqDynSecGroup, 0),
-			},
-		},
-	}
-
-	if err := mq.PublishEventToDynSecTopic(event); err != nil {
-		logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
-			event.Commands, err.Error()))
-	}
-
-	response := models.NodeGet{
-		Node:         node,
-		Peers:        peerUpdate.Peers,
-		ServerConfig: servercfg.GetServerInfo(),
+	response := models.NodeJoinResponse{
+		Node:         data.Node,
+		ServerConfig: server,
 		PeerIDs:      peerUpdate.PeerIDs,
 	}
-
-	logger.Log(1, r.Header.Get("user"), "created new node", node.Name, "on network", node.Network)
+	logger.Log(1, r.Header.Get("user"), "created new node", data.Host.Name, "on network", networkName)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(response)
-	runForceServerUpdate(&node, true)
-}
-
-// swagger:route POST /api/nodes/{network}/{nodeid}/approve nodes uncordonNode
-//
-// Takes a node out of pending state.
-//
-//		Schemes: https
-//
-// 		Security:
-//   		oauth
-//
-//		Responses:
-//			200: nodeResponse
-// Takes node out of pending state
-// TODO: May want to use cordon/uncordon terminology instead of "ispending".
-func uncordonNode(w http.ResponseWriter, r *http.Request) {
-	var params = mux.Vars(r)
-	w.Header().Set("Content-Type", "application/json")
-	var nodeid = params["nodeid"]
-	node, err := logic.UncordonNode(nodeid)
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"),
-			fmt.Sprintf("failed to uncordon node [%s]: %v", node.Name, err))
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	logger.Log(1, r.Header.Get("user"), "uncordoned node", node.Name)
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode("SUCCESS")
 
-	runUpdates(&node, false)
+	go func() {
+		if err := mq.PublishPeerUpdate(data.Node.Network, true); err != nil {
+			logger.Log(1, "failed a peer update after creation of node", data.Host.Name)
+		}
+	}()
+	//runForceServerUpdate(&data.Node, true)
 }
 
 // == EGRESS ==
@@ -725,13 +669,13 @@ func uncordonNode(w http.ResponseWriter, r *http.Request) {
 //
 // Create an egress gateway.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 //
-//		Responses:
-//			200: nodeResponse
+//			Responses:
+//				200: nodeResponse
 func createEgressGateway(w http.ResponseWriter, r *http.Request) {
 	var gateway models.EgressGatewayRequest
 	var params = mux.Vars(r)
@@ -753,9 +697,10 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	apiNode := node.ConvertToAPINode()
 	logger.Log(1, r.Header.Get("user"), "created egress gateway on node", gateway.NodeID, "on network", gateway.NetID)
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
+	json.NewEncoder(w).Encode(apiNode)
 
 	runUpdates(&node, true)
 }
@@ -764,13 +709,13 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
 //
 // Delete an egress gateway.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 //
-//		Responses:
-//			200: nodeResponse
+//			Responses:
+//				200: nodeResponse
 func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	var params = mux.Vars(r)
@@ -785,9 +730,10 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	apiNode := node.ConvertToAPINode()
 	logger.Log(1, r.Header.Get("user"), "deleted egress gateway on node", nodeid, "on network", netid)
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
+	json.NewEncoder(w).Encode(apiNode)
 
 	runUpdates(&node, true)
 }
@@ -798,13 +744,13 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
 //
 // Create an ingress gateway.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 //
-//		Responses:
-//			200: nodeResponse
+//			Responses:
+//				200: nodeResponse
 func createIngressGateway(w http.ResponseWriter, r *http.Request) {
 	var params = mux.Vars(r)
 	w.Header().Set("Content-Type", "application/json")
@@ -827,13 +773,14 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
 
 	if servercfg.Is_EE && failoverReqBody.Failover {
 		if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil {
-			logger.Log(1, "failed to reset failover list during failover create", node.Name, node.Network)
+			logger.Log(1, "failed to reset failover list during failover create", node.ID.String(), node.Network)
 		}
 	}
 
+	apiNode := node.ConvertToAPINode()
 	logger.Log(1, r.Header.Get("user"), "created ingress gateway on node", nodeid, "on network", netid)
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
+	json.NewEncoder(w).Encode(apiNode)
 
 	runUpdates(&node, true)
 }
@@ -842,13 +789,13 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
 //
 // Delete an ingress gateway.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 //
-//		Responses:
-//			200: nodeResponse
+//			Responses:
+//				200: nodeResponse
 func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	var params = mux.Vars(r)
@@ -865,40 +812,41 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 
 	if servercfg.Is_EE && wasFailover {
 		if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil {
-			logger.Log(1, "failed to reset failover list during failover create", node.Name, node.Network)
+			logger.Log(1, "failed to reset failover list during failover create", node.ID.String(), node.Network)
 		}
 	}
 
+	apiNode := node.ConvertToAPINode()
 	logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid)
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
+	json.NewEncoder(w).Encode(apiNode)
 
 	runUpdates(&node, true)
 }
 
-// swagger:route PUT /api/nodes/{network}/{nodeid} nodes updateNode
+// swagger:route PUT /api/nodes/{network}/{nodeid}/migrate nodes migrateNode
 //
-// Update an individual node.
+// Used to migrate a legacy node.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 //
-//		Responses:
-//			200: nodeResponse
-func updateNode(w http.ResponseWriter, r *http.Request) {
+//			Responses:
+//				200: nodeResponse
+func nodeNodeUpdate(w http.ResponseWriter, r *http.Request) {
+	// should only be used by nodes
 	w.Header().Set("Content-Type", "application/json")
 
 	var params = mux.Vars(r)
 
-	var node models.Node
 	//start here
 	nodeid := params["nodeid"]
-	node, err := logic.GetNodeByID(nodeid)
+	currentNode, err := logic.GetNodeByID(nodeid)
 	if err != nil {
-		logger.Log(0, r.Header.Get("user"),
-			fmt.Sprintf("error fetching node [ %s ] info: %v", nodeid, err))
+		logger.Log(0,
+			fmt.Sprintf("error fetching node [ %s ] info: %v during migrate", nodeid, err))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
@@ -912,53 +860,129 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	relayupdate := false
-	if node.IsRelay == "yes" && len(newNode.RelayAddrs) > 0 {
-		if len(newNode.RelayAddrs) != len(node.RelayAddrs) {
+	if currentNode.IsRelay && len(newNode.RelayAddrs) > 0 {
+		if len(newNode.RelayAddrs) != len(currentNode.RelayAddrs) {
 			relayupdate = true
 		} else {
 			for i, addr := range newNode.RelayAddrs {
-				if addr != node.RelayAddrs[i] {
+				if addr != currentNode.RelayAddrs[i] {
 					relayupdate = true
 				}
 			}
 		}
 	}
 	relayedUpdate := false
-	if node.IsRelayed == "yes" && (node.Address != newNode.Address || node.Address6 != newNode.Address6) {
+	if currentNode.IsRelayed && (currentNode.Address.String() != newNode.Address.String() || currentNode.Address6.String() != newNode.Address6.String()) {
 		relayedUpdate = true
 	}
 
 	if !servercfg.GetRce() {
-		newNode.PostDown = node.PostDown
-		newNode.PostUp = node.PostUp
+		newNode.PostDown = currentNode.PostDown
+		newNode.PostUp = currentNode.PostUp
 	}
 
-	ifaceDelta := logic.IfaceDelta(&node, &newNode)
-	// for a hub change also need to update the existing hub
-	if newNode.IsHub == "yes" && node.IsHub != "yes" {
-		nodeToUpdate, err := logic.UnsetHub(newNode.Network)
-		if err != nil {
-			logger.Log(2, "failed to unset hubs", err.Error())
-		}
-		if err := mq.NodeUpdate(nodeToUpdate); err != nil {
-			logger.Log(2, "failed to update hub node", nodeToUpdate.Name, err.Error())
+	ifaceDelta := logic.IfaceDelta(&currentNode, &newNode)
+
+	if ifaceDelta && servercfg.Is_EE {
+		if err = logic.EnterpriseResetAllPeersFailovers(currentNode.ID.String(), currentNode.Network); err != nil {
+			logger.Log(0, "failed to reset failover lists during node update for node", currentNode.ID.String(), currentNode.Network)
 		}
-		if nodeToUpdate.IsServer == "yes" {
-			// set ifacdelta true to force server to update peeers
-			if err := logic.ServerUpdate(nodeToUpdate, true); err != nil {
-				logger.Log(2, "failed to update server node on hub change", err.Error())
+	}
+
+	err = logic.UpdateNode(&currentNode, &newNode)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"),
+			fmt.Sprintf("failed to update node info [ %s ] info: %v", nodeid, err))
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	if relayupdate {
+		updatenodes := logic.UpdateRelay(currentNode.Network, currentNode.RelayAddrs, newNode.RelayAddrs)
+		if len(updatenodes) > 0 {
+			for _, relayedNode := range updatenodes {
+				runUpdates(&relayedNode, false)
 			}
+		}
+	}
+	if relayedUpdate {
+		updateRelay(&currentNode, &newNode)
+	}
+	if servercfg.IsDNSMode() {
+		logic.SetDNS()
+	}
 
+	logger.Log(1, r.Header.Get("user"), "updated node", currentNode.ID.String(), "on network", currentNode.Network)
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(newNode)
+
+	runUpdates(&newNode, ifaceDelta)
+}
+
+// swagger:route PUT /api/nodes/{network}/{nodeid} nodes updateNode
+//
+// Update an individual node.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: nodeResponse
+func updateNode(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", "application/json")
+
+	var params = mux.Vars(r)
+
+	//start here
+	nodeid := params["nodeid"]
+	currentNode, err := logic.GetNodeByID(nodeid)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"),
+			fmt.Sprintf("error fetching node [ %s ] info: %v", nodeid, err))
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	var newData models.ApiNode
+	// we decode our body request params
+	err = json.NewDecoder(r.Body).Decode(&newData)
+	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
+	}
+	newNode := newData.ConvertToServerNode(&currentNode)
+	relayupdate := false
+	if currentNode.IsRelay && len(newNode.RelayAddrs) > 0 {
+		if len(newNode.RelayAddrs) != len(currentNode.RelayAddrs) {
+			relayupdate = true
+		} else {
+			for i, addr := range newNode.RelayAddrs {
+				if addr != currentNode.RelayAddrs[i] {
+					relayupdate = true
+				}
+			}
 		}
 	}
+	relayedUpdate := false
+	if currentNode.IsRelayed && (currentNode.Address.String() != newNode.Address.String() || currentNode.Address6.String() != newNode.Address6.String()) {
+		relayedUpdate = true
+	}
+
+	if !servercfg.GetRce() {
+		newNode.PostDown = currentNode.PostDown
+		newNode.PostUp = currentNode.PostUp
+	}
+	ifaceDelta := logic.IfaceDelta(&currentNode, newNode)
 
 	if ifaceDelta && servercfg.Is_EE {
-		if err = logic.EnterpriseResetAllPeersFailovers(node.ID, node.Network); err != nil {
-			logger.Log(0, "failed to reset failover lists during node update for node", node.Name, node.Network)
+		if err = logic.EnterpriseResetAllPeersFailovers(currentNode.ID.String(), currentNode.Network); err != nil {
+			logger.Log(0, "failed to reset failover lists during node update for node", currentNode.ID.String(), currentNode.Network)
 		}
 	}
 
-	err = logic.UpdateNode(&node, &newNode)
+	err = logic.UpdateNode(&currentNode, newNode)
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 			fmt.Sprintf("failed to update node info [ %s ] info: %v", nodeid, err))
@@ -966,7 +990,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	if relayupdate {
-		updatenodes := logic.UpdateRelay(node.Network, node.RelayAddrs, newNode.RelayAddrs)
+		updatenodes := logic.UpdateRelay(currentNode.Network, currentNode.RelayAddrs, newNode.RelayAddrs)
 		if len(updatenodes) > 0 {
 			for _, relayedNode := range updatenodes {
 				runUpdates(&relayedNode, false)
@@ -974,30 +998,31 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 	if relayedUpdate {
-		updateRelay(&node, &newNode)
+		updateRelay(&currentNode, newNode)
 	}
 	if servercfg.IsDNSMode() {
 		logic.SetDNS()
 	}
 
-	logger.Log(1, r.Header.Get("user"), "updated node", node.ID, "on network", node.Network)
+	apiNode := newNode.ConvertToAPINode()
+	logger.Log(1, r.Header.Get("user"), "updated node", currentNode.ID.String(), "on network", currentNode.Network)
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(newNode)
+	json.NewEncoder(w).Encode(apiNode)
 
-	runUpdates(&newNode, ifaceDelta)
+	runUpdates(newNode, ifaceDelta)
 }
 
 // swagger:route DELETE /api/nodes/{network}/{nodeid} nodes deleteNode
 //
 // Delete an individual node.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 //
-//		Responses:
-//			200: nodeResponse
+//			Responses:
+//				200: nodeResponse
 func deleteNode(w http.ResponseWriter, r *http.Request) {
 	// Set header
 	w.Header().Set("Content-Type", "application/json")
@@ -1006,27 +1031,15 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
 	var params = mux.Vars(r)
 	var nodeid = params["nodeid"]
 	fromNode := r.Header.Get("requestfrom") == "node"
-	var node, err = logic.GetNodeByID(nodeid)
+	node, err := logic.GetNodeByID(nodeid)
 	if err != nil {
-		if fromNode {
-			node, err = logic.GetDeletedNodeByID(nodeid)
-			if err != nil {
-				logger.Log(0, r.Header.Get("user"),
-					fmt.Sprintf("error fetching node from deleted nodes [ %s ] info: %v", nodeid, err))
-				logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-				return
-			}
-		} else {
-			logger.Log(0, r.Header.Get("user"),
-				fmt.Sprintf("error fetching node [ %s ] info: %v", nodeid, err))
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-			return
-		}
+		logger.Log(0, "error retrieving node to delete", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
 	}
-	if isServer(&node) {
-		err := fmt.Errorf("cannot delete server node")
-		logger.Log(0, r.Header.Get("user"),
-			fmt.Sprintf("failed to delete node [ %s ]: %v", nodeid, err))
+	host, err := logic.GetHost(node.HostID.String())
+	if err != nil {
+		logger.Log(0, "error retrieving host for node", node.ID.String(), err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 	}
@@ -1037,118 +1050,83 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 	}
-	//send update to node to be deleted before deleting on server otherwise message cannot be sent
-	node.Action = models.NODE_DELETE
-
-	err = logic.DeleteNodeByID(&node, fromNode)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+	if err := logic.DeleteNode(&node, fromNode); err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal"))
 		return
 	}
+	if host.ProxyEnabled {
+		mq.ProxyUpdate(&proxy_models.ProxyManagerPayload{
+			Action:  proxy_models.DeleteNetwork,
+			Network: node.Network,
+		}, &node)
+	}
 	if fromNode {
-		// deletes node related role and client
-		event := mq.MqDynsecPayload{
-			Commands: []mq.MqDynSecCmd{
-				{
-					Command:  mq.DeleteClientCmd,
-					Username: nodeid,
-				},
-			},
-		}
-
-		if err := mq.PublishEventToDynSecTopic(event); err != nil {
-			logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
-				event.Commands, err.Error()))
+		// check if server should be removed from mq
+		// err is irrelevent
+		nodes, _ := logic.GetAllNodes()
+		var foundNode models.Node
+		for _, nodetocheck := range nodes {
+			if nodetocheck.HostID == node.HostID {
+				foundNode = nodetocheck
+				break
+			}
 		}
-	}
-
-	if servercfg.Is_EE {
-		if err = logic.EnterpriseResetAllPeersFailovers(node.ID, node.Network); err != nil {
-			logger.Log(0, "failed to reset failover lists during node delete for node", node.Name, node.Network)
+		// TODO: Address how to remove host
+		if foundNode.HostID != uuid.Nil {
+			if err = logic.DissasociateNodeFromHost(&foundNode, host); err == nil {
+				currNets := logic.GetHostNetworks(host.ID.String())
+				if len(currNets) > 0 {
+					mq.ModifyClient(&mq.MqClient{
+						ID:       host.ID.String(),
+						Text:     host.Name,
+						Networks: currNets,
+					})
+				}
+			}
 		}
 	}
-
 	logic.ReturnSuccessResponse(w, r, nodeid+" deleted.")
 	logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
-	runUpdates(&node, false)
-	runForceServerUpdate(&node, false)
+	if !fromNode {
+		runUpdates(&node, false)
+		return
+	}
+	go func() {
+		if err := mq.PublishPeerUpdate(node.Network, false); err != nil {
+			logger.Log(1, "error publishing peer update ", err.Error())
+			return
+		}
+	}()
+
 }
 
 func runUpdates(node *models.Node, ifaceDelta bool) {
 	go func() { // don't block http response
 		// publish node update if not server
 		if err := mq.NodeUpdate(node); err != nil {
-			logger.Log(1, "error publishing node update to node", node.Name, node.ID, err.Error())
-		}
-
-		if err := runServerUpdate(node, ifaceDelta); err != nil {
-			logger.Log(1, "error running server update", err.Error())
-		}
-	}()
-}
-
-// updates local peers for a server on a given node's network
-func runServerUpdate(node *models.Node, ifaceDelta bool) error {
-
-	if servercfg.IsClientMode() != "on" || !isServer(node) {
-		return nil
-	}
-
-	currentServerNode, err := logic.GetNetworkServerLocal(node.Network)
-	if err != nil {
-		return err
-	}
-
-	if ifaceDelta && logic.IsLeader(&currentServerNode) {
-		if err := mq.PublishPeerUpdate(&currentServerNode, false); err != nil {
-			logger.Log(1, "failed to publish peer update "+err.Error())
-		}
-	}
-
-	if err := logic.ServerUpdate(&currentServerNode, ifaceDelta); err != nil {
-		logger.Log(1, "server node:", currentServerNode.ID, "failed update")
-		return err
-	}
-	return nil
-}
-
-func runForceServerUpdate(node *models.Node, publishPeerUpdateToNode bool) {
-	go func() {
-		if err := mq.PublishPeerUpdate(node, publishPeerUpdateToNode); err != nil {
-			logger.Log(1, "failed a peer update after creation of node", node.Name)
-		}
-
-		var currentServerNode, getErr = logic.GetNetworkServerLeader(node.Network)
-		if getErr == nil {
-			if err := logic.ServerUpdate(&currentServerNode, false); err != nil {
-				logger.Log(1, "server node:", currentServerNode.ID, "failed update")
-			}
+			logger.Log(1, "error publishing node update to node", node.ID.String(), err.Error())
 		}
 	}()
 }
 
-func isServer(node *models.Node) bool {
-	return node.IsServer == "yes"
-}
-
 func updateRelay(oldnode, newnode *models.Node) {
 	relay := logic.FindRelay(oldnode)
 	newrelay := relay
 	//check if node's address has been updated and if so, update the relayAddrs of the relay node with the updated address of the relayed node
-	if oldnode.Address != newnode.Address {
+	if oldnode.Address.String() != newnode.Address.String() {
 		for i, ip := range newrelay.RelayAddrs {
-			if ip == oldnode.Address {
+			if ip == oldnode.Address.IP.String() {
 				newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], relay.RelayAddrs[i+1:]...)
-				newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address)
+				newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address.IP.String())
 			}
 		}
 	}
 	//check if node's address(v6) has been updated and if so, update the relayAddrs of the relay node with the updated address(v6) of the relayed node
-	if oldnode.Address6 != newnode.Address6 {
+	if oldnode.Address6.String() != newnode.Address6.String() {
 		for i, ip := range newrelay.RelayAddrs {
-			if ip == oldnode.Address {
+			if ip == oldnode.Address.IP.String() {
 				newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], newrelay.RelayAddrs[i+1:]...)
-				newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address6)
+				newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address6.IP.String())
 			}
 		}
 	}

+ 11 - 11
controllers/node_test.go

@@ -21,16 +21,16 @@ func TestCreateEgressGateway(t *testing.T) {
 	createNet()
 	t.Run("NoNodes", func(t *testing.T) {
 		node, err := logic.CreateEgressGateway(gateway)
-		assert.Equal(t, models.Node{}, node)
+		assert.Equal(t, models.LegacyNode{}, node)
 		assert.EqualError(t, err, "could not find any records")
 	})
 	t.Run("Non-linux node", func(t *testing.T) {
-		createnode := models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: "testnode", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet", OS: "windows"}
+		createnode := models.LegacyNode{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: "testnode", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet", OS: "windows"}
 		err := logic.CreateNode(&createnode)
 		assert.Nil(t, err)
 		gateway.NodeID = createnode.ID
 		node, err := logic.CreateEgressGateway(gateway)
-		assert.Equal(t, models.Node{}, node)
+		assert.Equal(t, models.LegacyNode{}, node)
 		assert.EqualError(t, err, "windows is unsupported for egress gateways")
 	})
 	t.Run("Success-Nat-Enabled", func(t *testing.T) {
@@ -113,7 +113,7 @@ func TestDeleteEgressGateway(t *testing.T) {
 	t.Run("BadNode", func(t *testing.T) {
 		node, err := logic.DeleteEgressGateway(gateway.NetID, "01:02:03")
 		assert.EqualError(t, err, "no result found")
-		assert.Equal(t, models.Node{}, node)
+		assert.Equal(t, models.LegacyNode{}, node)
 		deleteAllNodes()
 	})
 }
@@ -136,7 +136,7 @@ func TestGetNetworkNodes(t *testing.T) {
 		createTestNode()
 		node, err := logic.GetNetworkNodes("skynet")
 		assert.Nil(t, err)
-		assert.NotEqual(t, []models.Node(nil), node)
+		assert.NotEqual(t, []models.LegacyNode(nil), node)
 	})
 
 }
@@ -147,7 +147,7 @@ func TestUncordonNode(t *testing.T) {
 	node := createTestNode()
 	t.Run("BadID", func(t *testing.T) {
 		resp, err := logic.UncordonNode("blahblah")
-		assert.Equal(t, models.Node{}, resp)
+		assert.Equal(t, models.LegacyNode{}, resp)
 		assert.EqualError(t, err, "no result found")
 	})
 	t.Run("Success", func(t *testing.T) {
@@ -181,8 +181,8 @@ func TestValidateEgressGateway(t *testing.T) {
 
 func TestNodeACLs(t *testing.T) {
 	deleteAllNodes()
-	node1 := models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: "testnode", Endpoint: "10.0.0.50", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet", OS: "linux"}
-	node2 := models.Node{PublicKey: "DM5qhLAE20FG7BbfBCger+Ac9D2NDOwCtY1rbYDXf14=", Name: "testnode", Endpoint: "10.0.0.100", MacAddress: "01:02:03:04:05:07", Password: "password", Network: "skynet", OS: "linux"}
+	node1 := models.LegacyNode{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: "testnode", Endpoint: "10.0.0.50", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet", OS: "linux"}
+	node2 := models.LegacyNode{PublicKey: "DM5qhLAE20FG7BbfBCger+Ac9D2NDOwCtY1rbYDXf14=", Name: "testnode", Endpoint: "10.0.0.100", MacAddress: "01:02:03:04:05:07", Password: "password", Network: "skynet", OS: "linux"}
 	logic.CreateNode(&node1)
 	logic.CreateNode(&node2)
 	t.Run("acls not present", func(t *testing.T) {
@@ -222,7 +222,7 @@ func TestNodeACLs(t *testing.T) {
 		currentACL.Save(acls.ContainerID(node1.Network))
 	})
 	t.Run("node acls correct after add new node not allowed", func(t *testing.T) {
-		node3 := models.Node{PublicKey: "this-is-not-valid", Name: "testnode3", Endpoint: "10.0.0.100", MacAddress: "01:02:03:04:05:07", Password: "password", Network: "skynet", OS: "linux"}
+		node3 := models.LegacyNode{PublicKey: "this-is-not-valid", Name: "testnode3", Endpoint: "10.0.0.100", MacAddress: "01:02:03:04:05:07", Password: "password", Network: "skynet", OS: "linux"}
 		logic.CreateNode(&node3)
 		var currentACL, err = nodeacls.FetchAllACLs(nodeacls.NetworkID(node3.Network))
 		assert.Nil(t, err)
@@ -249,8 +249,8 @@ func deleteAllNodes() {
 	database.DeleteAllRecords(database.NODES_TABLE_NAME)
 }
 
-func createTestNode() *models.Node {
-	createnode := models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: "testnode", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet", OS: "linux"}
+func createTestNode() *models.LegacyNode {
+	createnode := models.LegacyNode{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: "testnode", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet", OS: "linux"}
 	logic.CreateNode(&createnode)
 	return &createnode
 }

+ 19 - 14
controllers/relay.go

@@ -16,13 +16,13 @@ import (
 //
 // Create a relay.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 //
-//		Responses:
-//			200: nodeResponse
+//			Responses:
+//				200: nodeResponse
 func createRelay(w http.ResponseWriter, r *http.Request) {
 	var relay models.RelayRequest
 	var params = mux.Vars(r)
@@ -42,15 +42,19 @@ func createRelay(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
+
 	logger.Log(1, r.Header.Get("user"), "created relay on node", relay.NodeID, "on network", relay.NetID)
 	for _, relayedNode := range updatenodes {
+
 		err = mq.NodeUpdate(&relayedNode)
 		if err != nil {
-			logger.Log(1, "error sending update to relayed node ", relayedNode.Name, "on network", relay.NetID, ": ", err.Error())
+			logger.Log(1, "error sending update to relayed node ", relayedNode.ID.String(), "on network", relay.NetID, ": ", err.Error())
 		}
 	}
+
+	apiNode := node.ConvertToAPINode()
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
+	json.NewEncoder(w).Encode(apiNode)
 	runUpdates(&node, true)
 }
 
@@ -58,13 +62,13 @@ func createRelay(w http.ResponseWriter, r *http.Request) {
 //
 // Remove a relay.
 //
-//		Schemes: https
+//			Schemes: https
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 //
-//		Responses:
-//			200: nodeResponse
+//			Responses:
+//				200: nodeResponse
 func deleteRelay(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	var params = mux.Vars(r)
@@ -80,10 +84,11 @@ func deleteRelay(w http.ResponseWriter, r *http.Request) {
 	for _, relayedNode := range updatenodes {
 		err = mq.NodeUpdate(&relayedNode)
 		if err != nil {
-			logger.Log(1, "error sending update to relayed node ", relayedNode.Name, "on network", netid, ": ", err.Error())
+			logger.Log(1, "error sending update to relayed node ", relayedNode.ID.String(), "on network", netid, ": ", err.Error())
 		}
 	}
+	apiNode := node.ConvertToAPINode()
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
+	json.NewEncoder(w).Encode(apiNode)
 	runUpdates(&node, true)
 }

+ 68 - 90
database/database.go

@@ -14,96 +14,73 @@ import (
 	"golang.org/x/crypto/nacl/box"
 )
 
-// NETWORKS_TABLE_NAME - networks table
-const NETWORKS_TABLE_NAME = "networks"
-
-// NODES_TABLE_NAME - nodes table
-const NODES_TABLE_NAME = "nodes"
-
-// DELETED_NODES_TABLE_NAME - deleted nodes table
-const DELETED_NODES_TABLE_NAME = "deletednodes"
-
-// USERS_TABLE_NAME - users table
-const USERS_TABLE_NAME = "users"
-
-// CERTS_TABLE_NAME - certificates table
-const CERTS_TABLE_NAME = "certs"
-
-// DNS_TABLE_NAME - dns table
-const DNS_TABLE_NAME = "dns"
-
-// EXT_CLIENT_TABLE_NAME - ext client table
-const EXT_CLIENT_TABLE_NAME = "extclients"
-
-// PEERS_TABLE_NAME - peers table
-const PEERS_TABLE_NAME = "peers"
-
-// SERVERCONF_TABLE_NAME - stores server conf
-const SERVERCONF_TABLE_NAME = "serverconf"
-
-// SERVER_UUID_TABLE_NAME - stores unique netmaker server data
-const SERVER_UUID_TABLE_NAME = "serveruuid"
-
-// SERVER_UUID_RECORD_KEY - telemetry thing
-const SERVER_UUID_RECORD_KEY = "serveruuid"
-
-// DATABASE_FILENAME - database file name
-const DATABASE_FILENAME = "netmaker.db"
-
-// GENERATED_TABLE_NAME - stores server generated k/v
-const GENERATED_TABLE_NAME = "generated"
-
-// NODE_ACLS_TABLE_NAME - stores the node ACL rules
-const NODE_ACLS_TABLE_NAME = "nodeacls"
-
-// SSO_STATE_CACHE - holds sso session information for OAuth2 sign-ins
-const SSO_STATE_CACHE = "ssostatecache"
-
-// METRICS_TABLE_NAME - stores network metrics
-const METRICS_TABLE_NAME = "metrics"
-
-// NETWORK_USER_TABLE_NAME - network user table tracks stats for a network user per network
-const NETWORK_USER_TABLE_NAME = "networkusers"
-
-// USER_GROUPS_TABLE_NAME - table for storing usergroups
-const USER_GROUPS_TABLE_NAME = "usergroups"
-
-// CACHE_TABLE_NAME - caching table
-const CACHE_TABLE_NAME = "cache"
-
-// == ERROR CONSTS ==
-
-// NO_RECORD - no singular result found
-const NO_RECORD = "no result found"
-
-// NO_RECORDS - no results found
-const NO_RECORDS = "could not find any records"
-
-// == Constants ==
-
-// INIT_DB - initialize db
-const INIT_DB = "init"
-
-// CREATE_TABLE - create table const
-const CREATE_TABLE = "createtable"
-
-// INSERT - insert into db const
-const INSERT = "insert"
-
-// INSERT_PEER - insert peer into db const
-const INSERT_PEER = "insertpeer"
-
-// DELETE - delete db record const
-const DELETE = "delete"
-
-// DELETE_ALL - delete a table const
-const DELETE_ALL = "deleteall"
-
-// FETCH_ALL - fetch table contents const
-const FETCH_ALL = "fetchall"
-
-// CLOSE_DB - graceful close of db const
-const CLOSE_DB = "closedb"
+const (
+	// == Table Names ==
+	// NETWORKS_TABLE_NAME - networks table
+	NETWORKS_TABLE_NAME = "networks"
+	// NODES_TABLE_NAME - nodes table
+	NODES_TABLE_NAME = "nodes"
+	// DELETED_NODES_TABLE_NAME - deleted nodes table
+	DELETED_NODES_TABLE_NAME = "deletednodes"
+	// USERS_TABLE_NAME - users table
+	USERS_TABLE_NAME = "users"
+	// CERTS_TABLE_NAME - certificates table
+	CERTS_TABLE_NAME = "certs"
+	// DNS_TABLE_NAME - dns table
+	DNS_TABLE_NAME = "dns"
+	// EXT_CLIENT_TABLE_NAME - ext client table
+	EXT_CLIENT_TABLE_NAME = "extclients"
+	// PEERS_TABLE_NAME - peers table
+	PEERS_TABLE_NAME = "peers"
+	// SERVERCONF_TABLE_NAME - stores server conf
+	SERVERCONF_TABLE_NAME = "serverconf"
+	// SERVER_UUID_TABLE_NAME - stores unique netmaker server data
+	SERVER_UUID_TABLE_NAME = "serveruuid"
+	// SERVER_UUID_RECORD_KEY - telemetry thing
+	SERVER_UUID_RECORD_KEY = "serveruuid"
+	// DATABASE_FILENAME - database file name
+	DATABASE_FILENAME = "netmaker.db"
+	// GENERATED_TABLE_NAME - stores server generated k/v
+	GENERATED_TABLE_NAME = "generated"
+	// NODE_ACLS_TABLE_NAME - stores the node ACL rules
+	NODE_ACLS_TABLE_NAME = "nodeacls"
+	// SSO_STATE_CACHE - holds sso session information for OAuth2 sign-ins
+	SSO_STATE_CACHE = "ssostatecache"
+	// METRICS_TABLE_NAME - stores network metrics
+	METRICS_TABLE_NAME = "metrics"
+	// NETWORK_USER_TABLE_NAME - network user table tracks stats for a network user per network
+	NETWORK_USER_TABLE_NAME = "networkusers"
+	// USER_GROUPS_TABLE_NAME - table for storing usergroups
+	USER_GROUPS_TABLE_NAME = "usergroups"
+	// CACHE_TABLE_NAME - caching table
+	CACHE_TABLE_NAME = "cache"
+	// HOSTS_TABLE_NAME - the table name for hosts
+	HOSTS_TABLE_NAME = "hosts"
+
+	// == ERROR CONSTS ==
+	// NO_RECORD - no singular result found
+	NO_RECORD = "no result found"
+	// NO_RECORDS - no results found
+	NO_RECORDS = "could not find any records"
+
+	// == DB Constants ==
+	// INIT_DB - initialize db
+	INIT_DB = "init"
+	// CREATE_TABLE - create table const
+	CREATE_TABLE = "createtable"
+	// INSERT - insert into db const
+	INSERT = "insert"
+	// INSERT_PEER - insert peer into db const
+	INSERT_PEER = "insertpeer"
+	// DELETE - delete db record const
+	DELETE = "delete"
+	// DELETE_ALL - delete a table const
+	DELETE_ALL = "deleteall"
+	// FETCH_ALL - fetch table contents const
+	FETCH_ALL = "fetchall"
+	// CLOSE_DB - graceful close of db const
+	CLOSE_DB = "closedb"
+)
 
 func getCurrentDB() map[string]interface{} {
 	switch servercfg.GetDB() {
@@ -155,6 +132,7 @@ func createTables() {
 	createTable(NETWORK_USER_TABLE_NAME)
 	createTable(USER_GROUPS_TABLE_NAME)
 	createTable(CACHE_TABLE_NAME)
+	createTable(HOSTS_TABLE_NAME)
 }
 
 func createTable(tableName string) error {

+ 6 - 0
docker/Caddyfile

@@ -30,6 +30,12 @@ https://api.NETMAKER_BASE_DOMAIN {
         reverse_proxy http://netmaker:8081
 }
 
+# STUN
+https://stun.NETMAKER_BASE_DOMAIN {
+	reverse_proxy netmaker:3478
+}
+
+
 # MQ
 wss://broker.NETMAKER_BASE_DOMAIN {
         reverse_proxy ws://mq:8883

+ 5 - 0
docker/Caddyfile-EE

@@ -44,6 +44,11 @@ https://api.NETMAKER_BASE_DOMAIN {
         reverse_proxy http://netmaker:8081
 }
 
+# STUN
+https://stun.NETMAKER_BASE_DOMAIN {
+	reverse_proxy netmaker:3478
+}
+
 # MQ
 wss://broker.NETMAKER_BASE_DOMAIN {
         reverse_proxy ws://mq:8883

+ 8 - 8
ee/ee_controllers/metrics.go

@@ -60,12 +60,12 @@ func getNetworkNodesMetrics(w http.ResponseWriter, r *http.Request) {
 
 	for i := range networkNodes {
 		id := networkNodes[i].ID
-		metrics, err := logic.GetMetrics(id)
+		metrics, err := logic.GetMetrics(id.String())
 		if err != nil {
-			logger.Log(1, r.Header.Get("user"), "failed to append metrics of node", id, "during network metrics fetch", err.Error())
+			logger.Log(1, r.Header.Get("user"), "failed to append metrics of node", id.String(), "during network metrics fetch", err.Error())
 			continue
 		}
-		networkMetrics.Nodes[id] = *metrics
+		networkMetrics.Nodes[id.String()] = *metrics
 	}
 
 	logger.Log(1, r.Header.Get("user"), "fetched metrics for network", network)
@@ -101,9 +101,9 @@ func getNetworkExtMetrics(w http.ResponseWriter, r *http.Request) {
 
 	for i := range ingresses {
 		id := ingresses[i].ID
-		ingressMetrics, err := logic.GetMetrics(id)
+		ingressMetrics, err := logic.GetMetrics(id.String())
 		if err != nil {
-			logger.Log(1, r.Header.Get("user"), "failed to append external client metrics from ingress node", id, err.Error())
+			logger.Log(1, r.Header.Get("user"), "failed to append external client metrics from ingress node", id.String(), err.Error())
 			continue
 		}
 		if ingressMetrics.Connectivity == nil {
@@ -142,12 +142,12 @@ func getAllMetrics(w http.ResponseWriter, r *http.Request) {
 
 	for i := range allNodes {
 		id := allNodes[i].ID
-		metrics, err := logic.GetMetrics(id)
+		metrics, err := logic.GetMetrics(id.String())
 		if err != nil {
-			logger.Log(1, r.Header.Get("user"), "failed to append metrics of node", id, "during all nodes metrics fetch", err.Error())
+			logger.Log(1, r.Header.Get("user"), "failed to append metrics of node", id.String(), "during all nodes metrics fetch", err.Error())
 			continue
 		}
-		networkMetrics.Nodes[id] = *metrics
+		networkMetrics.Nodes[id.String()] = *metrics
 	}
 
 	logger.Log(1, r.Header.Get("user"), "fetched metrics for all nodes on server")

+ 4 - 4
ee/ee_controllers/networkusers.go

@@ -110,16 +110,16 @@ func getNetworkUserData(w http.ResponseWriter, r *http.Request) {
 						// if access level is NODE_ACCESS, filter nodes
 						if netUser.AccessLevel == pro.NODE_ACCESS {
 							for i := range netNodes {
-								if logic.StringSliceContains(netUser.Nodes, netNodes[i].ID) {
+								if logic.StringSliceContains(netUser.Nodes, netNodes[i].ID.String()) {
 									newData.Nodes = append(newData.Nodes, netNodes[i])
 								}
 							}
 						} else { // net admin so, get all nodes and ext clients on network...
 							newData.Nodes = netNodes
 							for i := range netNodes {
-								if netNodes[i].IsIngressGateway == "yes" {
+								if netNodes[i].IsIngressGateway {
 									newData.Vpn = append(newData.Vpn, netNodes[i])
-									if clients, err := logic.GetExtClientsByID(netNodes[i].ID, netID); err == nil {
+									if clients, err := logic.GetExtClientsByID(netNodes[i].ID.String(), netID); err == nil {
 										newData.Clients = append(newData.Clients, clients...)
 									}
 								}
@@ -134,7 +134,7 @@ func getNetworkUserData(w http.ResponseWriter, r *http.Request) {
 							}
 						}
 						for i := range netNodes {
-							if netNodes[i].IsIngressGateway == "yes" {
+							if netNodes[i].IsIngressGateway {
 								newData.Vpn = append(newData.Vpn, netNodes[i])
 							}
 						}

+ 14 - 13
ee/logic/failover.go

@@ -1,6 +1,7 @@
 package logic
 
 import (
+	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
@@ -24,11 +25,11 @@ func ResetFailover(network string) error {
 	for _, node := range nodes {
 		err = SetFailover(&node)
 		if err != nil {
-			logger.Log(2, "error setting failover for node", node.Name, ":", err.Error())
+			logger.Log(2, "error setting failover for node", node.ID.String(), ":", err.Error())
 		}
-		err = WipeFailover(node.ID)
+		err = WipeFailover(node.ID.String())
 		if err != nil {
-			logger.Log(2, "error wiping failover for node", node.Name, ":", err.Error())
+			logger.Log(2, "error wiping failover for node", node.ID.String(), ":", err.Error())
 		}
 	}
 	return nil
@@ -43,7 +44,7 @@ func determineFailoverCandidate(nodeToBeRelayed *models.Node) *models.Node {
 		return nil
 	}
 
-	currentMetrics, err := logic.GetMetrics(nodeToBeRelayed.ID)
+	currentMetrics, err := logic.GetMetrics(nodeToBeRelayed.ID.String())
 	if err != nil || currentMetrics == nil || currentMetrics.Connectivity == nil {
 		return nil
 	}
@@ -55,10 +56,10 @@ func determineFailoverCandidate(nodeToBeRelayed *models.Node) *models.Node {
 			continue
 		}
 
-		if currentMetrics.Connectivity[currentNetworkNodes[i].ID].Connected && (currentNetworkNodes[i].Failover == "yes") {
-			if currentMetrics.Connectivity[currentNetworkNodes[i].ID].Latency < int64(minLatency) {
+		if currentMetrics.Connectivity[currentNetworkNodes[i].ID.String()].Connected && (currentNetworkNodes[i].Failover) {
+			if currentMetrics.Connectivity[currentNetworkNodes[i].ID.String()].Latency < int64(minLatency) {
 				fastestCandidate = &currentNetworkNodes[i]
-				minLatency = currentMetrics.Connectivity[currentNetworkNodes[i].ID].Latency
+				minLatency = currentMetrics.Connectivity[currentNetworkNodes[i].ID.String()].Latency
 			}
 		}
 	}
@@ -70,7 +71,7 @@ func determineFailoverCandidate(nodeToBeRelayed *models.Node) *models.Node {
 func setFailoverNode(failoverNode, node *models.Node) error {
 
 	node.FailoverNode = failoverNode.ID
-	nodeToUpdate, err := logic.GetNodeByID(node.ID)
+	nodeToUpdate, err := logic.GetNodeByID(node.ID.String())
 	if err != nil {
 		return err
 	}
@@ -95,25 +96,25 @@ func WipeFailover(nodeid string) error {
 
 // WipeAffectedFailoversOnly - wipes failovers for nodes that have given node (ID)
 // in their respective failover lists
-func WipeAffectedFailoversOnly(nodeid, network string) error {
+func WipeAffectedFailoversOnly(nodeid uuid.UUID, network string) error {
 	currentNetworkNodes, err := logic.GetNetworkNodes(network)
 	if err != nil {
 		return nil
 	}
-	WipeFailover(nodeid)
+	WipeFailover(nodeid.String())
 
 	for i := range currentNetworkNodes {
 		currNodeID := currentNetworkNodes[i].ID
 		if currNodeID == nodeid {
 			continue
 		}
-		currMetrics, err := logic.GetMetrics(currNodeID)
+		currMetrics, err := logic.GetMetrics(currNodeID.String())
 		if err != nil || currMetrics == nil {
 			continue
 		}
 		if currMetrics.FailoverPeers != nil {
-			if len(currMetrics.FailoverPeers[nodeid]) > 0 {
-				WipeFailover(currNodeID)
+			if len(currMetrics.FailoverPeers[nodeid.String()]) > 0 {
+				WipeFailover(currNodeID.String())
 			}
 		}
 	}

+ 0 - 1
ee/util.go

@@ -45,6 +45,5 @@ func getCurrentServerLimit() (limits LicenseLimits) {
 	if err == nil {
 		limits.Users = len(users)
 	}
-	limits.Servers = logic.GetServerCount()
 	return
 }

+ 23 - 56
go.mod

@@ -15,98 +15,65 @@ require (
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 	github.com/stretchr/testify v1.8.1
 	github.com/txn2/txeh v1.3.0
-	github.com/urfave/cli/v2 v2.23.7
-	golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
-	golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
+	golang.org/x/crypto v0.3.0
+	golang.org/x/net v0.2.0 // indirect
 	golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094
-	golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
-	golang.org/x/text v0.3.7 // indirect
-	golang.zx2c4.com/wireguard v0.0.0-20220318042302-193cf8d6a5d6 // indirect
+	golang.org/x/sys v0.2.0 // indirect
+	golang.org/x/text v0.4.0 // indirect
+	golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c
 	golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220324164955-056925b7df31
 	google.golang.org/protobuf v1.28.1 // indirect
-	gopkg.in/ini.v1 v1.67.0
 	gopkg.in/yaml.v3 v3.0.1
 )
 
 require (
 	filippo.io/edwards25519 v1.0.0
-	fyne.io/fyne/v2 v2.2.4
-	github.com/c-robinson/iplib v1.0.4
-	github.com/cloverstd/tcping v0.1.1
+	github.com/c-robinson/iplib v1.0.3
 	github.com/go-ping/ping v1.1.0
-	github.com/guumaster/hostctl v1.1.3
-	github.com/kr/pretty v0.3.1
 	github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0
 )
 
 require (
-	github.com/agnivade/levenshtein v1.1.1
 	github.com/coreos/go-oidc/v3 v3.4.0
 	github.com/gorilla/websocket v1.5.0
+	github.com/pkg/errors v0.9.1
+	github.com/sirupsen/logrus v1.9.0
+	golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e
+	gortc.io/stun v1.23.0
+)
+
+require (
+	github.com/gravitl/netclient v0.0.0-20221228022055-5bdb0bc7861d
 	github.com/guumaster/tablewriter v0.0.10
+	github.com/matryer/is v1.4.0
 	github.com/olekukonko/tablewriter v0.0.5
-	github.com/spf13/cobra v1.6.1
-	golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e
-	golang.org/x/term v0.0.0-20220722155259-a9ba230a4035
+	github.com/spf13/cobra v1.6.0
+)
+
+require (
+	github.com/inconshreveable/mousetrap v1.0.1 // indirect
+	github.com/rivo/uniseg v0.1.0 // indirect
+	github.com/spf13/pflag v1.0.5 // indirect
 )
 
 require (
 	cloud.google.com/go/compute v1.7.0 // indirect
-	fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93 // indirect
-	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
-	github.com/Microsoft/go-winio v0.5.2 // indirect
-	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
-	github.com/docker/distribution v2.8.1+incompatible // indirect
-	github.com/docker/docker v20.10.17+incompatible // indirect
-	github.com/docker/go-connections v0.4.0 // indirect
-	github.com/docker/go-units v0.4.0 // indirect
 	github.com/felixge/httpsnoop v1.0.3 // indirect
-	github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 // indirect
-	github.com/fsnotify/fsnotify v1.5.4 // indirect
-	github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // indirect
-	github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 // indirect
-	github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // indirect
-	github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect
-	github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec // indirect
 	github.com/go-playground/locales v0.14.0 // indirect
 	github.com/go-playground/universal-translator v0.18.0 // indirect
-	github.com/godbus/dbus/v5 v5.1.0 // indirect
-	github.com/gogo/protobuf v1.3.2 // indirect
-	github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/google/go-cmp v0.5.8 // indirect
-	github.com/gopherjs/gopherjs v1.17.2 // indirect
-	github.com/inconshreveable/mousetrap v1.0.1 // indirect
+	github.com/hashicorp/go-version v1.6.0
 	github.com/josharian/native v1.0.0 // indirect
-	github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
-	github.com/kr/text v0.2.0 // indirect
 	github.com/leodido/go-urn v1.2.1 // indirect
 	github.com/mattn/go-runewidth v0.0.10 // indirect
 	github.com/mdlayher/genetlink v1.2.0 // indirect
 	github.com/mdlayher/netlink v1.6.0 // indirect
 	github.com/mdlayher/socket v0.1.1 // indirect
-	github.com/opencontainers/go-digest v1.0.0 // indirect
-	github.com/opencontainers/image-spec v1.0.2 // indirect
-	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/rivo/uniseg v0.1.0 // indirect
-	github.com/rogpeppe/go-internal v1.9.0 // indirect
-	github.com/russross/blackfriday/v2 v2.1.0 // indirect
-	github.com/sirupsen/logrus v1.9.0 // indirect
-	github.com/spf13/afero v1.9.2 // indirect
-	github.com/spf13/pflag v1.0.5 // indirect
-	github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect
-	github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect
-	github.com/tevino/abool v1.2.0 // indirect
-	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
 	github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
-	github.com/yuin/goldmark v1.4.13 // indirect
-	golang.org/x/image v0.0.0-20220601225756-64ec528b34cd // indirect
-	golang.org/x/mobile v0.0.0-20211207041440-4e6c2922fdee // indirect
 	golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
 	google.golang.org/appengine v1.6.7 // indirect
 	gopkg.in/square/go-jose.v2 v2.6.0 // indirect
-	gopkg.in/yaml.v2 v2.4.0 // indirect
-	honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2 // indirect
 )

+ 22 - 231
go.sum

@@ -3,7 +3,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
 cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
 cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
 cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
 cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
 cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
 cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
@@ -16,7 +15,6 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY
 cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
 cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
 cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
-cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
 cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
 cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
 cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
@@ -45,7 +43,6 @@ cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9U
 cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
-cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
 cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
 cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
 cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
@@ -56,37 +53,17 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
 cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
 cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
-cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
 cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
 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/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
-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/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/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-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/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/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/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-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
-github.com/c-robinson/iplib v1.0.4 h1:S7EuLks/AicXzVJ9+oMvnqXUACugGvv9eSXYaqhDURE=
-github.com/c-robinson/iplib v1.0.4/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo=
+github.com/c-robinson/iplib v1.0.3 h1:NG0UF0GoEsrC1/vyfX1Lx2Ss7CySWl3KqqXh3q4DdPU=
+github.com/c-robinson/iplib v1.0.3/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -94,8 +71,6 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cloverstd/tcping v0.1.1 h1:3Yp9nvSDI7Z63zoVQDJzVk1PUczrF9tJoOrKGV30iOk=
-github.com/cloverstd/tcping v0.1.1/go.mod h1:NYXTrTDwlwuOKQ0vwksUVUbIr0sxDDsf1J6aFpScCBo=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -110,28 +85,13 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz
 github.com/coreos/go-oidc/v3 v3.4.0 h1:xz7elHb/LDwm/ERpwHd+5nb7wFHL32rsr6bBOgaeu6g=
 github.com/coreos/go-oidc/v3 v3.4.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw=
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
 github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 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/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/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/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
-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/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 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=
@@ -144,31 +104,14 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
 github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
 github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
 github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
-github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA=
-github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3/go.mod h1:CzM2G82Q9BDUvMTGHnXf/6OExw/Dz2ivDj48nVg7Lg8=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
-github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
-github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe h1:A/wiwvQ0CAjPkuJytaD+SsXkPU0asQ+guQEIg1BJGX4=
-github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe/go.mod h1:d4clgH0/GrRwWjRzJJQXxT/h1TyuNSfF/X64zb/3Ggg=
-github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 h1:+31CdF/okdokeFNoy9L/2PccG3JFidQT3ev64/r4pYU=
-github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504/go.mod h1:gLRWYfYnMA9TONeppRSikMdXlHQ97xVsPojddUv3b/E=
-github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 h1:hnLq+55b7Zh7/2IRzWCpiTcAvjv/P8ERF+N7+xXbZhk=
-github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2/go.mod h1:eO7W361vmlPOrykIg+Rsh1SZ3tQBaOsfzZhsIOb/Lm0=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk=
-github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec h1:3FLiRYO6PlQFDpUU7OEFlWgjGD1jnBIVSJ5SYRWk+9c=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
 github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
 github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
@@ -179,13 +122,6 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j
 github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
 github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
 github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
-github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8=
-github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
 github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
 github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -237,7 +173,6 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
 github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -251,7 +186,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
 github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
@@ -271,11 +205,6 @@ github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/Oth
 github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
 github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
 github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
-github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/gopherjs/gopherjs v0.0.0-20211219123610-ec9572f70e60/go.mod h1:cz9oNYuRUWGdHmLF2IodMLkAhcPtXeULvcBNagUrxTI=
-github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
-github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
 github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
 github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
 github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
@@ -283,56 +212,30 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
 github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY=
-github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywTcjV6wZlOU4GuVFQ8F5328KY3MJ79CY=
+github.com/gravitl/netclient v0.0.0-20221228022055-5bdb0bc7861d h1:sAVmZR4L1WB/dTr5nFuaRNzq3nvWSmLS5+Y+4TfVSuQ=
+github.com/gravitl/netclient v0.0.0-20221228022055-5bdb0bc7861d/go.mod h1:DC4NIT30g4E1deqofUtYzD3J4Rc+zyC5m+qA2w4g7Ns=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/guumaster/hostctl v1.1.3 h1:b/yR3svkYsbr5VBdvfdyLXUl2xaKopSzgE/Xi7+1WRo=
-github.com/guumaster/hostctl v1.1.3/go.mod h1:h5rDx5Z8Hj2bYZfDt/eX4BNS2RSq7iRcGVQqfROJyH8=
 github.com/guumaster/tablewriter v0.0.10 h1:A0HD94yMdt4usgxBjoEceNeE0XMJ027euoHAzsPqBQs=
 github.com/guumaster/tablewriter v0.0.10/go.mod h1:p4FRFhyfo0UD9ZLmMRbbJooTUsxo6b80qZTERVDWrH8=
-github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
-github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
-github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
-github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
-github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
-github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
-github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
-github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
-github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
-github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
+github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
+github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
-github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
-github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
-github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
 github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/jackmordaunt/icns/v2 v2.2.1/go.mod h1:6aYIB9eSzyfHHMKqDf17Xrs1zetQPReAkiUSHzdw4cI=
-github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY=
 github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
 github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
-github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
-github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e h1:LvL4XsI70QxOGHed6yhQtAU34Kx3Qq2wwBzGFKY8zKk=
-github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
-github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
-github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -341,62 +244,32 @@ github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
 github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
 github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
 github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2OJReAmwzKivcYyremnibWGbK7WfftHzc=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
-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/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
+github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
 github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
 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/mdlayher/genetlink v1.2.0 h1:4yrIkRV5Wfk1WfpWTcoOlGmsWgQj3OtQN9ZsbrE+XtU=
 github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ=
 github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0=
 github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
 github.com/mdlayher/socket v0.1.1 h1:q3uOGirUPfAV2MUoaC7BavjQ154J7+JOkTWyiV+intI=
 github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
-github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
 github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
-github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
-github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
-github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
-github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
-github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI=
-github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
-github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
-github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
-github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
-github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
-github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
-github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
-github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
-github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
 github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
-github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
 github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0 h1:Y2hUrkfuM0on62KZOci/VLijlkdF/yeWU262BQgvcjE=
 github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -407,76 +280,44 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
 github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
 github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
-github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f h1:BSnJgAfHzEp7o8PYJ7YfwAVHhqu7BYUTggcn/LGlUWY=
 github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f/go.mod h1:UW/gxgQwSePTvL1KA8QEHsXeYHP4xkoXgbDdN781p34=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
-github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
-github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
-github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
 github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
-github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
-github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
-github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw=
-github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
-github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
-github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
-github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
+github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI=
+github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
-github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
-github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 h1:HunZiaEKNGVdhTRQOVpMmj5MQnGnv+e8uZNu3xFLgyM=
-github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
-github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 h1:m59mIOBO4kfcNCEzJNy71UkeF4XIx2EVmL9KLwDQdmM=
-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.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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.6.1/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.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
 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/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA=
-github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg=
 github.com/txn2/txeh v1.3.0 h1:vnbv63htVMZCaQgLqVBxKvj2+HHHFUzNW7I183zjg3E=
 github.com/txn2/txeh v1.3.0/go.mod h1:O7M6gUTPeMF+vsa4c4Ipx3JDkOYrruB1Wry8QRsMcw8=
 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/v2 v2.4.0/go.mod h1:NX9W0zmTvedE5oDoOMs2RTC8RvdK98NTYZE5LbaEYPg=
-github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY=
-github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
 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/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
 github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g=
 github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -484,12 +325,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
-go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
-go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -498,28 +333,20 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
-go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
-go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
-golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
-golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
+golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
 golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
 golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
 golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
@@ -531,8 +358,6 @@ golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfU
 golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20220601225756-64ec528b34cd h1:9NbNcTg//wfC5JskFW4Z3sqwVnjmJKHxLAol1bW2qgw=
-golang.org/x/image v0.0.0-20220601225756-64ec528b34cd/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -547,8 +372,6 @@ golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPI
 golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
-golang.org/x/mobile v0.0.0-20211207041440-4e6c2922fdee h1:/tShaw8UTf0XzI8DOZwQHzC7d6Vi3EtrBnftiZ4vAvU=
-golang.org/x/mobile v0.0.0-20211207041440-4e6c2922fdee/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
@@ -560,8 +383,6 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -591,13 +412,11 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211111083644-e5c967477495/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -608,8 +427,9 @@ golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su
 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
 golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
+golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -621,7 +441,6 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ
 golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
@@ -646,9 +465,7 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
 golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -658,10 +475,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -683,16 +497,12 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -700,7 +510,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -720,12 +529,11 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
 golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
-golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -734,11 +542,11 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -746,7 +554,6 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@@ -756,7 +563,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
 golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -779,7 +585,6 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
 golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
@@ -788,15 +593,12 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -807,8 +609,8 @@ golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNq
 golang.zx2c4.com/go118/netip v0.0.0-20211111135330-a4a02eeacf9d/go.mod h1:5yyfuiqVIJ7t+3MqrpTQ+QqRkMWiESiyDvPNvKYCecg=
 golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
 golang.zx2c4.com/wireguard v0.0.0-20220202223031-3b95c81cc178/go.mod h1:TjUWrnD5ATh7bFvmm/ALEJZQ4ivKbETb6pmyj1vUoNI=
-golang.zx2c4.com/wireguard v0.0.0-20220318042302-193cf8d6a5d6 h1:kgBK1EGuTIYbwoKROmsoV0FQp08gnCcVa110A4Unqhk=
-golang.zx2c4.com/wireguard v0.0.0-20220318042302-193cf8d6a5d6/go.mod h1:bVQfyl2sCM/QIIGHpWbFGfHPuDvqnCNkT6MQLTCjO/U=
+golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c h1:Okh6a1xpnJslG9Mn84pId1Mn+Q8cvpo4HCeeFWHo0cA=
+golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c/go.mod h1:enML0deDxY1ux+B6ANGiwtg0yAJi1rctkTpcHNAVPyg=
 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220324164955-056925b7df31 h1:AgW3hljgTzuRbCB0j+q9tXT0uy6ij7vMjEzSCeMlQY0=
 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220324164955-056925b7df31/go.mod h1:8P32Ilp1kCpwB4ItaHyvSk4xAtnpQ+8gQVfg5WaO1TU=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
@@ -832,7 +634,6 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q
 google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
 google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
 google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
-google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
 google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
 google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
 google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
@@ -894,9 +695,7 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
@@ -989,27 +788,19 @@ google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175
 google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
-gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
 gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
-honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2 h1:oomkgU6VaQDsV6qZby2uz1Lap0eXmku8+2em3A/l700=
-honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2/go.mod h1:sUMDUKNB2ZcVjt92UnLy3cdGs+wDAcrPdV3JP6sVgA4=
+gortc.io/stun v1.23.0 h1:CpRQFjakCZMwVKTwInKbcCzlBklj62LGzD3NPdFyGrE=
+gortc.io/stun v1.23.0/go.mod h1:XD5lpONVyjvV3BgOyJFNo0iv6R2oZB4L+weMqxts+zg=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

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

@@ -128,7 +128,7 @@ apiVersion: v1
 kind: Service
 metadata:
   labels:
-  name: 'netmaker-wireguard'
+    name: 'netmaker-wireguard'
 spec:
   externalTrafficPolicy: Local
   type: NodePort

+ 15 - 4
logic/extpeers.go

@@ -34,7 +34,7 @@ func GetExtPeersList(node *models.Node) ([]models.ExtPeersResponse, error) {
 			continue
 		}
 
-		if extClient.Enabled && extClient.Network == node.Network && extClient.IngressGatewayID == node.ID {
+		if extClient.Enabled && extClient.Network == node.Network && extClient.IngressGatewayID == node.ID.String() {
 			peers = append(peers, peer)
 		}
 	}
@@ -57,7 +57,7 @@ func GetEgressRangesOnNetwork(client *models.ExtClient) ([]string, error) {
 		if currentNode.Network != client.Network {
 			continue
 		}
-		if currentNode.IsEgressGateway == "yes" { // add the egress gateway range(s) to the result
+		if currentNode.IsEgressGateway { // add the egress gateway range(s) to the result
 			if len(currentNode.EgressGatewayRanges) > 0 {
 				result = append(result, currentNode.EgressGatewayRanges...)
 			}
@@ -137,7 +137,13 @@ func CreateExtClient(extclient *models.ExtClient) error {
 			if err != nil {
 				return err
 			}
-			extclient.Address = newAddress
+			extclient.Address = newAddress.String()
+
+			extclientInternalAddr, err := UniqueAddress(extclient.Network, true)
+			if err != nil {
+				return err
+			}
+			extclient.InternalIPAddr = extclientInternalAddr.String()
 		}
 	}
 
@@ -147,7 +153,12 @@ func CreateExtClient(extclient *models.ExtClient) error {
 			if err != nil {
 				return err
 			}
-			extclient.Address6 = addr6
+			extclient.Address6 = addr6.String()
+			extclientInternalAddr6, err := UniqueAddress6(extclient.Network, true)
+			if err != nil {
+				return err
+			}
+			extclient.InternalIPAddr6 = extclientInternalAddr6.String()
 		}
 	}
 

+ 64 - 64
logic/gateway.go

@@ -15,6 +15,14 @@ import (
 
 // CreateEgressGateway - creates an egress gateway
 func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, error) {
+	node, err := GetNodeByID(gateway.NodeID)
+	if err != nil {
+		return models.Node{}, err
+	}
+	host, err := GetHost(node.HostID.String())
+	if err != nil {
+		return models.Node{}, err
+	}
 	for i, cidr := range gateway.Ranges {
 		normalized, err := NormalizeCIDR(cidr)
 		if err != nil {
@@ -23,14 +31,10 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
 		gateway.Ranges[i] = normalized
 
 	}
-	node, err := GetNodeByID(gateway.NodeID)
-	if err != nil {
-		return models.Node{}, err
-	}
-	if node.OS != "linux" && node.OS != "freebsd" { // add in darwin later
-		return models.Node{}, errors.New(node.OS + " is unsupported for egress gateways")
+	if host.OS != "linux" && host.OS != "freebsd" { // add in darwin later
+		return models.Node{}, errors.New(host.OS + " is unsupported for egress gateways")
 	}
-	if node.OS == "linux" && node.FirewallInUse == models.FIREWALL_NONE {
+	if host.OS == "linux" && host.FirewallInUse == models.FIREWALL_NONE {
 		return models.Node{}, errors.New("firewall is not supported for egress gateways")
 	}
 	if gateway.NatEnabled == "" {
@@ -40,20 +44,17 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
 	if err != nil {
 		return models.Node{}, err
 	}
-	node.IsEgressGateway = "yes"
+	node.IsEgressGateway = true
 	node.EgressGatewayRanges = gateway.Ranges
-	node.EgressGatewayNatEnabled = gateway.NatEnabled
+	node.EgressGatewayNatEnabled = models.ParseBool(gateway.NatEnabled)
 	node.EgressGatewayRequest = gateway // store entire request for use when preserving the egress gateway
 	postUpCmd := ""
 	postDownCmd := ""
 	ipv4, ipv6 := getNetworkProtocols(gateway.Ranges)
-	// no support for ipv6 and ip6tables in netmaker container
-	if node.IsServer == "yes" {
-		ipv6 = false
-	}
-	logger.Log(3, "creating egress gateway firewall in use is '", node.FirewallInUse, "'")
-	if node.OS == "linux" {
-		switch node.FirewallInUse {
+	logger.Log(3, "creating egress gateway firewall in use is '", host.FirewallInUse, "'")
+	iface := models.WIREGUARD_INTERFACE
+	if host.OS == "linux" {
+		switch host.FirewallInUse {
 		case models.FIREWALL_NFTABLES:
 			// nftables only supported on Linux
 			// assumes chains eg FORWARD and postrouting already exist
@@ -62,14 +63,14 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
 			// removing the chain with rules in it would remove all rules in that section (not safe
 			// if there are remaining rules on the host that need to stay).  In practice the chain is removed
 			// when non-empty even though the removal of a non-empty chain should not be possible per nftables wiki.
-			postUpCmd, postDownCmd = firewallNFTCommandsCreateEgress(node.Interface, gateway.Interface, gateway.Ranges, node.EgressGatewayNatEnabled, ipv4, ipv6)
+			postUpCmd, postDownCmd = firewallNFTCommandsCreateEgress(iface, gateway.Interface, gateway.Ranges, node.EgressGatewayNatEnabled, ipv4, ipv6)
 
 		default: // iptables assumed
 			logger.Log(3, "creating egress gateway nftables is not present")
-			postUpCmd, postDownCmd = firewallIPTablesCommandsCreateEgress(node.Interface, gateway.Interface, node.EgressGatewayNatEnabled, ipv4, ipv6)
+			postUpCmd, postDownCmd = firewallIPTablesCommandsCreateEgress(iface, gateway.Interface, node.EgressGatewayNatEnabled, ipv4, ipv6)
 		}
 	}
-	if node.OS == "freebsd" {
+	if host.OS == "freebsd" {
 		// spacing around ; is important for later parsing of postup/postdown in wireguard/common.go
 		postUpCmd = "kldload ipfw ipfw_nat ; "
 		postUpCmd += "ipfw disable one_pass ; "
@@ -108,7 +109,7 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
 	if err != nil {
 		return node, err
 	}
-	if err = database.Insert(node.ID, string(nodeData), database.NODES_TABLE_NAME); err != nil {
+	if err = database.Insert(node.ID.String(), string(nodeData), database.NODES_TABLE_NAME); err != nil {
 		return models.Node{}, err
 	}
 	return node, nil
@@ -131,13 +132,16 @@ func ValidateEgressGateway(gateway models.EgressGatewayRequest) error {
 
 // DeleteEgressGateway - deletes egress from node
 func DeleteEgressGateway(network, nodeid string) (models.Node, error) {
-
 	node, err := GetNodeByID(nodeid)
 	if err != nil {
 		return models.Node{}, err
 	}
+	host, err := GetHost(node.HostID.String())
+	if err != nil {
+		return models.Node{}, err
 
-	node.IsEgressGateway = "no"
+	}
+	node.IsEgressGateway = false
 	node.EgressGatewayRanges = []string{}
 	node.EgressGatewayRequest = models.EgressGatewayRequest{} // remove preserved request as the egress gateway is gone
 	// needed in case we don't preserve a gateway (i.e., no ingress to preserve)
@@ -147,19 +151,20 @@ func DeleteEgressGateway(network, nodeid string) (models.Node, error) {
 	cidrs = append(cidrs, node.IngressGatewayRange)
 	cidrs = append(cidrs, node.IngressGatewayRange6)
 	ipv4, ipv6 := getNetworkProtocols(cidrs)
-	logger.Log(3, "deleting egress gateway firewall in use is '", node.FirewallInUse, "'")
-	if node.IsIngressGateway == "yes" { // check if node is still an ingress gateway before completely deleting postdown/up rules
+	logger.Log(3, "deleting egress gateway firewall in use is '", host.FirewallInUse, "'")
+	if node.IsIngressGateway { // check if node is still an ingress gateway before completely deleting postdown/up rules
 		// still have an ingress gateway so preserve it
-		if node.OS == "linux" {
-			switch node.FirewallInUse {
+		iface := models.WIREGUARD_INTERFACE
+		if host.OS == "linux" {
+			switch host.FirewallInUse {
 			case models.FIREWALL_NFTABLES:
 				// nftables only supported on Linux
 				// assumes chains eg FORWARD and postrouting already exist
 				logger.Log(3, "deleting egress gateway nftables is present")
-				node.PostUp, node.PostDown = firewallNFTCommandsCreateIngress(node.Interface)
+				node.PostUp, node.PostDown = firewallNFTCommandsCreateIngress(iface)
 			default:
 				logger.Log(3, "deleting egress gateway nftables is not present")
-				node.PostUp, node.PostDown = firewallIPTablesCommandsCreateIngress(node.Interface, ipv4, ipv6)
+				node.PostUp, node.PostDown = firewallIPTablesCommandsCreateIngress(iface, ipv4, ipv6)
 			}
 		}
 		// no need to preserve ingress gateway on FreeBSD as ingress is not supported on that OS
@@ -170,7 +175,7 @@ func DeleteEgressGateway(network, nodeid string) (models.Node, error) {
 	if err != nil {
 		return models.Node{}, err
 	}
-	if err = database.Insert(node.ID, string(data), database.NODES_TABLE_NAME); err != nil {
+	if err = database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME); err != nil {
 		return models.Node{}, err
 	}
 	return node, nil
@@ -181,15 +186,14 @@ func CreateIngressGateway(netid string, nodeid string, failover bool) (models.No
 
 	var postUpCmd, postDownCmd string
 	node, err := GetNodeByID(nodeid)
-
 	if err != nil {
 		return models.Node{}, err
 	}
-
-	if node.OS != "linux" { // add in darwin later
-		return models.Node{}, errors.New(node.OS + " is unsupported for ingress gateways")
+	host, err := GetHost(node.HostID.String())
+	if err != nil {
+		return models.Node{}, err
 	}
-	if node.OS == "linux" && node.FirewallInUse == models.FIREWALL_NONE {
+	if host.FirewallInUse == models.FIREWALL_NONE {
 		return models.Node{}, errors.New("firewall is not supported for ingress gateways")
 	}
 
@@ -197,27 +201,24 @@ func CreateIngressGateway(netid string, nodeid string, failover bool) (models.No
 	if err != nil {
 		return models.Node{}, err
 	}
-	node.IsIngressGateway = "yes"
+	node.IsIngressGateway = true
 	cidrs := []string{}
 	cidrs = append(cidrs, network.AddressRange)
 	cidrs = append(cidrs, network.AddressRange6)
 	node.IngressGatewayRange = network.AddressRange
 	node.IngressGatewayRange6 = network.AddressRange6
 	ipv4, ipv6 := getNetworkProtocols(cidrs)
-	// no support for ipv6 and ip6tables in netmaker container
-	if node.IsServer == "yes" {
-		ipv6 = false
-	}
-	logger.Log(3, "creating ingress gateway firewall in use is '", node.FirewallInUse, "'")
-	switch node.FirewallInUse {
+	logger.Log(3, "creating ingress gateway firewall in use is '", host.FirewallInUse, "'")
+	iface := models.WIREGUARD_INTERFACE
+	switch host.FirewallInUse {
 	case models.FIREWALL_NFTABLES:
 		// nftables only supported on Linux
 		// assumes chains eg FORWARD and postrouting already exist
 		logger.Log(3, "creating ingress gateway nftables is present")
-		postUpCmd, postDownCmd = firewallNFTCommandsCreateIngress(node.Interface)
+		postUpCmd, postDownCmd = firewallNFTCommandsCreateIngress(iface)
 	default:
 		logger.Log(3, "creating ingress gateway using nftables is not present")
-		postUpCmd, postDownCmd = firewallIPTablesCommandsCreateIngress(node.Interface, ipv4, ipv6)
+		postUpCmd, postDownCmd = firewallIPTablesCommandsCreateIngress(iface, ipv4, ipv6)
 	}
 
 	if node.PostUp != "" {
@@ -233,15 +234,14 @@ func CreateIngressGateway(netid string, nodeid string, failover bool) (models.No
 	node.SetLastModified()
 	node.PostUp = postUpCmd
 	node.PostDown = postDownCmd
-	node.UDPHolePunch = "no"
 	if failover && servercfg.Is_EE {
-		node.Failover = "yes"
+		node.Failover = true
 	}
 	data, err := json.Marshal(&node)
 	if err != nil {
 		return models.Node{}, err
 	}
-	err = database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
+	err = database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
 	if err != nil {
 		return models.Node{}, err
 	}
@@ -251,34 +251,34 @@ func CreateIngressGateway(netid string, nodeid string, failover bool) (models.No
 
 // DeleteIngressGateway - deletes an ingress gateway
 func DeleteIngressGateway(networkName string, nodeid string) (models.Node, bool, error) {
-
 	node, err := GetNodeByID(nodeid)
 	if err != nil {
 		return models.Node{}, false, err
 	}
-	network, err := GetParentNetwork(networkName)
+	//host, err := GetHost(node.ID.String())
+	//if err != nil {
+	//return models.Node{}, false, err
+	//}
+	//network, err := GetParentNetwork(networkName)
 	if err != nil {
 		return models.Node{}, false, err
 	}
 	// delete ext clients belonging to ingress gateway
-	if err = DeleteGatewayExtClients(node.ID, networkName); err != nil {
+	if err = DeleteGatewayExtClients(node.ID.String(), networkName); err != nil {
 		return models.Node{}, false, err
 	}
 	logger.Log(3, "deleting ingress gateway")
-	wasFailover := node.Failover == "yes"
-	if node.IsServer != "yes" {
-		node.UDPHolePunch = network.DefaultUDPHolePunch
-	}
-	node.LastModified = time.Now().Unix()
-	node.IsIngressGateway = "no"
+	wasFailover := node.Failover
+	node.LastModified = time.Now()
+	node.IsIngressGateway = false
 	node.IngressGatewayRange = ""
-	node.Failover = "no"
+	node.Failover = false
 
 	// default to removing postup and postdown
 	node.PostUp = ""
 	node.PostDown = ""
 
-	logger.Log(3, "deleting ingress gateway firewall in use is '", node.FirewallInUse, "' and isEgressGateway is", node.IsEgressGateway)
+	//logger.Log(3, "deleting ingress gateway firewall in use is '", host.FirewallInUse, "' and isEgressGateway is", node.IsEgressGateway)
 	if node.EgressGatewayRequest.NodeID != "" {
 		_, err := CreateEgressGateway(node.EgressGatewayRequest)
 		if err != nil {
@@ -291,7 +291,7 @@ func DeleteIngressGateway(networkName string, nodeid string) (models.Node, bool,
 	if err != nil {
 		return models.Node{}, false, err
 	}
-	err = database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
+	err = database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
 	if err != nil {
 		return models.Node{}, wasFailover, err
 	}
@@ -335,7 +335,7 @@ func firewallNFTCommandsCreateIngress(networkInterface string) (string, string)
 }
 
 // firewallNFTCommandsCreateEgress - used to centralize firewall command maintenance for creating an egress gateway using the nftables firewall.
-func firewallNFTCommandsCreateEgress(networkInterface string, gatewayInterface string, gatewayranges []string, egressNatEnabled string, ipv4, ipv6 bool) (string, string) {
+func firewallNFTCommandsCreateEgress(networkInterface string, gatewayInterface string, gatewayranges []string, egressNatEnabled bool, ipv4, ipv6 bool) (string, string) {
 	// spacing around ; is important for later parsing of postup/postdown in wireguard/common.go
 	postUp := ""
 	postDown := ""
@@ -351,7 +351,7 @@ func firewallNFTCommandsCreateEgress(networkInterface string, gatewayInterface s
 
 		postDown += "nft flush table filter ; "
 
-		if egressNatEnabled == "yes" {
+		if egressNatEnabled {
 			postUp += "nft add table nat ; "
 			postUp += "nft add chain nat postrouting ; "
 			postUp += "nft add rule ip nat postrouting oifname " + gatewayInterface + " counter masquerade ; "
@@ -368,7 +368,7 @@ func firewallNFTCommandsCreateEgress(networkInterface string, gatewayInterface s
 
 		postDown += "nft flush table ip6 filter ; "
 
-		if egressNatEnabled == "yes" {
+		if egressNatEnabled {
 			postUp += "nft add table ip6 nat ; "
 			postUp += "nft 'add chain ip6 nat prerouting { type nat hook prerouting priority 0 ;}' ; "
 			postUp += "nft 'add chain ip6 nat postrouting { type nat hook postrouting priority 0 ;}' ; "
@@ -411,7 +411,7 @@ func firewallIPTablesCommandsCreateIngress(networkInterface string, ipv4, ipv6 b
 }
 
 // firewallIPTablesCommandsCreateEgress - used to centralize firewall command maintenance for creating an egress gateway using the iptables firewall.
-func firewallIPTablesCommandsCreateEgress(networkInterface string, gatewayInterface string, egressNatEnabled string, ipv4, ipv6 bool) (string, string) {
+func firewallIPTablesCommandsCreateEgress(networkInterface string, gatewayInterface string, egressNatEnabled bool, ipv4, ipv6 bool) (string, string) {
 	// spacing around ; is important for later parsing of postup/postdown in wireguard/common.go
 	postUp := ""
 	postDown := ""
@@ -421,7 +421,7 @@ func firewallIPTablesCommandsCreateEgress(networkInterface string, gatewayInterf
 		postDown += "iptables -D FORWARD -i " + networkInterface + " -j ACCEPT ; "
 		postDown += "iptables -D FORWARD -o " + networkInterface + " -j ACCEPT ; "
 
-		if egressNatEnabled == "yes" {
+		if egressNatEnabled {
 			postUp += "iptables -t nat -A POSTROUTING -o " + gatewayInterface + " -j MASQUERADE ; "
 			postDown += "iptables -t nat -D POSTROUTING -o " + gatewayInterface + " -j MASQUERADE ; "
 		}
@@ -432,7 +432,7 @@ func firewallIPTablesCommandsCreateEgress(networkInterface string, gatewayInterf
 		postDown += "ip6tables -D FORWARD -i " + networkInterface + " -j ACCEPT ; "
 		postDown += "ip6tables -D FORWARD -o " + networkInterface + " -j ACCEPT ; "
 
-		if egressNatEnabled == "yes" {
+		if egressNatEnabled {
 			postUp += "ip6tables -t nat -A POSTROUTING -o " + gatewayInterface + " -j MASQUERADE ; "
 			postDown += "ip6tables -t nat -D POSTROUTING -o " + gatewayInterface + " -j MASQUERADE ; "
 		}

+ 297 - 0
logic/hosts.go

@@ -0,0 +1,297 @@
+package logic
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+
+	"github.com/google/uuid"
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/models"
+	"golang.org/x/crypto/bcrypt"
+)
+
+var (
+	// ErrHostExists error indicating that host exists when trying to create new host
+	ErrHostExists error = errors.New("host already exists")
+	// ErrInvalidHostID
+	ErrInvalidHostID error = errors.New("invalid host id")
+)
+
+// GetAllHosts - returns all hosts in flat list or error
+func GetAllHosts() ([]models.Host, error) {
+	currHostMap, err := GetHostsMap()
+	if err != nil {
+		return nil, err
+	}
+	var currentHosts = []models.Host{}
+	for k := range currHostMap {
+		var h = *currHostMap[k]
+		currentHosts = append(currentHosts, h)
+	}
+
+	return currentHosts, nil
+}
+
+// GetAllHostsAPI - get's all the hosts in an API usable format
+func GetAllHostsAPI(hosts []models.Host) []models.ApiHost {
+	apiHosts := []models.ApiHost{}
+	for i := range hosts {
+		newApiHost := hosts[i].ConvertNMHostToAPI()
+		apiHosts = append(apiHosts, *newApiHost)
+	}
+	return apiHosts[:]
+}
+
+// GetHostsMap - gets all the current hosts on machine in a map
+func GetHostsMap() (map[string]*models.Host, error) {
+	records, err := database.FetchRecords(database.HOSTS_TABLE_NAME)
+	if err != nil && !database.IsEmptyRecord(err) {
+		return nil, err
+	}
+	currHostMap := make(map[string]*models.Host)
+	for k := range records {
+		var h models.Host
+		err = json.Unmarshal([]byte(records[k]), &h)
+		if err != nil {
+			return nil, err
+		}
+		currHostMap[h.ID.String()] = &h
+	}
+
+	return currHostMap, nil
+}
+
+// GetHost - gets a host from db given id
+func GetHost(hostid string) (*models.Host, error) {
+	record, err := database.FetchRecord(database.HOSTS_TABLE_NAME, hostid)
+	if err != nil {
+		return nil, err
+	}
+
+	var h models.Host
+	if err = json.Unmarshal([]byte(record), &h); err != nil {
+		return nil, err
+	}
+
+	return &h, nil
+}
+
+// CreateHost - creates a host if not exist
+func CreateHost(h *models.Host) error {
+	_, err := GetHost(h.ID.String())
+	if (err != nil && !database.IsEmptyRecord(err)) || (err == nil) {
+		return ErrHostExists
+	}
+	//encrypt that password so we never see it
+	hash, err := bcrypt.GenerateFromPassword([]byte(h.HostPass), 5)
+	if err != nil {
+		return err
+	}
+	h.HostPass = string(hash)
+	return UpsertHost(h)
+}
+
+// UpdateHost - updates host data by field
+func UpdateHost(newHost, currentHost *models.Host) {
+	// unchangeable fields via API here
+	newHost.DaemonInstalled = currentHost.DaemonInstalled
+	newHost.OS = currentHost.OS
+	newHost.IPForwarding = currentHost.IPForwarding
+	newHost.HostPass = currentHost.HostPass
+	newHost.MacAddress = currentHost.MacAddress
+	newHost.Debug = currentHost.Debug
+	newHost.Nodes = currentHost.Nodes
+	newHost.PublicKey = currentHost.PublicKey
+	newHost.InternetGateway = currentHost.InternetGateway
+	newHost.TrafficKeyPublic = currentHost.TrafficKeyPublic
+
+	// changeable fields
+	if len(newHost.Version) == 0 {
+		newHost.Version = currentHost.Version
+	}
+
+	if len(newHost.Name) == 0 {
+		newHost.Name = currentHost.Name
+	}
+
+	if newHost.LocalRange.String() != currentHost.LocalRange.String() {
+		newHost.LocalRange = currentHost.LocalRange
+	}
+
+	if newHost.MTU == 0 {
+		newHost.MTU = currentHost.MTU
+	}
+
+	if newHost.ListenPort == 0 {
+		newHost.ListenPort = currentHost.ListenPort
+	}
+
+	if newHost.ProxyListenPort == 0 {
+		newHost.ProxyListenPort = currentHost.ProxyListenPort
+	}
+}
+
+// UpsertHost - upserts into DB a given host model, does not check for existence*
+func UpsertHost(h *models.Host) error {
+	data, err := json.Marshal(h)
+	if err != nil {
+		return err
+	}
+
+	return database.Insert(h.ID.String(), string(data), database.HOSTS_TABLE_NAME)
+}
+
+// RemoveHost - removes a given host from server
+func RemoveHost(h *models.Host) error {
+	if len(h.Nodes) > 0 {
+		for i := range h.Nodes {
+			id := h.Nodes[i]
+			n, err := GetNodeByID(id)
+			if err == nil {
+				if err = DissasociateNodeFromHost(&n, h); err != nil {
+					return err // must remove associated nodes before removing a host
+				}
+			}
+		}
+	}
+	return database.DeleteRecord(database.HOSTS_TABLE_NAME, h.ID.String())
+}
+
+// UpdateHostNetworks - updates a given host's networks
+func UpdateHostNetworks(h *models.Host, server string, nets []string) error {
+	if len(h.Nodes) > 0 {
+		for i := range h.Nodes {
+			n, err := GetNodeByID(h.Nodes[i])
+			if err != nil {
+				return err
+			}
+			// loop through networks and remove need for updating existing networks
+			found := false
+			for j := range nets {
+				if len(nets[j]) > 0 && nets[j] == n.Network {
+					nets[j] = "" // mark as ignore
+					found = true
+				}
+			}
+			if !found { // remove the node/host from that network
+				if err = DissasociateNodeFromHost(&n, h); err != nil {
+					return err
+				}
+			}
+		}
+	} else {
+		h.Nodes = []string{}
+	}
+
+	for i := range nets {
+		// create a node for each non zero network remaining
+		if len(nets[i]) > 0 {
+			newNode := models.Node{}
+			newNode.Server = server
+			newNode.Network = nets[i]
+			if err := AssociateNodeToHost(&newNode, h); err != nil {
+				return err
+			}
+			logger.Log(1, "added new node", newNode.ID.String(), "to host", h.Name)
+		}
+	}
+
+	return nil
+}
+
+// AssociateNodeToHost - associates and creates a node with a given host
+// should be the only way nodes get created as of 0.18
+func AssociateNodeToHost(n *models.Node, h *models.Host) error {
+	if len(h.ID.String()) == 0 || h.ID == uuid.Nil {
+		return ErrInvalidHostID
+	}
+	n.HostID = h.ID
+	err := createNode(n)
+	if err != nil {
+		return err
+	}
+	h.Nodes = append(h.Nodes, n.ID.String())
+	return UpsertHost(h)
+}
+
+// DissasociateNodeFromHost - deletes a node and removes from host nodes
+// should be the only way nodes are deleted as of 0.18
+func DissasociateNodeFromHost(n *models.Node, h *models.Host) error {
+	if len(h.ID.String()) == 0 || h.ID == uuid.Nil {
+		return ErrInvalidHostID
+	}
+	if n.HostID != h.ID { // check if node actually belongs to host
+		return fmt.Errorf("node is not associated with host")
+	}
+	if len(h.Nodes) == 0 {
+		return fmt.Errorf("no nodes present in given host")
+	}
+	index := -1
+	for i := range h.Nodes {
+		if h.Nodes[i] == n.ID.String() {
+			index = i
+			break
+		}
+	}
+	if index < 0 {
+		if len(h.Nodes) == 0 {
+			return fmt.Errorf("node %s, not found in host, %s", n.ID.String(), h.ID.String())
+		}
+	} else {
+		h.Nodes = RemoveStringSlice(h.Nodes, index)
+	}
+	if err := deleteNodeByID(n); err != nil {
+		return err
+	}
+
+	return UpsertHost(h)
+}
+
+// GetDefaultHosts - retrieve all hosts marked as default from DB
+func GetDefaultHosts() []models.Host {
+	defaultHostList := []models.Host{}
+	hosts, err := GetAllHosts()
+	if err != nil {
+		return defaultHostList
+	}
+	for i := range hosts {
+		if hosts[i].IsDefault {
+			defaultHostList = append(defaultHostList, hosts[i])
+		}
+	}
+	return defaultHostList[:]
+}
+
+// AddDefaultHostsToNetwork - adds a node to network for every default host on Netmaker server
+func AddDefaultHostsToNetwork(network, server string) error {
+	// add default hosts to network
+	defaultHosts := GetDefaultHosts()
+	for i := range defaultHosts {
+		newNode := models.Node{}
+		newNode.Network = network
+		newNode.Server = server
+		if err := AssociateNodeToHost(&newNode, &defaultHosts[i]); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// GetHostNetworks - fetches all the networks
+func GetHostNetworks(hostID string) []string {
+	currHost, err := GetHost(hostID)
+	if err != nil {
+		return nil
+	}
+	nets := []string{}
+	for i := range currHost.Nodes {
+		n, err := GetNodeByID(currHost.Nodes[i])
+		if err != nil {
+			return nil
+		}
+		nets = append(nets, n.Network)
+	}
+	return nets
+}

+ 0 - 67
logic/metrics.go

@@ -5,7 +5,6 @@ import (
 
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/wireguard"
 )
 
 // GetMetrics - gets the metrics
@@ -38,69 +37,3 @@ func UpdateMetrics(nodeid string, metrics *models.Metrics) error {
 func DeleteMetrics(nodeid string) error {
 	return database.DeleteRecord(database.METRICS_TABLE_NAME, nodeid)
 }
-
-// CollectServerMetrics - collects metrics for given server node
-func CollectServerMetrics(serverID string, networkNodes []models.Node) *models.Metrics {
-	newServerMetrics := models.Metrics{}
-	newServerMetrics.Connectivity = make(map[string]models.Metric)
-	var serverNode models.Node
-	for i := range networkNodes {
-		currNodeID := networkNodes[i].ID
-		if currNodeID == serverID {
-			serverNode = networkNodes[i]
-			continue
-		}
-		if currMetrics, err := GetMetrics(currNodeID); err == nil {
-			if currMetrics.Connectivity != nil && currMetrics.Connectivity[serverID].Connected {
-				metrics := currMetrics.Connectivity[serverID]
-				metrics.NodeName = networkNodes[i].Name
-				metrics.IsServer = "no"
-				newServerMetrics.Connectivity[currNodeID] = metrics
-			}
-		} else {
-			newServerMetrics.Connectivity[currNodeID] = models.Metric{
-				Connected: false,
-				Latency:   999,
-			}
-		}
-	}
-
-	if serverNode.IsIngressGateway == "yes" {
-		clients, err := GetExtClientsByID(serverID, serverNode.Network)
-		if err == nil {
-			peers, err := wireguard.GetDevicePeers(serverNode.Interface)
-			if err == nil {
-				for i := range clients {
-					for j := range peers {
-						if clients[i].PublicKey == peers[j].PublicKey.String() {
-							if peers[j].ReceiveBytes > 0 &&
-								peers[j].TransmitBytes > 0 {
-								newServerMetrics.Connectivity[clients[i].ClientID] = models.Metric{
-									NodeName:      clients[i].ClientID,
-									TotalTime:     5,
-									Uptime:        5,
-									IsServer:      "no",
-									TotalReceived: peers[j].ReceiveBytes,
-									TotalSent:     peers[j].TransmitBytes,
-									Connected:     true,
-									Latency:       -1, // can not determine latency on server currently
-								}
-							} else {
-								newServerMetrics.Connectivity[clients[i].ClientID] = models.Metric{
-									NodeName:  clients[i].ClientID,
-									TotalTime: 5,
-									Uptime:    0,
-									IsServer:  "no",
-									Connected: false,
-									Latency:   999,
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-
-	return &newServerMetrics
-}

+ 13 - 14
logic/metrics/metrics.go

@@ -1,19 +1,19 @@
 package metrics
 
 import (
-	"runtime"
 	"time"
 
 	"github.com/go-ping/ping"
+	proxy_metrics "github.com/gravitl/netclient/nmproxy/metrics"
+	proxy_models "github.com/gravitl/netclient/nmproxy/models"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/wireguard"
 	"golang.zx2c4.com/wireguard/wgctrl"
 )
 
 // Collect - collects metrics
-func Collect(iface string, peerMap models.PeerMap) (*models.Metrics, error) {
+func Collect(iface, network string, proxy bool, peerMap models.PeerMap) (*models.Metrics, error) {
 	var metrics models.Metrics
 	metrics.Connectivity = make(map[string]models.Metric)
 	var wgclient, err = wgctrl.New()
@@ -22,19 +22,13 @@ func Collect(iface string, peerMap models.PeerMap) (*models.Metrics, error) {
 		return &metrics, err
 	}
 	defer wgclient.Close()
-
-	if runtime.GOOS == "darwin" {
-		iface, err = wireguard.GetRealIface(iface)
-		if err != nil {
-			fillUnconnectedData(&metrics, peerMap)
-			return &metrics, err
-		}
-	}
 	device, err := wgclient.Device(iface)
 	if err != nil {
 		fillUnconnectedData(&metrics, peerMap)
 		return &metrics, err
 	}
+	metrics.ProxyMetrics = make(map[string]proxy_models.Metric)
+
 	// TODO handle freebsd??
 	for i := range device.Peers {
 		currPeer := device.Peers[i]
@@ -88,6 +82,8 @@ func Collect(iface string, peerMap models.PeerMap) (*models.Metrics, error) {
 
 		newMetric.TotalTime = 1
 		metrics.Connectivity[id] = newMetric
+		metrics.ProxyMetrics[id] = proxy_metrics.GetMetric(network, currPeer.PublicKey.String())
+		proxy_metrics.ResetMetricsForPeer(network, currPeer.PublicKey.String())
 	}
 
 	fillUnconnectedData(&metrics, peerMap)
@@ -96,8 +92,11 @@ func Collect(iface string, peerMap models.PeerMap) (*models.Metrics, error) {
 
 // GetExchangedBytesForNode - get exchanged bytes for current node peers
 func GetExchangedBytesForNode(node *models.Node, metrics *models.Metrics) error {
-
-	peers, err := logic.GetPeerUpdate(node)
+	host, err := logic.GetHost(node.HostID.String())
+	if err != nil {
+		return err
+	}
+	peers, err := logic.GetPeerUpdate(node, host)
 	if err != nil {
 		logger.Log(0, "Failed to get peers: ", err.Error())
 		return err
@@ -107,7 +106,7 @@ func GetExchangedBytesForNode(node *models.Node, metrics *models.Metrics) error
 		return err
 	}
 	defer wgclient.Close()
-	device, err := wgclient.Device(node.Interface)
+	device, err := wgclient.Device(models.WIREGUARD_INTERFACE)
 	if err != nil {
 		return err
 	}

+ 37 - 134
logic/networks.go

@@ -5,7 +5,6 @@ import (
 	"errors"
 	"fmt"
 	"net"
-	"os/exec"
 	"strings"
 
 	"github.com/c-robinson/iplib"
@@ -15,7 +14,6 @@ import (
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
 	"github.com/gravitl/netmaker/logic/pro"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/validation"
 )
 
@@ -51,18 +49,6 @@ func DeleteNetwork(network string) error {
 	nodeCount, err := GetNetworkNonServerNodeCount(network)
 	if nodeCount == 0 || database.IsEmptyRecord(err) {
 		// delete server nodes first then db records
-		servers, err := GetSortedNetworkServerNodes(network)
-		if err == nil {
-			for _, s := range servers {
-				if err = DeleteNodeByID(&s, true); err != nil {
-					logger.Log(2, "could not removed server", s.Name, "before deleting network", network)
-				} else {
-					logger.Log(2, "removed server", s.Name, "before deleting network", network)
-				}
-			}
-		} else {
-			logger.Log(1, "could not remove servers before deleting network", network)
-		}
 		if err = pro.RemoveAllNetworkUsers(network); err != nil {
 			logger.Log(0, "failed to remove network users on network delete for network", network, err.Error())
 		}
@@ -139,7 +125,7 @@ func GetNetworkNonServerNodeCount(networkName string) (int, error) {
 		if err = json.Unmarshal([]byte(value), &node); err != nil {
 			return count, err
 		} else {
-			if node.Network == networkName && node.IsServer != "yes" {
+			if node.Network == networkName {
 				count++
 			}
 		}
@@ -178,22 +164,22 @@ func GetNetworkSettings(networkname string) (models.Network, error) {
 }
 
 // UniqueAddress - see if address is unique
-func UniqueAddress(networkName string, reverse bool) (string, error) {
-
+func UniqueAddress(networkName string, reverse bool) (net.IP, error) {
+	add := net.IP{}
 	var network models.Network
 	network, err := GetParentNetwork(networkName)
 	if err != nil {
 		logger.Log(0, "UniqueAddressServer encountered  an error")
-		return "666", err
+		return add, err
 	}
 
 	if network.IsIPv4 == "no" {
-		return "", fmt.Errorf("IPv4 not active on network " + networkName)
+		return add, fmt.Errorf("IPv4 not active on network " + networkName)
 	}
 	//ensure AddressRange is valid
 	if _, _, err := net.ParseCIDR(network.AddressRange); err != nil {
 		logger.Log(0, "UniqueAddress encountered  an error")
-		return "666", err
+		return add, err
 	}
 	net4 := iplib.Net4FromStr(network.AddressRange)
 	newAddrs := net4.FirstAddress()
@@ -205,7 +191,7 @@ func UniqueAddress(networkName string, reverse bool) (string, error) {
 	for {
 		if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, false) &&
 			IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, false) {
-			return newAddrs.String(), nil
+			return newAddrs, nil
 		}
 		if reverse {
 			newAddrs, err = net4.PreviousIP(newAddrs)
@@ -217,7 +203,7 @@ func UniqueAddress(networkName string, reverse bool) (string, error) {
 		}
 	}
 
-	return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", errors.New("ERROR: No unique addresses available. Check network subnet")
+	return add, errors.New("ERROR: No unique addresses available. Check network subnet")
 }
 
 // IsIPUnique - checks if an IP is unique
@@ -236,11 +222,11 @@ func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool {
 			continue
 		}
 		if isIpv6 {
-			if node.Address6 == ip && node.Network == network {
+			if node.Address6.IP.String() == ip && node.Network == network {
 				return false
 			}
 		} else {
-			if node.Address == ip && node.Network == network {
+			if node.Address.IP.String() == ip && node.Network == network {
 				return false
 			}
 		}
@@ -250,21 +236,21 @@ func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool {
 }
 
 // UniqueAddress6 - see if ipv6 address is unique
-func UniqueAddress6(networkName string, reverse bool) (string, error) {
-
+func UniqueAddress6(networkName string, reverse bool) (net.IP, error) {
+	add := net.IP{}
 	var network models.Network
 	network, err := GetParentNetwork(networkName)
 	if err != nil {
 		fmt.Println("Network Not Found")
-		return "", err
+		return add, err
 	}
 	if network.IsIPv6 == "no" {
-		return "", fmt.Errorf("IPv6 not active on network " + networkName)
+		return add, fmt.Errorf("IPv6 not active on network " + networkName)
 	}
 
 	//ensure AddressRange is valid
 	if _, _, err := net.ParseCIDR(network.AddressRange6); err != nil {
-		return "666", err
+		return add, err
 	}
 	net6 := iplib.Net6FromStr(network.AddressRange6)
 
@@ -273,13 +259,13 @@ func UniqueAddress6(networkName string, reverse bool) (string, error) {
 		newAddrs, err = net6.PreviousIP(net6.LastAddress())
 	}
 	if err != nil {
-		return "", err
+		return add, err
 	}
 
 	for {
 		if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, true) &&
 			IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, true) {
-			return newAddrs.String(), nil
+			return newAddrs, nil
 		}
 		if reverse {
 			newAddrs, err = net6.PreviousIP(newAddrs)
@@ -291,23 +277,21 @@ func UniqueAddress6(networkName string, reverse bool) (string, error) {
 		}
 	}
 
-	return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", errors.New("ERROR: No unique IPv6 addresses available. Check network subnet")
+	return add, errors.New("ERROR: No unique IPv6 addresses available. Check network subnet")
 }
 
 // GetLocalIP - gets the local ip
 func GetLocalIP(node models.Node) string {
-
 	var local string
-
 	ifaces, err := net.Interfaces()
 	if err != nil {
 		return local
 	}
-	_, localrange, err := net.ParseCIDR(node.LocalRange)
+	host, err := GetHost(node.HostID.String())
 	if err != nil {
 		return local
 	}
-
+	localrange := host.LocalRange
 	found := false
 	for _, i := range ifaces {
 		if i.Flags&net.FlagUp == 0 {
@@ -327,7 +311,7 @@ func GetLocalIP(node models.Node) string {
 				if !found {
 					ip = v.IP
 					local = ip.String()
-					if node.IsLocal == "yes" {
+					if node.IsLocal {
 						found = localrange.Contains(ip)
 					} else {
 						found = true
@@ -337,7 +321,7 @@ func GetLocalIP(node models.Node) string {
 				if !found {
 					ip = v.IP
 					local = ip.String()
-					if node.IsLocal == "yes" {
+					if node.IsLocal {
 						found = localrange.Contains(ip)
 
 					} else {
@@ -369,53 +353,27 @@ func UpdateNetworkLocalAddresses(networkName string) error {
 			return err
 		}
 		if node.Network == networkName {
-			var ipaddr string
+			var ipaddr net.IP
 			var iperr error
-			if node.IsServer == "yes" {
-				ipaddr, iperr = UniqueAddress(networkName, true)
-			} else {
-				ipaddr, iperr = UniqueAddress(networkName, false)
-			}
+			ipaddr, iperr = UniqueAddress(networkName, false)
 			if iperr != nil {
 				fmt.Println("error in node  address assignment!")
 				return iperr
 			}
 
-			node.Address = ipaddr
+			node.Address.IP = ipaddr
 			newNodeData, err := json.Marshal(&node)
 			if err != nil {
 				logger.Log(1, "error in node  address assignment!")
 				return err
 			}
-			database.Insert(node.ID, string(newNodeData), database.NODES_TABLE_NAME)
+			database.Insert(node.ID.String(), string(newNodeData), database.NODES_TABLE_NAME)
 		}
 	}
 
 	return nil
 }
 
-// UpdateNetworkLocalAddresses - updates network localaddresses
-func UpdateNetworkHolePunching(networkName string, holepunch string) error {
-
-	nodes, err := GetNetworkNodes(networkName)
-	if err != nil {
-		return err
-	}
-
-	for _, node := range nodes {
-		if node.IsServer != "yes" {
-			node.UDPHolePunch = holepunch
-			newNodeData, err := json.Marshal(&node)
-			if err != nil {
-				logger.Log(1, "error in node hole punch assignment")
-				return err
-			}
-			database.Insert(node.ID, string(newNodeData), database.NODES_TABLE_NAME)
-		}
-	}
-	return nil
-}
-
 // RemoveNetworkNodeIPv6Addresses - removes network node IPv6 addresses
 func RemoveNetworkNodeIPv6Addresses(networkName string) error {
 
@@ -433,12 +391,12 @@ func RemoveNetworkNodeIPv6Addresses(networkName string) error {
 			return err
 		}
 		if node.Network == networkName {
-			node.Address6 = ""
+			node.Address6.IP = nil
 			data, err := json.Marshal(&node)
 			if err != nil {
 				return err
 			}
-			database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
+			database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
 		}
 	}
 
@@ -462,24 +420,20 @@ func UpdateNetworkNodeAddresses(networkName string) error {
 			return err
 		}
 		if node.Network == networkName {
-			var ipaddr string
+			var ipaddr net.IP
 			var iperr error
-			if node.IsServer == "yes" {
-				ipaddr, iperr = UniqueAddress(networkName, true)
-			} else {
-				ipaddr, iperr = UniqueAddress(networkName, false)
-			}
+			ipaddr, iperr = UniqueAddress(networkName, false)
 			if iperr != nil {
 				logger.Log(1, "error in node ipv4 address assignment!")
 				return iperr
 			}
 
-			node.Address = ipaddr
+			node.Address.IP = ipaddr
 			data, err := json.Marshal(&node)
 			if err != nil {
 				return err
 			}
-			database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
+			database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
 		}
 	}
 
@@ -503,24 +457,20 @@ func UpdateNetworkNodeAddresses6(networkName string) error {
 			return err
 		}
 		if node.Network == networkName {
-			var ipaddr string
+			var ipaddr net.IP
 			var iperr error
-			if node.IsServer == "yes" {
-				ipaddr, iperr = UniqueAddress6(networkName, true)
-			} else {
-				ipaddr, iperr = UniqueAddress6(networkName, false)
-			}
+			ipaddr, iperr = UniqueAddress6(networkName, false)
 			if iperr != nil {
 				logger.Log(1, "error in node ipv6 address assignment!")
 				return iperr
 			}
 
-			node.Address6 = ipaddr
+			node.Address6.IP = ipaddr
 			data, err := json.Marshal(&node)
 			if err != nil {
 				return err
 			}
-			database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
+			database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
 		}
 	}
 
@@ -702,55 +652,8 @@ func networkNodesUpdateAction(networkName string, action string) error {
 			if err != nil {
 				return err
 			}
-			database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
+			database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
 		}
 	}
 	return nil
 }
-
-func deleteInterface(ifacename string, postdown string) error {
-	var err error
-	if !ncutils.IsKernel() {
-		err = RemoveConf(ifacename, true)
-	} else {
-		ipExec, errN := exec.LookPath("ip")
-		err = errN
-		if err != nil {
-			logger.Log(1, err.Error())
-		}
-		_, err = ncutils.RunCmd(ipExec+" link del "+ifacename, false)
-		if postdown != "" {
-			_, err = ncutils.RunCmd(postdown, false)
-		}
-	}
-	return err
-}
-
-func isInterfacePresent(iface string, address string) (string, bool) {
-	var interfaces []net.Interface
-	var err error
-	interfaces, err = net.Interfaces()
-	if err != nil {
-		logger.Log(0, "ERROR: could not read interfaces")
-		return "", true
-	}
-	for _, currIface := range interfaces {
-		var currAddrs []net.Addr
-		currAddrs, err = currIface.Addrs()
-		if err != nil || len(currAddrs) == 0 {
-			continue
-		}
-		for _, addr := range currAddrs {
-			if strings.Contains(addr.String(), address) && currIface.Name != iface {
-				// logger.Log(2, "found iface", addr.String(), currIface.Name)
-				interfaces = nil
-				currAddrs = nil
-				return currIface.Name, false
-			}
-		}
-		currAddrs = nil
-	}
-	interfaces = nil
-	// logger.Log(2, "failed to find iface", iface)
-	return "", true
-}

+ 248 - 390
logic/nodes.go

@@ -1,10 +1,11 @@
 package logic
 
 import (
+	"context"
 	"encoding/json"
 	"errors"
 	"fmt"
-	"sort"
+	"net"
 	"time"
 
 	validator "github.com/go-playground/validator/v10"
@@ -19,11 +20,16 @@ import (
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/validation"
-	"golang.org/x/crypto/bcrypt"
 )
 
-// RELAY_NODE_ERR - error to return if relay node is unfound
-const RELAY_NODE_ERR = "could not find relay for node"
+const (
+	// RELAY_NODE_ERR - error to return if relay node is unfound
+	RELAY_NODE_ERR = "could not find relay for node"
+	// NodePurgeTime time to wait for node to response to a NODE_DELETE actions
+	NodePurgeTime = time.Second * 10
+	// NodePurgeCheckTime is how often to check nodes for Pending Delete
+	NodePurgeCheckTime = time.Second * 30
+)
 
 // GetNetworkNodes - gets the nodes of a network
 func GetNetworkNodes(network string) ([]models.Node, error) {
@@ -40,92 +46,11 @@ func GetNetworkNodes(network string) ([]models.Node, error) {
 	return nodes, nil
 }
 
-// GetSortedNetworkServerNodes - gets nodes of a network, except sorted by update time
-func GetSortedNetworkServerNodes(network string) ([]models.Node, error) {
-	var nodes []models.Node
-	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
-	if err != nil {
-		if database.IsEmptyRecord(err) {
-			return []models.Node{}, nil
-		}
-		return nodes, err
-	}
-	for _, value := range collection {
-
-		var node models.Node
-		err := json.Unmarshal([]byte(value), &node)
-		if err != nil {
-			continue
-		}
-		if node.Network == network && node.IsServer == "yes" {
-			nodes = append(nodes, node)
-		}
-	}
-	sort.Sort(models.NodesArray(nodes))
-	return nodes, nil
-}
-
-// GetServerNodes - gets the server nodes of a network
-func GetServerNodes(network string) []models.Node {
-	var serverNodes = make([]models.Node, 0)
-	var nodes, err = GetNetworkNodes(network)
-	if err != nil {
-		return serverNodes
-	}
-	for _, node := range nodes {
-		if node.IsServer == "yes" {
-			serverNodes = append(serverNodes, node)
-		}
-	}
-	return serverNodes
-}
-
-// UncordonNode - approves a node to join a network
-func UncordonNode(nodeid string) (models.Node, error) {
-	node, err := GetNodeByID(nodeid)
-	if err != nil {
-		return models.Node{}, err
-	}
-	node.SetLastModified()
-	node.IsPending = "no"
-	data, err := json.Marshal(&node)
-	if err != nil {
-		return node, err
-	}
-
-	err = database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
-	return node, err
-}
-
-// SetIfLeader - gets the peers of a given server node
-func SetPeersIfLeader(node *models.Node) {
-	if IsLeader(node) {
-		setNetworkServerPeers(node)
-	}
-}
-
-// IsLeader - determines if a given server node is a leader
-func IsLeader(node *models.Node) bool {
-	nodes, err := GetSortedNetworkServerNodes(node.Network)
-	if err != nil {
-		logger.Log(0, "ERROR: COULD NOT RETRIEVE SERVER NODES. THIS WILL BREAK HOLE PUNCHING.")
-		return false
-	}
-	for _, n := range nodes {
-		if n.LastModified > time.Now().Add(-1*time.Minute).Unix() {
-			return n.Address == node.Address
-		}
-	}
-	return len(nodes) <= 1 || nodes[1].Address == node.Address
-}
-
-// == DB related functions ==
-
 // UpdateNode - takes a node and updates another node with it's values
 func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
-	if newNode.Address != currentNode.Address {
+	if newNode.Address.String() != currentNode.Address.String() {
 		if network, err := GetParentNetwork(newNode.Network); err == nil {
-			if !IsAddressInCIDR(newNode.Address, network.AddressRange) {
+			if !IsAddressInCIDR(newNode.Address.String(), network.AddressRange) {
 				return fmt.Errorf("invalid address provided; out of network range for node %s", newNode.ID)
 			}
 		}
@@ -133,9 +58,6 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
 	nodeACLDelta := currentNode.DefaultACL != newNode.DefaultACL
 	newNode.Fill(currentNode)
 
-	if currentNode.IsServer == "yes" && !validateServer(currentNode, newNode) {
-		return fmt.Errorf("this operation is not supported on server nodes")
-	}
 	// check for un-settable server values
 	if err := ValidateNode(newNode, true); err != nil {
 		return err
@@ -144,7 +66,7 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
 	if newNode.ID == currentNode.ID {
 		if nodeACLDelta {
 			if err := updateProNodeACLS(newNode); err != nil {
-				logger.Log(1, "failed to apply node level ACLs during creation of node", newNode.ID, "-", err.Error())
+				logger.Log(1, "failed to apply node level ACLs during creation of node", newNode.ID.String(), "-", err.Error())
 				return err
 			}
 		}
@@ -153,36 +75,48 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
 		if data, err := json.Marshal(newNode); err != nil {
 			return err
 		} else {
-			return database.Insert(newNode.ID, string(data), database.NODES_TABLE_NAME)
+			return database.Insert(newNode.ID.String(), string(data), database.NODES_TABLE_NAME)
 		}
 	}
 
-	return fmt.Errorf("failed to update node " + currentNode.ID + ", cannot change ID.")
+	return fmt.Errorf("failed to update node " + currentNode.ID.String() + ", cannot change ID.")
 }
 
-// DeleteNodeByID - deletes a node from database or moves into delete nodes table
-func DeleteNodeByID(node *models.Node, exterminate bool) error {
-	var err error
-	var key = node.ID
-	//delete any ext clients as required
-	if node.IsIngressGateway == "yes" {
-		if err := DeleteGatewayExtClients(node.ID, node.Network); err != nil {
-			logger.Log(0, "failed to deleted ext clients", err.Error())
-		}
-	}
-	if !exterminate {
-		node.Action = models.NODE_DELETE
-		nodedata, err := json.Marshal(&node)
-		if err != nil {
+// DeleteNode - marks node for deletion if called by UI or deletes node if called by node
+func DeleteNode(node *models.Node, purge bool) error {
+	node.Action = models.NODE_DELETE
+	if !purge {
+		newnode := *node
+		newnode.PendingDelete = true
+		if err := UpdateNode(node, &newnode); err != nil {
 			return err
 		}
-		err = database.Insert(key, string(nodedata), database.DELETED_NODES_TABLE_NAME)
-		if err != nil {
-			return err
+		return nil
+	}
+	host, err := GetHost(node.HostID.String())
+	if err != nil {
+		return err
+	}
+	if err := DissasociateNodeFromHost(node, host); err != nil {
+		return err
+	}
+	if servercfg.Is_EE {
+		if err := EnterpriseResetAllPeersFailovers(node.ID.String(), node.Network); err != nil {
+			logger.Log(0, "failed to reset failover lists during node delete for node", host.Name, node.Network)
 		}
-	} else {
-		if err := database.DeleteRecord(database.DELETED_NODES_TABLE_NAME, key); err != nil {
-			logger.Log(2, err.Error())
+	}
+
+	return nil
+}
+
+// deleteNodeByID - deletes a node from database
+func deleteNodeByID(node *models.Node) error {
+	var err error
+	var key = node.ID.String()
+	//delete any ext clients as required
+	if node.IsIngressGateway {
+		if err := DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil {
+			logger.Log(0, "failed to deleted ext clients", err.Error())
 		}
 	}
 	if err = database.DeleteRecord(database.NODES_TABLE_NAME, key); err != nil {
@@ -190,37 +124,30 @@ func DeleteNodeByID(node *models.Node, exterminate bool) error {
 			return err
 		}
 	}
-
 	if servercfg.IsDNSMode() {
 		SetDNS()
 	}
 	if node.OwnerID != "" {
-		err = pro.DissociateNetworkUserNode(node.OwnerID, node.Network, node.ID)
+		err = pro.DissociateNetworkUserNode(node.OwnerID, node.Network, node.ID.String())
 		if err != nil {
-			logger.Log(0, "failed to dissasociate", node.OwnerID, "from node", node.ID, ":", err.Error())
+			logger.Log(0, "failed to dissasociate", node.OwnerID, "from node", node.ID.String(), ":", err.Error())
 		}
 	}
-
-	_, err = nodeacls.RemoveNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID))
+	_, err = nodeacls.RemoveNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()))
 	if err != nil {
 		// ignoring for now, could hit a nil pointer if delete called twice
-		logger.Log(2, "attempted to remove node ACL for node", node.Name, node.ID)
+		logger.Log(2, "attempted to remove node ACL for node", node.ID.String())
 	}
 	// removeZombie <- node.ID
-	if err = DeleteMetrics(node.ID); err != nil {
-		logger.Log(1, "unable to remove metrics from DB for node", node.ID, err.Error())
+	if err = DeleteMetrics(node.ID.String()); err != nil {
+		logger.Log(1, "unable to remove metrics from DB for node", node.ID.String(), err.Error())
 	}
-
-	if node.IsServer == "yes" {
-		return removeLocalServer(node)
-	}
-
 	return nil
 }
 
 // IsNodeIDUnique - checks if node id is unique
 func IsNodeIDUnique(node *models.Node) (bool, error) {
-	_, err := database.FetchRecord(database.NODES_TABLE_NAME, node.ID)
+	_, err := database.FetchRecord(database.NODES_TABLE_NAME, node.ID.String())
 	return database.IsEmptyRecord(err), err
 }
 
@@ -238,18 +165,10 @@ func ValidateNode(node *models.Node, isUpdate bool) error {
 		_, err := GetNetworkByNode(node)
 		return err == nil
 	})
-	_ = v.RegisterValidation("in_charset", func(fl validator.FieldLevel) bool {
-		isgood := node.NameInNodeCharSet()
-		return isgood
-	})
-	_ = v.RegisterValidation("checkyesorno", func(fl validator.FieldLevel) bool {
-		return validation.CheckYesOrNo(fl)
-	})
-	_ = v.RegisterValidation("checkyesornoorunset", func(fl validator.FieldLevel) bool {
-		return validation.CheckYesOrNoOrUnset(fl)
+	_ = v.RegisterValidation("checkyesornoorunset", func(f1 validator.FieldLevel) bool {
+		return validation.CheckYesOrNoOrUnset(f1)
 	})
 	err := v.Struct(node)
-
 	return err
 }
 
@@ -260,118 +179,13 @@ func IsFailoverPresent(network string) bool {
 		return false
 	}
 	for i := range netNodes {
-		if netNodes[i].Failover == "yes" {
+		if netNodes[i].Failover {
 			return true
 		}
 	}
 	return false
 }
 
-// CreateNode - creates a node in database
-func CreateNode(node *models.Node) error {
-
-	//encrypt that password so we never see it
-	hash, err := bcrypt.GenerateFromPassword([]byte(node.Password), 5)
-	if err != nil {
-		return err
-	}
-	//set password to encrypted password
-	node.Password = string(hash)
-	if node.Name == models.NODE_SERVER_NAME {
-		node.IsServer = "yes"
-	}
-	if node.DNSOn == "" {
-		if servercfg.IsDNSMode() {
-			node.DNSOn = "yes"
-		} else {
-			node.DNSOn = "no"
-		}
-	}
-
-	SetNodeDefaults(node)
-
-	defaultACLVal := acls.Allowed
-	parentNetwork, err := GetNetwork(node.Network)
-	if err == nil {
-		if parentNetwork.DefaultACL != "yes" {
-			defaultACLVal = acls.NotAllowed
-		}
-	}
-
-	if node.DefaultACL == "" {
-		node.DefaultACL = "unset"
-	}
-
-	reverse := node.IsServer == "yes"
-	if node.Address == "" {
-		if parentNetwork.IsIPv4 == "yes" {
-			if node.Address, err = UniqueAddress(node.Network, reverse); err != nil {
-				return err
-			}
-		}
-	} else if !IsIPUnique(node.Network, node.Address, database.NODES_TABLE_NAME, false) {
-		return fmt.Errorf("invalid address: ipv4 " + node.Address + " is not unique")
-	}
-
-	if node.Address6 == "" {
-		if parentNetwork.IsIPv6 == "yes" {
-			if node.Address6, err = UniqueAddress6(node.Network, reverse); err != nil {
-				return err
-			}
-		}
-	} else if !IsIPUnique(node.Network, node.Address6, database.NODES_TABLE_NAME, true) {
-		return fmt.Errorf("invalid address: ipv6 " + node.Address6 + " is not unique")
-	}
-
-	node.ID = uuid.NewString()
-
-	//Create a JWT for the node
-	tokenString, _ := CreateJWT(node.ID, node.MacAddress, node.Network)
-	if tokenString == "" {
-		//logic.ReturnErrorResponse(w, r, errorResponse)
-		return err
-	}
-	err = ValidateNode(node, false)
-	if err != nil {
-		return err
-	}
-	CheckZombies(node)
-
-	nodebytes, err := json.Marshal(&node)
-	if err != nil {
-		return err
-	}
-	err = database.Insert(node.ID, string(nodebytes), database.NODES_TABLE_NAME)
-	if err != nil {
-		return err
-	}
-
-	_, err = nodeacls.CreateNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID), defaultACLVal)
-	if err != nil {
-		logger.Log(1, "failed to create node ACL for node,", node.ID, "err:", err.Error())
-		return err
-	}
-
-	if err = updateProNodeACLS(node); err != nil {
-		logger.Log(1, "failed to apply node level ACLs during creation of node", node.ID, "-", err.Error())
-		return err
-	}
-
-	if node.IsPending != "yes" {
-		DecrimentKey(node.Network, node.AccessKey)
-	}
-
-	if err = UpdateMetrics(node.ID, &models.Metrics{Connectivity: make(map[string]models.Metric)}); err != nil {
-		logger.Log(1, "failed to initialize metrics for node", node.Name, node.ID, err.Error())
-	}
-
-	SetNetworkNodesLastModified(node.Network)
-	if servercfg.IsDNSMode() {
-		err = SetDNS()
-	}
-	return err
-}
-
 // GetAllNodes - returns all nodes in the DB
 func GetAllNodes() ([]models.Node, error) {
 	var nodes []models.Node
@@ -396,24 +210,6 @@ func GetAllNodes() ([]models.Node, error) {
 	return nodes, nil
 }
 
-// CheckIsServer - check if a node is the server node
-func CheckIsServer(node *models.Node) bool {
-	nodeData, err := database.FetchRecords(database.NODES_TABLE_NAME)
-	if err != nil && !database.IsEmptyRecord(err) {
-		return false
-	}
-	for _, value := range nodeData {
-		var tmpNode models.Node
-		if err := json.Unmarshal([]byte(value), &tmpNode); err != nil {
-			continue
-		}
-		if tmpNode.Network == node.Network && tmpNode.MacAddress != node.MacAddress {
-			return false
-		}
-	}
-	return true
-}
-
 // GetNetworkByNode - gets the network model from a node
 func GetNetworkByNode(node *models.Node) (models.Network, error) {
 
@@ -431,26 +227,23 @@ func GetNetworkByNode(node *models.Node) (models.Network, error) {
 // SetNodeDefaults - sets the defaults of a node to avoid empty fields
 func SetNodeDefaults(node *models.Node) {
 
-	//TODO: Maybe I should make Network a part of the node struct. Then we can just query the Network object for stuff.
 	parentNetwork, _ := GetNetworkByNode(node)
-	node.NetworkSettings = parentNetwork
-	node.NetworkSettings.AccessKeys = []models.AccessKey{}
-
-	node.ExpirationDateTime = time.Now().Unix() + models.TEN_YEARS_IN_SECONDS
-
-	if node.DefaultACL == "" && node.IsServer != "yes" {
-		node.DefaultACL = parentNetwork.DefaultACL
+	_, cidr, err := net.ParseCIDR(parentNetwork.AddressRange)
+	if err == nil {
+		node.NetworkRange = *cidr
 	}
-
-	if node.ListenPort == 0 {
-		node.ListenPort = parentNetwork.DefaultListenPort
+	_, cidr, err = net.ParseCIDR(parentNetwork.AddressRange6)
+	if err == nil {
+		node.NetworkRange6 = *cidr
 	}
+	node.ExpirationDateTime = time.Now().Add(models.TEN_YEARS_IN_SECONDS)
 
-	if node.Interface == "" {
-		node.Interface = parentNetwork.DefaultInterface
+	if node.DefaultACL == "" {
+		node.DefaultACL = parentNetwork.DefaultACL
 	}
+
 	if node.PersistentKeepalive == 0 {
-		node.PersistentKeepalive = parentNetwork.DefaultKeepalive
+		node.PersistentKeepalive = time.Second * time.Duration(parentNetwork.DefaultKeepalive)
 	}
 	if node.PostUp == "" {
 		postup := parentNetwork.DefaultPostUp
@@ -460,44 +253,41 @@ func SetNodeDefaults(node *models.Node) {
 		postdown := parentNetwork.DefaultPostDown
 		node.PostDown = postdown
 	}
-	if node.IsStatic == "" {
-		node.IsStatic = "no"
-	}
-	if node.UDPHolePunch == "" {
-		node.UDPHolePunch = parentNetwork.DefaultUDPHolePunch
-		if node.UDPHolePunch == "" {
-			node.UDPHolePunch = "yes"
-		}
-	}
 	// == Parent Network settings ==
 
-	if node.MTU == 0 {
-		node.MTU = parentNetwork.DefaultMTU
-	}
 	// == node defaults if not set by parent ==
-	node.SetIPForwardingDefault()
-	node.SetDNSOnDefault()
-	node.SetIsLocalDefault()
-	node.SetLastModified()
-	node.SetDefaultName()
-	node.SetLastCheckIn()
-	node.SetLastPeerUpdate()
-	node.SetDefaultAction()
-	node.SetIsServerDefault()
-	node.SetIsStaticDefault()
-	node.SetDefaultEgressGateway()
-	node.SetDefaultIngressGateway()
-	node.SetDefaulIsPending()
-	node.SetDefaultMTU()
-	node.SetDefaultNFTablesPresent()
-	node.SetDefaultIsRelayed()
-	node.SetDefaultIsRelay()
-	node.SetDefaultIsDocker()
-	node.SetDefaultIsK8S()
-	node.SetDefaultIsHub()
-	node.SetDefaultConnected()
-	node.SetDefaultACL()
-	node.SetDefaultFailover()
+	///TODO ___ REVISIT ------
+	///TODO ___ REVISIT ------
+	///TODO ___ REVISIT ------
+	///TODO ___ REVISIT ------
+	///TODO ___ REVISIT ------
+	//node.SetIPForwardingDefault()
+	//node.SetDNSOnDefault()
+	//node.SetIsLocalDefault()
+	//node.SetLastModified()
+	//node.SetDefaultName()
+	//node.SetLastCheckIn()
+	//node.SetLastPeerUpdate()
+	//node.SetDefaultAction()
+	//node.SetIsServerDefault()
+	//node.SetIsStaticDefault()
+	//node.SetDefaultEgressGateway()
+	//node.SetDefaultIngressGateway()
+	//node.SetDefaulIsPending()
+	//node.SetDefaultMTU()
+	//node.SetDefaultNFTablesPresent()
+	//node.SetDefaultIsRelayed()
+	//node.SetDefaultIsRelay()
+	//node.SetDefaultIsDocker()
+	//node.SetDefaultIsK8S()
+	//node.SetDefaultIsHub()
+	//node.SetDefaultConnected()
+	//node.SetDefaultACL()
+	//node.SetDefaultFailover()
+	///TODO ___ REVISIT ------
+	///TODO ___ REVISIT ------
+	///TODO ___ REVISIT ------
+	///TODO ___ REVISIT ------
 }
 
 // GetRecordKey - get record key
@@ -541,7 +331,7 @@ func GetNodesByAddress(network string, addresses []string) ([]models.Node, error
 		return []models.Node{}, err
 	}
 	for _, node := range allnodes {
-		if node.Network == network && ncutils.StringSliceContains(addresses, node.Address) {
+		if node.Network == network && ncutils.StringSliceContains(addresses, node.Address.String()) {
 			nodes = append(nodes, node)
 		}
 	}
@@ -589,7 +379,7 @@ func GetNodeRelay(network string, relayedNodeAddr string) (models.Node, error) {
 			logger.Log(2, err.Error())
 			continue
 		}
-		if relay.IsRelay == "yes" {
+		if relay.IsRelay {
 			for _, addr := range relay.RelayAddrs {
 				if addr == relayedNodeAddr {
 					return relay, nil
@@ -631,81 +421,9 @@ func GetDeletedNodeByID(uuid string) (models.Node, error) {
 	return node, nil
 }
 
-// GetNetworkServerNodeID - get network server node ID if exists
-func GetNetworkServerLeader(network string) (models.Node, error) {
-	nodes, err := GetSortedNetworkServerNodes(network)
-	if err != nil {
-		return models.Node{}, err
-	}
-	for _, node := range nodes {
-		if IsLeader(&node) {
-			return node, nil
-		}
-	}
-	return models.Node{}, errors.New("could not find server leader")
-}
-
-// GetNetworkServerNodeID - get network server node ID if exists
-func GetNetworkServerLocal(network string) (models.Node, error) {
-	nodes, err := GetSortedNetworkServerNodes(network)
-	if err != nil {
-		return models.Node{}, err
-	}
-	mac := servercfg.GetNodeID()
-	if mac == "" {
-		return models.Node{}, fmt.Errorf("error retrieving local server node: server node ID is unset")
-	}
-	for _, node := range nodes {
-		if mac == node.MacAddress {
-			return node, nil
-		}
-	}
-	return models.Node{}, errors.New("could not find node for local server")
-}
-
-// IsLocalServer - get network server node ID if exists
-func IsLocalServer(node *models.Node) bool {
-	var islocal bool
-	local, err := GetNetworkServerLocal(node.Network)
-	if err != nil {
-		return islocal
-	}
-	return node.ID != "" && local.ID == node.ID
-}
-
-// validateServer - make sure servers dont change port or address
-func validateServer(currentNode, newNode *models.Node) bool {
-	return (newNode.Address == currentNode.Address &&
-		newNode.ListenPort == currentNode.ListenPort &&
-		newNode.IsServer == "yes")
-}
-
-// unsetHub - unset hub on network nodes
-func UnsetHub(networkName string) (*models.Node, error) {
-	var nodesToUpdate models.Node
-	nodes, err := GetNetworkNodes(networkName)
-	if err != nil {
-		return &nodesToUpdate, err
-	}
-
-	for i := range nodes {
-		if nodes[i].IsHub == "yes" {
-			nodes[i].IsHub = "no"
-			nodesToUpdate = nodes[i]
-			newNodeData, err := json.Marshal(&nodes[i])
-			if err != nil {
-				logger.Log(1, "error on node during hub update")
-				return &nodesToUpdate, err
-			}
-			database.Insert(nodes[i].ID, string(newNodeData), database.NODES_TABLE_NAME)
-		}
-	}
-	return &nodesToUpdate, nil
-}
-
 // FindRelay - returns the node that is the relay for a relayed node
 func FindRelay(node *models.Node) *models.Node {
-	if node.IsRelayed == "no" {
+	if !node.IsRelayed {
 		return nil
 	}
 	peers, err := GetNetworkNodes(node.Network)
@@ -713,11 +431,11 @@ func FindRelay(node *models.Node) *models.Node {
 		return nil
 	}
 	for _, peer := range peers {
-		if peer.IsRelay == "no" {
+		if !peer.IsRelay {
 			continue
 		}
 		for _, ip := range peer.RelayAddrs {
-			if ip == node.Address || ip == node.Address6 {
+			if ip == node.Address.IP.String() || ip == node.Address6.IP.String() {
 				return &peer
 			}
 		}
@@ -731,10 +449,10 @@ func findNode(ip string) (*models.Node, error) {
 		return nil, err
 	}
 	for _, node := range nodes {
-		if node.Address == ip {
+		if node.Address.IP.String() == ip {
 			return &node, nil
 		}
-		if node.Address6 == ip {
+		if node.Address6.IP.String() == ip {
 			return &node, nil
 		}
 	}
@@ -749,13 +467,23 @@ func GetNetworkIngresses(network string) ([]models.Node, error) {
 		return []models.Node{}, err
 	}
 	for i := range netNodes {
-		if netNodes[i].IsIngressGateway == "yes" {
+		if netNodes[i].IsIngressGateway {
 			ingresses = append(ingresses, netNodes[i])
 		}
 	}
 	return ingresses, nil
 }
 
+// GetAllNodesAPI - get all nodes for api usage
+func GetAllNodesAPI(nodes []models.Node) []models.ApiNode {
+	apiNodes := []models.ApiNode{}
+	for i := range nodes {
+		newApiNode := nodes[i].ConvertToAPINode()
+		apiNodes = append(apiNodes, *newApiNode)
+	}
+	return apiNodes[:]
+}
+
 // == PRO ==
 
 func updateProNodeACLS(node *models.Node) error {
@@ -770,4 +498,134 @@ func updateProNodeACLS(node *models.Node) error {
 	return nil
 }
 
+func PurgePendingNodes(ctx context.Context) {
+	ticker := time.NewTicker(NodePurgeCheckTime)
+	defer ticker.Stop()
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		case <-ticker.C:
+			nodes, err := GetAllNodes()
+			if err != nil {
+				logger.Log(0, "PurgePendingNodes failed to retrieve nodes", err.Error())
+				continue
+			}
+			for _, node := range nodes {
+				if node.PendingDelete {
+					modified := node.LastModified
+					if time.Since(modified) > NodePurgeTime {
+						if err := DeleteNode(&node, true); err != nil {
+							logger.Log(0, "failed to purge node", node.ID.String(), err.Error())
+						} else {
+							logger.Log(0, "purged node ", node.ID.String())
+						}
+					}
+				}
+			}
+		}
+	}
+}
+
+// createNode - creates a node in database
+func createNode(node *models.Node) error {
+	host, err := GetHost(node.HostID.String())
+	if err != nil {
+		return err
+	}
+
+	if !node.DNSOn {
+		if servercfg.IsDNSMode() {
+			node.DNSOn = true
+		} else {
+			node.DNSOn = false
+		}
+	}
+
+	SetNodeDefaults(node)
+
+	defaultACLVal := acls.Allowed
+	parentNetwork, err := GetNetwork(node.Network)
+	if err == nil {
+		if parentNetwork.DefaultACL != "yes" {
+			defaultACLVal = acls.NotAllowed
+		}
+	}
+
+	if node.DefaultACL == "" {
+		node.DefaultACL = "unset"
+	}
+
+	if node.Address.IP == nil {
+		if parentNetwork.IsIPv4 == "yes" {
+			if node.Address.IP, err = UniqueAddress(node.Network, false); err != nil {
+				return err
+			}
+			_, cidr, err := net.ParseCIDR(parentNetwork.AddressRange)
+			if err != nil {
+				return err
+			}
+			node.Address.Mask = net.CIDRMask(cidr.Mask.Size())
+		}
+	} else if !IsIPUnique(node.Network, node.Address.String(), database.NODES_TABLE_NAME, false) {
+		return fmt.Errorf("invalid address: ipv4 " + node.Address.String() + " is not unique")
+	}
+	if node.Address6.IP == nil {
+		if parentNetwork.IsIPv6 == "yes" {
+			if node.Address6.IP, err = UniqueAddress6(node.Network, false); err != nil {
+				return err
+			}
+			_, cidr, err := net.ParseCIDR(parentNetwork.AddressRange6)
+			if err != nil {
+				return err
+			}
+			node.Address6.Mask = net.CIDRMask(cidr.Mask.Size())
+		}
+	} else if !IsIPUnique(node.Network, node.Address6.String(), database.NODES_TABLE_NAME, true) {
+		return fmt.Errorf("invalid address: ipv6 " + node.Address6.String() + " is not unique")
+	}
+	node.ID = uuid.New()
+	//Create a JWT for the node
+	tokenString, _ := CreateJWT(node.ID.String(), host.MacAddress.String(), node.Network)
+	if tokenString == "" {
+		//logic.ReturnErrorResponse(w, r, errorResponse)
+		return err
+	}
+	err = ValidateNode(node, false)
+	if err != nil {
+		return err
+	}
+	CheckZombies(node, host.MacAddress)
+
+	nodebytes, err := json.Marshal(&node)
+	if err != nil {
+		return err
+	}
+	err = database.Insert(node.ID.String(), string(nodebytes), database.NODES_TABLE_NAME)
+	if err != nil {
+		return err
+	}
+
+	_, err = nodeacls.CreateNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), defaultACLVal)
+	if err != nil {
+		logger.Log(1, "failed to create node ACL for node,", node.ID.String(), "err:", err.Error())
+		return err
+	}
+
+	if err = updateProNodeACLS(node); err != nil {
+		logger.Log(1, "failed to apply node level ACLs during creation of node", node.ID.String(), "-", err.Error())
+		return err
+	}
+
+	if err = UpdateMetrics(node.ID.String(), &models.Metrics{Connectivity: make(map[string]models.Metric)}); err != nil {
+		logger.Log(1, "failed to initialize metrics for node", node.ID.String(), err.Error())
+	}
+
+	SetNetworkNodesLastModified(node.Network)
+	if servercfg.IsDNSMode() {
+		err = SetDNS()
+	}
+	return err
+}
+
 // == END PRO ==

+ 496 - 161
logic/peers.go

@@ -3,6 +3,7 @@ package logic
 import (
 	"errors"
 	"fmt"
+	"log"
 	"net"
 	"sort"
 	"strconv"
@@ -10,6 +11,7 @@ import (
 	"time"
 
 	"github.com/c-robinson/iplib"
+	proxy_models "github.com/gravitl/netclient/nmproxy/models"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
@@ -20,23 +22,285 @@ import (
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 )
 
+// GetPeersforProxy calculates the peers for a proxy
+// TODO ==========================
+// TODO ==========================
+// TODO ==========================
+// TODO ==========================
+// TODO ==========================
+// revisit this logic with new host/node models.
+func GetPeersForProxy(node *models.Node, onlyPeers bool) (proxy_models.ProxyManagerPayload, error) {
+	proxyPayload := proxy_models.ProxyManagerPayload{}
+	var peers []wgtypes.PeerConfig
+	peerConfMap := make(map[string]proxy_models.PeerConf)
+	var err error
+	currentPeers, err := GetNetworkNodes(node.Network)
+	if err != nil {
+		return proxyPayload, err
+	}
+	if !onlyPeers {
+		if node.IsRelayed {
+			relayNode := FindRelay(node)
+			relayHost, err := GetHost(relayNode.HostID.String())
+			if err != nil {
+				return proxyPayload, err
+			}
+			if relayNode != nil {
+				host, err := GetHost(relayNode.HostID.String())
+				if err != nil {
+					logger.Log(0, "error retrieving host for relay node", relayNode.HostID.String(), err.Error())
+				}
+				relayEndpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayHost.EndpointIP, host.LocalListenPort))
+				if err != nil {
+					logger.Log(1, "failed to resolve relay node endpoint: ", err.Error())
+				}
+				proxyPayload.IsRelayed = true
+				proxyPayload.RelayedTo = relayEndpoint
+			} else {
+				logger.Log(0, "couldn't find relay node for:  ", node.ID.String())
+			}
+
+		}
+		if node.IsRelay {
+			host, err := GetHost(node.HostID.String())
+			if err != nil {
+				logger.Log(0, "error retrieving host for relay node", node.ID.String(), err.Error())
+			}
+			relayedNodes, err := GetRelayedNodes(node)
+			if err != nil {
+				logger.Log(1, "failed to relayed nodes: ", node.ID.String(), err.Error())
+				proxyPayload.IsRelay = false
+			} else {
+				relayPeersMap := make(map[string]proxy_models.RelayedConf)
+				for _, relayedNode := range relayedNodes {
+					payload, err := GetPeersForProxy(&relayedNode, true)
+					if err == nil {
+						relayedHost, err := GetHost(relayedNode.HostID.String())
+						if err != nil {
+							logger.Log(0, "error retrieving host for relayNode", relayedNode.ID.String(), err.Error())
+						}
+						relayedEndpoint, udpErr := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayedHost.EndpointIP, host.LocalListenPort))
+						if udpErr == nil {
+							relayPeersMap[host.PublicKey.String()] = proxy_models.RelayedConf{
+								RelayedPeerEndpoint: relayedEndpoint,
+								RelayedPeerPubKey:   relayedHost.PublicKey.String(),
+								Peers:               payload.Peers,
+							}
+						}
+
+					}
+				}
+				proxyPayload.IsRelay = true
+				proxyPayload.RelayedPeerConf = relayPeersMap
+			}
+		}
+
+	}
+
+	for _, peer := range currentPeers {
+		if peer.ID == node.ID {
+			//skip yourself
+			continue
+		}
+		host, err := GetHost(peer.HostID.String())
+		if err != nil {
+			continue
+		}
+		proxyStatus := host.ProxyEnabled
+		listenPort := host.LocalListenPort
+		if proxyStatus {
+			listenPort = host.ProxyListenPort
+			if listenPort == 0 {
+				listenPort = proxy_models.NmProxyPort
+			}
+		} else if listenPort == 0 {
+			listenPort = host.ListenPort
+
+		}
+
+		endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", host.EndpointIP, listenPort))
+		if err != nil {
+			logger.Log(1, "failed to resolve udp addr for node: ", peer.ID.String(), host.EndpointIP.String(), err.Error())
+			continue
+		}
+		allowedips := GetAllowedIPs(node, &peer, nil, false)
+		var keepalive time.Duration
+		if node.PersistentKeepalive != 0 {
+			// set_keepalive
+			keepalive = node.PersistentKeepalive
+		}
+		peers = append(peers, wgtypes.PeerConfig{
+			PublicKey:                   host.PublicKey,
+			Endpoint:                    endpoint,
+			AllowedIPs:                  allowedips,
+			PersistentKeepaliveInterval: &keepalive,
+			ReplaceAllowedIPs:           true,
+		})
+		peerConfMap[host.PublicKey.String()] = proxy_models.PeerConf{
+			Address:          net.ParseIP(peer.PrimaryAddress()),
+			Proxy:            proxyStatus,
+			PublicListenPort: int32(listenPort),
+		}
+
+		if !onlyPeers && peer.IsRelayed {
+			relayNode := FindRelay(&peer)
+			if relayNode != nil {
+				relayHost, err := GetHost(relayNode.HostID.String())
+				if err != nil {
+					logger.Log(0, "error retrieving host for relayNode", relayNode.ID.String(), err.Error())
+					continue
+				}
+				relayTo, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayHost.EndpointIP, relayHost.LocalListenPort))
+				if err == nil {
+					peerConfMap[host.PublicKey.String()] = proxy_models.PeerConf{
+
+						IsRelayed:        true,
+						RelayedTo:        relayTo,
+						Address:          net.ParseIP(peer.PrimaryAddress()),
+						Proxy:            proxyStatus,
+						PublicListenPort: int32(listenPort),
+					}
+				}
+
+			}
+
+		}
+	}
+	if node.IsIngressGateway {
+		var extPeers []wgtypes.PeerConfig
+		extPeers, peerConfMap, err = getExtPeersForProxy(node, peerConfMap)
+		if err == nil {
+			peers = append(peers, extPeers...)
+
+		} else if !database.IsEmptyRecord(err) {
+			logger.Log(1, "error retrieving external clients:", err.Error())
+		}
+	}
+
+	proxyPayload.IsIngress = node.IsIngressGateway
+	addr := node.Address
+	if addr.String() == "" {
+		addr = node.Address6
+	}
+	proxyPayload.WgAddr = addr.String()
+	proxyPayload.Peers = peers
+	proxyPayload.PeerMap = peerConfMap
+	proxyPayload.Network = node.Network
+	//proxyPayload.InterfaceName = node.Interface
+	//hardcode or read from host ??
+	proxyPayload.InterfaceName = models.WIREGUARD_INTERFACE
+
+	return proxyPayload, nil
+}
+
 // GetPeerUpdate - gets a wireguard peer config for each peer of a node
-func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
+func GetPeerUpdate(node *models.Node, host *models.Host) (models.PeerUpdate, error) {
+	log.Println("peer update for node ", node.ID)
+	peerUpdate := models.PeerUpdate{
+		Network:       node.Network,
+		ServerVersion: ncutils.Version,
+		DNS:           getPeerDNS(node.Network),
+	}
+	currentPeers, err := GetNetworkNodes(node.Network)
+	if err != nil {
+		log.Println("no network nodes")
+		return models.PeerUpdate{}, err
+	}
+	for _, peer := range currentPeers {
+		var peerConfig wgtypes.PeerConfig
+		peerHost, err := GetHost(peer.HostID.String())
+		if err != nil {
+			log.Println("no peer host", err)
+			return models.PeerUpdate{}, err
+		}
+		if peer.ID == node.ID {
+			log.Println("peer update, skipping self")
+			//skip yourself
+
+			continue
+		}
+		if !peer.Connected {
+			log.Println("peer update, skipping unconnected node")
+			//skip unconnected nodes
+			continue
+		}
+		if !nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(peer.ID.String())) {
+			log.Println("peer update, skipping node for acl")
+			//skip if not permitted by acl
+			continue
+		}
+		peerConfig.PublicKey = peerHost.PublicKey
+		peerConfig.PersistentKeepaliveInterval = &peer.PersistentKeepalive
+		peerConfig.ReplaceAllowedIPs = true
+		uselocal := false
+		if host.EndpointIP.String() == peerHost.EndpointIP.String() {
+			//peer is on same network
+			// set to localaddress
+			uselocal = true
+			if node.LocalAddress.IP == nil {
+				// use public endpint
+				uselocal = false
+			}
+			if node.LocalAddress.String() == peer.LocalAddress.String() {
+				uselocal = false
+			}
+		}
+		peerConfig.Endpoint = &net.UDPAddr{
+			IP:   peerHost.EndpointIP,
+			Port: peerHost.ListenPort,
+		}
+		if !host.ProxyEnabled && peerHost.ProxyEnabled {
+			peerConfig.Endpoint.Port = peerHost.ProxyListenPort
+		}
+		if uselocal {
+			peerConfig.Endpoint.IP = peer.LocalAddress.IP
+		}
+		allowedips := getNodeAllowedIPs(&peer, node)
+		if peer.IsIngressGateway {
+			for _, entry := range peer.IngressGatewayRange {
+				_, cidr, err := net.ParseCIDR(string(entry))
+				if err == nil {
+					allowedips = append(allowedips, *cidr)
+				}
+			}
+		}
+		if peer.IsRelay {
+			allowedips = append(allowedips, getRelayAllowedIPs(node, &peer)...)
+		}
+		if peer.IsEgressGateway {
+			allowedips = append(allowedips, getEgressIPs(node, &peer)...)
+		}
+		peerConfig.AllowedIPs = allowedips
+		peerUpdate.Peers = append(peerUpdate.Peers, peerConfig)
+	}
+	return peerUpdate, nil
+}
+
+func getRelayAllowedIPs(node, peer *models.Node) []net.IPNet {
+	var allowedips []net.IPNet
+	var allowedip net.IPNet
+	for _, addr := range peer.RelayAddrs {
+		if node.Address.IP.String() == addr {
+			continue
+		}
+		if node.Address6.IP.String() == addr {
+			continue
+		}
+		allowedip.IP = net.ParseIP(addr)
+		allowedips = append(allowedips, allowedip)
+	}
+	return allowedips
+}
+
+// GetPeerUpdateLegacy - gets a wireguard peer config for each peer of a node
+func GetPeerUpdateLegacy(node *models.Node) (models.PeerUpdate, error) {
 	var peerUpdate models.PeerUpdate
 	var peers []wgtypes.PeerConfig
 	var serverNodeAddresses = []models.ServerAddr{}
-	var isP2S bool
-	network, err := GetNetwork(node.Network)
-	if err != nil {
-		return peerUpdate, err
-	} else if network.IsPointToSite == "yes" && node.IsHub != "yes" {
-		isP2S = true
-	}
 	var peerMap = make(models.PeerMap)
-
 	var metrics *models.Metrics
 	if servercfg.Is_EE {
-		metrics, _ = GetMetrics(node.ID)
+		metrics, _ = GetMetrics(node.ID.String())
 	}
 	if metrics == nil {
 		metrics = &models.Metrics{}
@@ -55,8 +319,11 @@ func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
 	if err != nil {
 		return models.PeerUpdate{}, err
 	}
-
-	if node.IsRelayed == "yes" {
+	host, err := GetHost(node.HostID.String())
+	if err != nil {
+		return peerUpdate, err
+	}
+	if node.IsRelayed && !host.ProxyEnabled {
 		return GetPeerUpdateForRelayedNode(node, udppeers)
 	}
 
@@ -64,54 +331,58 @@ func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
 	// #2 Set local address: set_local - could be a LOT BETTER and fix some bugs with additional logic
 	// #3 Set allowedips: set_allowedips
 	for _, peer := range currentPeers {
+		peerHost, err := GetHost(peer.HostID.String())
+		if err != nil {
+			logger.Log(0, "error retrieving host for peer", node.ID.String(), err.Error())
+			return models.PeerUpdate{}, err
+		}
 		if peer.ID == node.ID {
 			//skip yourself
 			continue
 		}
-		// on point to site networks -- get peers regularily if you are the hub --- otherwise the only peer is the hub
-		if node.NetworkSettings.IsPointToSite == "yes" && node.IsHub == "no" && peer.IsHub == "no" {
-			continue
-		}
-		if node.Connected != "yes" {
+		if node.Connected {
 			//skip unconnected nodes
 			continue
 		}
 
 		// if the node is not a server, set the endpoint
-		var setEndpoint = !(node.IsServer == "yes")
+		var setEndpoint = true
 
-		if peer.IsRelayed == "yes" {
-			if !(node.IsRelay == "yes" && ncutils.StringSliceContains(node.RelayAddrs, peer.PrimaryAddress())) {
+		if peer.IsRelayed {
+			if !peerHost.ProxyEnabled && !(node.IsRelay && ncutils.StringSliceContains(node.RelayAddrs, peer.PrimaryAddress())) {
 				//skip -- will be added to relay
 				continue
-			} else if node.IsRelay == "yes" && ncutils.StringSliceContains(node.RelayAddrs, peer.PrimaryAddress()) {
+			} else if node.IsRelay && ncutils.StringSliceContains(node.RelayAddrs, peer.PrimaryAddress()) {
 				// dont set peer endpoint if it's relayed by node
 				setEndpoint = false
 			}
 		}
-		if !nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID), nodeacls.NodeID(peer.ID)) {
+		if !nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(peer.ID.String())) {
 			//skip if not permitted by acl
 			continue
 		}
-		if isP2S && peer.IsHub != "yes" {
+		if len(metrics.FailoverPeers[peer.ID.String()]) > 0 && IsFailoverPresent(node.Network) {
+			logger.Log(2, "peer", peer.ID.String(), peer.PrimaryAddress(), "was found to be in failover peers list for node", node.ID.String(), node.PrimaryAddress())
 			continue
 		}
-		if len(metrics.FailoverPeers[peer.ID]) > 0 && IsFailoverPresent(node.Network) {
-			logger.Log(2, "peer", peer.Name, peer.PrimaryAddress(), "was found to be in failover peers list for node", node.Name, node.PrimaryAddress())
-			continue
+		if err != nil {
+			return models.PeerUpdate{}, err
 		}
-		pubkey, err := wgtypes.ParseKey(peer.PublicKey)
+		host, err := GetHost(node.HostID.String())
 		if err != nil {
+			logger.Log(0, "error retrieving host for node", node.ID.String(), err.Error())
 			return models.PeerUpdate{}, err
 		}
-		if node.Endpoint == peer.Endpoint {
+		if host.EndpointIP.String() == peerHost.EndpointIP.String() {
 			//peer is on same network
 			// set_local
-			if node.LocalAddress != peer.LocalAddress && peer.LocalAddress != "" {
-				peer.Endpoint = peer.LocalAddress
-				if peer.LocalListenPort != 0 {
-					peer.ListenPort = peer.LocalListenPort
+			if node.LocalAddress.String() != peer.LocalAddress.String() && peer.LocalAddress.IP != nil {
+				peerHost.EndpointIP = peer.LocalAddress.IP
+				if peerHost.LocalListenPort != 0 {
+					peerHost.ListenPort = peerHost.LocalListenPort
 				}
+			} else {
+				continue
 			}
 		}
 
@@ -126,39 +397,43 @@ func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
 		if setEndpoint {
 
 			var setUDPPort = false
-			if peer.UDPHolePunch == "yes" && errN == nil && CheckEndpoint(udppeers[peer.PublicKey]) {
-				endpointstring := udppeers[peer.PublicKey]
+			if CheckEndpoint(udppeers[peerHost.PublicKey.String()]) {
+				endpointstring := udppeers[peerHost.PublicKey.String()]
 				endpointarr := strings.Split(endpointstring, ":")
 				if len(endpointarr) == 2 {
 					port, err := strconv.Atoi(endpointarr[1])
 					if err == nil {
 						setUDPPort = true
-						peer.ListenPort = int32(port)
+						peerHost.ListenPort = port
 					}
 				}
 			}
 			// if udp hole punching is on, but udp hole punching did not set it, use the LocalListenPort instead
 			// or, if port is for some reason zero use the LocalListenPort
 			// but only do this if LocalListenPort is not zero
-			if ((peer.UDPHolePunch == "yes" && !setUDPPort) || peer.ListenPort == 0) && peer.LocalListenPort != 0 {
-				peer.ListenPort = peer.LocalListenPort
+			if ((!setUDPPort) || peerHost.ListenPort == 0) && peerHost.LocalListenPort != 0 {
+				peerHost.ListenPort = peerHost.LocalListenPort
 			}
 
-			endpoint := peer.Endpoint + ":" + strconv.FormatInt(int64(peer.ListenPort), 10)
+			endpoint := peerHost.EndpointIP.String() + ":" + strconv.FormatInt(int64(peerHost.ListenPort), 10)
 			address, err = net.ResolveUDPAddr("udp", endpoint)
 			if err != nil {
 				return models.PeerUpdate{}, err
 			}
 		}
-
-		allowedips := GetAllowedIPs(node, &peer, metrics)
+		fetchRelayedIps := true
+		if host.ProxyEnabled {
+			fetchRelayedIps = false
+		}
+		allowedips := GetAllowedIPs(node, &peer, metrics, fetchRelayedIps)
 		var keepalive time.Duration
 		if node.PersistentKeepalive != 0 {
+
 			// set_keepalive
-			keepalive, _ = time.ParseDuration(strconv.FormatInt(int64(node.PersistentKeepalive), 10) + "s")
+			keepalive = node.PersistentKeepalive
 		}
 		var peerData = wgtypes.PeerConfig{
-			PublicKey:                   pubkey,
+			PublicKey:                   peerHost.PublicKey,
 			Endpoint:                    address,
 			ReplaceAllowedIPs:           true,
 			AllowedIPs:                  allowedips,
@@ -166,19 +441,16 @@ func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
 		}
 
 		peers = append(peers, peerData)
-		peerMap[peer.PublicKey] = models.IDandAddr{
-			Name:     peer.Name,
-			ID:       peer.ID,
+		peerMap[peerHost.PublicKey.String()] = models.IDandAddr{
+			Name:     peerHost.Name,
+			ID:       peer.ID.String(),
 			Address:  peer.PrimaryAddress(),
-			IsServer: peer.IsServer,
+			IsServer: "no",
 		}
 
-		if peer.IsServer == "yes" {
-			serverNodeAddresses = append(serverNodeAddresses, models.ServerAddr{IsLeader: IsLeader(&peer), Address: peer.Address})
-		}
 	}
-	if node.IsIngressGateway == "yes" {
-		extPeers, idsAndAddr, err := getExtPeers(node)
+	if node.IsIngressGateway {
+		extPeers, idsAndAddr, err := getExtPeers(node, true)
 		if err == nil {
 			peers = append(peers, extPeers...)
 			for i := range idsAndAddr {
@@ -201,10 +473,14 @@ func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
 	return peerUpdate, nil
 }
 
-func getExtPeers(node *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, error) {
+func getExtPeers(node *models.Node, forIngressNode bool) ([]wgtypes.PeerConfig, []models.IDandAddr, error) {
 	var peers []wgtypes.PeerConfig
 	var idsAndAddr []models.IDandAddr
-	extPeers, err := GetExtPeersList(node)
+	extPeers, err := GetNetworkExtClients(node.Network)
+	if err != nil {
+		return peers, idsAndAddr, err
+	}
+	host, err := GetHost(node.HostID.String())
 	if err != nil {
 		return peers, idsAndAddr, err
 	}
@@ -215,13 +491,13 @@ func getExtPeers(node *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, e
 			continue
 		}
 
-		if node.PublicKey == extPeer.PublicKey {
+		if host.PublicKey.String() == extPeer.PublicKey {
 			continue
 		}
 
 		var allowedips []net.IPNet
 		var peer wgtypes.PeerConfig
-		if extPeer.Address != "" {
+		if forIngressNode && extPeer.Address != "" {
 			var peeraddr = net.IPNet{
 				IP:   net.ParseIP(extPeer.Address),
 				Mask: net.CIDRMask(32, 32),
@@ -231,7 +507,7 @@ func getExtPeers(node *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, e
 			}
 		}
 
-		if extPeer.Address6 != "" {
+		if forIngressNode && extPeer.Address6 != "" {
 			var addr6 = net.IPNet{
 				IP:   net.ParseIP(extPeer.Address6),
 				Mask: net.CIDRMask(128, 128),
@@ -240,12 +516,31 @@ func getExtPeers(node *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, e
 				allowedips = append(allowedips, addr6)
 			}
 		}
+		if !forIngressNode {
+			if extPeer.InternalIPAddr != "" {
+				peerInternalAddr := net.IPNet{
+					IP:   net.ParseIP(extPeer.InternalIPAddr),
+					Mask: net.CIDRMask(32, 32),
+				}
+				if peerInternalAddr.IP != nil && peerInternalAddr.Mask != nil {
+					allowedips = append(allowedips, peerInternalAddr)
+				}
+			}
+			if extPeer.InternalIPAddr6 != "" {
+				peerInternalAddr6 := net.IPNet{
+					IP:   net.ParseIP(extPeer.InternalIPAddr6),
+					Mask: net.CIDRMask(32, 32),
+				}
+				if peerInternalAddr6.IP != nil && peerInternalAddr6.Mask != nil {
+					allowedips = append(allowedips, peerInternalAddr6)
+				}
+			}
+		}
 
 		primaryAddr := extPeer.Address
 		if primaryAddr == "" {
 			primaryAddr = extPeer.Address6
 		}
-
 		peer = wgtypes.PeerConfig{
 			PublicKey:         pubkey,
 			ReplaceAllowedIPs: true,
@@ -257,41 +552,113 @@ func getExtPeers(node *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, e
 			Address: primaryAddr,
 		})
 	}
-	sort.SliceStable(peers[:], func(i, j int) bool {
-		return peers[i].PublicKey.String() < peers[j].PublicKey.String()
-	})
 	return peers, idsAndAddr, nil
+
+}
+
+func getExtPeersForProxy(node *models.Node, proxyPeerConf map[string]proxy_models.PeerConf) ([]wgtypes.PeerConfig, map[string]proxy_models.PeerConf, error) {
+	var peers []wgtypes.PeerConfig
+	host, err := GetHost(node.HostID.String())
+	if err != nil {
+		logger.Log(0, "error retrieving host for node", node.ID.String(), err.Error())
+	}
+
+	extPeers, err := GetNetworkExtClients(node.Network)
+	if err != nil {
+		return peers, proxyPeerConf, err
+	}
+	for _, extPeer := range extPeers {
+		pubkey, err := wgtypes.ParseKey(extPeer.PublicKey)
+		if err != nil {
+			logger.Log(1, "error parsing ext pub key:", err.Error())
+			continue
+		}
+
+		if host.PublicKey.String() == extPeer.PublicKey {
+			continue
+		}
+
+		var allowedips []net.IPNet
+		var peer wgtypes.PeerConfig
+		if extPeer.Address != "" {
+			var peeraddr = net.IPNet{
+				IP:   net.ParseIP(extPeer.Address),
+				Mask: net.CIDRMask(32, 32),
+			}
+			if peeraddr.IP != nil && peeraddr.Mask != nil {
+				allowedips = append(allowedips, peeraddr)
+			}
+		}
+
+		if extPeer.Address6 != "" {
+			var addr6 = net.IPNet{
+				IP:   net.ParseIP(extPeer.Address6),
+				Mask: net.CIDRMask(128, 128),
+			}
+			if addr6.IP != nil && addr6.Mask != nil {
+				allowedips = append(allowedips, addr6)
+			}
+		}
+
+		peer = wgtypes.PeerConfig{
+			PublicKey:         pubkey,
+			ReplaceAllowedIPs: true,
+			AllowedIPs:        allowedips,
+		}
+		extInternalPrimaryAddr := extPeer.InternalIPAddr
+		if extInternalPrimaryAddr == "" {
+			extInternalPrimaryAddr = extPeer.InternalIPAddr6
+		}
+		extConf := proxy_models.PeerConf{
+			IsExtClient:   true,
+			Address:       net.ParseIP(extPeer.Address),
+			ExtInternalIp: net.ParseIP(extInternalPrimaryAddr),
+		}
+		if extPeer.IngressGatewayID == node.ID.String() {
+			extConf.IsAttachedExtClient = true
+		}
+		ingGatewayUdpAddr, err := net.ResolveUDPAddr("udp", extPeer.IngressGatewayEndpoint)
+		if err == nil {
+			extConf.IngressGatewayEndPoint = ingGatewayUdpAddr
+		}
+
+		proxyPeerConf[peer.PublicKey.String()] = extConf
+
+		peers = append(peers, peer)
+	}
+	return peers, proxyPeerConf, nil
+
 }
 
 // GetAllowedIPs - calculates the wireguard allowedip field for a peer of a node based on the peer and node settings
-func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet {
+func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics, fetchRelayedIps bool) []net.IPNet {
 	var allowedips []net.IPNet
 	allowedips = getNodeAllowedIPs(peer, node)
 
 	// handle ingress gateway peers
-	if peer.IsIngressGateway == "yes" {
-		extPeers, _, err := getExtPeers(peer)
+	if peer.IsIngressGateway {
+		extPeers, _, err := getExtPeers(peer, false)
 		if err != nil {
-			logger.Log(2, "could not retrieve ext peers for ", peer.Name, err.Error())
+			logger.Log(2, "could not retrieve ext peers for ", peer.ID.String(), err.Error())
 		}
 		for _, extPeer := range extPeers {
 			allowedips = append(allowedips, extPeer.AllowedIPs...)
 		}
 		// if node is a failover node, add allowed ips from nodes it is handling
-		if peer.Failover == "yes" && metrics.FailoverPeers != nil {
+		if metrics != nil && peer.Failover && metrics.FailoverPeers != nil {
 			// traverse through nodes that need handling
-			logger.Log(3, "peer", peer.Name, "was found to be failover for", node.Name, "checking failover peers...")
+			logger.Log(3, "peer", peer.ID.String(), "was found to be failover for", node.ID.String(), "checking failover peers...")
 			for k := range metrics.FailoverPeers {
 				// if FailoverNode is me for this node, add allowedips
-				if metrics.FailoverPeers[k] == peer.ID {
+				if metrics.FailoverPeers[k] == peer.ID.String() {
 					// get original node so we can traverse the allowed ips
 					nodeToFailover, err := GetNodeByID(k)
 					if err == nil {
-						failoverNodeMetrics, err := GetMetrics(nodeToFailover.ID)
+						failoverNodeMetrics, err := GetMetrics(nodeToFailover.ID.String())
 						if err == nil && failoverNodeMetrics != nil {
 							if len(failoverNodeMetrics.NodeName) > 0 {
 								allowedips = append(allowedips, getNodeAllowedIPs(&nodeToFailover, peer)...)
-								logger.Log(0, "failing over node", nodeToFailover.Name, nodeToFailover.PrimaryAddress(), "to failover node", peer.Name)
+								logger.Log(0, "failing over node", nodeToFailover.ID.String(), nodeToFailover.PrimaryAddress(), "to failover node", peer.ID.String())
 							}
 						}
 					}
@@ -300,7 +667,7 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet
 		}
 	}
 	// handle relay gateway peers
-	if peer.IsRelay == "yes" {
+	if fetchRelayedIps && peer.IsRelay {
 		for _, ip := range peer.RelayAddrs {
 			//find node ID of relayed peer
 			relayedPeer, err := findNode(ip)
@@ -316,7 +683,7 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet
 				continue
 			}
 			//check if acl permits comms
-			if !nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID), nodeacls.NodeID(relayedPeer.ID)) {
+			if !nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(relayedPeer.ID.String())) {
 				continue
 			}
 			if iplib.Version(net.ParseIP(ip)) == 4 {
@@ -338,12 +705,12 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet
 				logger.Log(1, "unable to find node for relayed address", ip, err.Error())
 				continue
 			}
-			if relayedNode.IsEgressGateway == "yes" {
+			if relayedNode.IsEgressGateway {
 				extAllowedIPs := getEgressIPs(node, relayedNode)
 				allowedips = append(allowedips, extAllowedIPs...)
 			}
-			if relayedNode.IsIngressGateway == "yes" {
-				extPeers, _, err := getExtPeers(relayedNode)
+			if relayedNode.IsIngressGateway {
+				extPeers, _, err := getExtPeers(relayedNode, false)
 				if err == nil {
 					for _, extPeer := range extPeers {
 						allowedips = append(allowedips, extPeer.AllowedIPs...)
@@ -360,8 +727,12 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet
 func getPeerDNS(network string) string {
 	var dns string
 	if nodes, err := GetNetworkNodes(network); err == nil {
-		for i := range nodes {
-			dns = dns + fmt.Sprintf("%s %s.%s\n", nodes[i].Address, nodes[i].Name, nodes[i].Network)
+		for i, node := range nodes {
+			host, err := GetHost(node.HostID.String())
+			if err != nil {
+				logger.Log(0, "error retrieving host for node", node.ID.String(), err.Error())
+			}
+			dns = dns + fmt.Sprintf("%s %s.%s\n", nodes[i].Address, host.Name, nodes[i].Network)
 		}
 	}
 
@@ -388,22 +759,20 @@ func GetPeerUpdateForRelayedNode(node *models.Node, udppeers map[string]string)
 	}
 
 	//add relay to lists of allowed ip
-	if relay.Address != "" {
-		relayIP := net.IPNet{
-			IP:   net.ParseIP(relay.Address),
-			Mask: net.CIDRMask(32, 32),
-		}
+	if relay.Address.IP != nil {
+		relayIP := relay.Address
 		allowedips = append(allowedips, relayIP)
 	}
-	if relay.Address6 != "" {
-		relayIP6 := net.IPNet{
-			IP:   net.ParseIP(relay.Address6),
-			Mask: net.CIDRMask(128, 128),
-		}
+	if relay.Address6.IP != nil {
+		relayIP6 := relay.Address6
 		allowedips = append(allowedips, relayIP6)
 	}
 	//get PeerUpdate for relayed node
-	relayPeerUpdate, err := GetPeerUpdate(relay)
+	relayHost, err := GetHost(relay.HostID.String())
+	if err != nil {
+		return models.PeerUpdate{}, err
+	}
+	relayPeerUpdate, err := GetPeerUpdate(relay, relayHost)
 	if err != nil {
 		return models.PeerUpdate{}, err
 	}
@@ -422,19 +791,19 @@ func GetPeerUpdateForRelayedNode(node *models.Node, udppeers map[string]string)
 			logger.Log(0, "failed to find node for ip", allowedips[i].IP.String())
 			continue
 		}
-		if !nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID), nodeacls.NodeID(target.ID)) {
-			logger.Log(0, "deleting node from relayednode per acl", node.Name, target.Name)
+		if !nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(target.ID.String())) {
+			logger.Log(0, "deleting node from relayednode per acl", node.ID.String(), target.ID.String())
 			allowedips = append(allowedips[:i], allowedips[i+1:]...)
 		}
 	}
 	//delete self from allowed ips
 	for i := len(allowedips) - 1; i >= 0; i-- {
-		if allowedips[i].IP.String() == node.Address || allowedips[i].IP.String() == node.Address6 {
+		if allowedips[i].IP.String() == node.Address.IP.String() || allowedips[i].IP.String() == node.Address6.IP.String() {
 			allowedips = append(allowedips[:i], allowedips[i+1:]...)
 		}
 	}
 	//delete egressrange from allowedip if we are egress gateway
-	if node.IsEgressGateway == "yes" {
+	if node.IsEgressGateway {
 		for i := len(allowedips) - 1; i >= 0; i-- {
 			if StringSliceContains(node.EgressGatewayRanges, allowedips[i].String()) {
 				allowedips = append(allowedips[:i], allowedips[i+1:]...)
@@ -442,7 +811,7 @@ func GetPeerUpdateForRelayedNode(node *models.Node, udppeers map[string]string)
 		}
 	}
 	//delete extclients from allowedip if we are ingress gateway
-	if node.IsIngressGateway == "yes" {
+	if node.IsIngressGateway {
 		for i := len(allowedips) - 1; i >= 0; i-- {
 			if strings.Contains(node.IngressGatewayRange, allowedips[i].IP.String()) {
 				allowedips = append(allowedips[:i], allowedips[i+1:]...)
@@ -450,7 +819,7 @@ func GetPeerUpdateForRelayedNode(node *models.Node, udppeers map[string]string)
 		}
 	}
 	//add egress range if relay is egress
-	if relay.IsEgressGateway == "yes" {
+	if relay.IsEgressGateway {
 		var ip *net.IPNet
 		for _, cidr := range relay.EgressGatewayRanges {
 			_, ip, err = net.ParseCIDR(cidr)
@@ -460,31 +829,27 @@ func GetPeerUpdateForRelayedNode(node *models.Node, udppeers map[string]string)
 		}
 		allowedips = append(allowedips, *ip)
 	}
-
-	pubkey, err := wgtypes.ParseKey(relay.PublicKey)
-	if err != nil {
-		return models.PeerUpdate{}, err
-	}
 	var setUDPPort = false
-	if relay.UDPHolePunch == "yes" && CheckEndpoint(udppeers[relay.PublicKey]) {
-		endpointstring := udppeers[relay.PublicKey]
+	var listenPort int
+	if CheckEndpoint(udppeers[relayHost.PublicKey.String()]) {
+		endpointstring := udppeers[relayHost.PublicKey.String()]
 		endpointarr := strings.Split(endpointstring, ":")
 		if len(endpointarr) == 2 {
 			port, err := strconv.Atoi(endpointarr[1])
 			if err == nil {
 				setUDPPort = true
-				relay.ListenPort = int32(port)
+				listenPort = port
 			}
 		}
 	}
 	// if udp hole punching is on, but udp hole punching did not set it, use the LocalListenPort instead
 	// or, if port is for some reason zero use the LocalListenPort
 	// but only do this if LocalListenPort is not zero
-	if ((relay.UDPHolePunch == "yes" && !setUDPPort) || relay.ListenPort == 0) && relay.LocalListenPort != 0 {
-		relay.ListenPort = relay.LocalListenPort
+	if ((!setUDPPort) || relayHost.ListenPort == 0) && relayHost.LocalListenPort != 0 {
+		listenPort = relayHost.LocalListenPort
 	}
 
-	endpoint := relay.Endpoint + ":" + strconv.FormatInt(int64(relay.ListenPort), 10)
+	endpoint := relayHost.EndpointIP.String() + ":" + strconv.FormatInt(int64(listenPort), 10)
 	address, err := net.ResolveUDPAddr("udp", endpoint)
 	if err != nil {
 		return models.PeerUpdate{}, err
@@ -492,26 +857,23 @@ func GetPeerUpdateForRelayedNode(node *models.Node, udppeers map[string]string)
 	var keepalive time.Duration
 	if node.PersistentKeepalive != 0 {
 		// set_keepalive
-		keepalive, _ = time.ParseDuration(strconv.FormatInt(int64(node.PersistentKeepalive), 10) + "s")
+		keepalive = node.PersistentKeepalive
 	}
 	var peerData = wgtypes.PeerConfig{
-		PublicKey:                   pubkey,
+		PublicKey:                   relayHost.PublicKey,
 		Endpoint:                    address,
 		ReplaceAllowedIPs:           true,
 		AllowedIPs:                  allowedips,
 		PersistentKeepaliveInterval: &keepalive,
 	}
 	peers = append(peers, peerData)
-	if relay.IsServer == "yes" {
-		serverNodeAddresses = append(serverNodeAddresses, models.ServerAddr{IsLeader: IsLeader(relay), Address: relay.Address})
-	}
 	//if ingress add extclients
-	if node.IsIngressGateway == "yes" {
-		extPeers, _, err := getExtPeers(node)
+	if node.IsIngressGateway {
+		extPeers, _, err := getExtPeers(node, true)
 		if err == nil {
 			peers = append(peers, extPeers...)
 		} else {
-			logger.Log(2, "could not retrieve ext peers for ", node.Name, err.Error())
+			logger.Log(2, "could not retrieve ext peers for ", node.ID.String(), err.Error())
 		}
 	}
 	peerUpdate.Network = node.Network
@@ -526,6 +888,15 @@ func GetPeerUpdateForRelayedNode(node *models.Node, udppeers map[string]string)
 }
 
 func getEgressIPs(node, peer *models.Node) []net.IPNet {
+	host, err := GetHost(node.HostID.String())
+	if err != nil {
+		logger.Log(0, "error retrieving host for node", node.ID.String(), err.Error())
+	}
+	peerHost, err := GetHost(peer.HostID.String())
+	if err != nil {
+		logger.Log(0, "error retrieving host for peer", peer.ID.String(), err.Error())
+	}
+
 	//check for internet gateway
 	internetGateway := false
 	if slices.Contains(peer.EgressGatewayRanges, "0.0.0.0/0") || slices.Contains(peer.EgressGatewayRanges, "::/0") {
@@ -538,14 +909,14 @@ func getEgressIPs(node, peer *models.Node) []net.IPNet {
 			logger.Log(1, "could not parse gateway IP range. Not adding ", iprange)
 			continue // if can't parse CIDR
 		}
-		nodeEndpointArr := strings.Split(peer.Endpoint, ":")                     // getting the public ip of node
+		nodeEndpointArr := strings.Split(peerHost.EndpointIP.String(), ":")      // getting the public ip of node
 		if ipnet.Contains(net.ParseIP(nodeEndpointArr[0])) && !internetGateway { // ensuring egress gateway range does not contain endpoint of node
-			logger.Log(2, "egress IP range of ", iprange, " overlaps with ", node.Endpoint, ", omitting")
+			logger.Log(2, "egress IP range of ", iprange, " overlaps with ", host.EndpointIP.String(), ", omitting")
 			continue // skip adding egress range if overlaps with node's ip
 		}
 		// TODO: Could put in a lot of great logic to avoid conflicts / bad routes
-		if ipnet.Contains(net.ParseIP(node.LocalAddress)) && !internetGateway { // ensuring egress gateway range does not contain public ip of node
-			logger.Log(2, "egress IP range of ", iprange, " overlaps with ", node.LocalAddress, ", omitting")
+		if ipnet.Contains(net.ParseIP(node.LocalAddress.String())) && !internetGateway { // ensuring egress gateway range does not contain public ip of node
+			logger.Log(2, "egress IP range of ", iprange, " overlaps with ", node.LocalAddress.String(), ", omitting")
 			continue // skip adding egress range if overlaps with node's local ip
 		}
 		if err != nil {
@@ -559,60 +930,24 @@ func getEgressIPs(node, peer *models.Node) []net.IPNet {
 
 func getNodeAllowedIPs(peer, node *models.Node) []net.IPNet {
 	var allowedips = []net.IPNet{}
-
-	if peer.Address != "" {
-		var peeraddr = net.IPNet{
-			IP:   net.ParseIP(peer.Address),
+	if peer.Address.IP != nil {
+		allowed := net.IPNet{
+			IP:   peer.Address.IP,
 			Mask: net.CIDRMask(32, 32),
 		}
-		allowedips = append(allowedips, peeraddr)
+		allowedips = append(allowedips, allowed)
 	}
-
-	if peer.Address6 != "" {
-		var addr6 = net.IPNet{
-			IP:   net.ParseIP(peer.Address6),
+	if peer.Address6.IP != nil {
+		allowed := net.IPNet{
+			IP:   peer.Address6.IP,
 			Mask: net.CIDRMask(128, 128),
 		}
-		allowedips = append(allowedips, addr6)
-	}
-	// handle manually set peers
-	for _, allowedIp := range peer.AllowedIPs {
-
-		// parsing as a CIDR first. If valid CIDR, append
-		if _, ipnet, err := net.ParseCIDR(allowedIp); err == nil {
-			nodeEndpointArr := strings.Split(node.Endpoint, ":")
-			if !ipnet.Contains(net.IP(nodeEndpointArr[0])) && ipnet.IP.String() != peer.Address { // don't need to add an allowed ip that already exists..
-				allowedips = append(allowedips, *ipnet)
-			}
-
-		} else { // parsing as an IP second. If valid IP, check if ipv4 or ipv6, then append
-			if iplib.Version(net.ParseIP(allowedIp)) == 4 && allowedIp != peer.Address {
-				ipnet := net.IPNet{
-					IP:   net.ParseIP(allowedIp),
-					Mask: net.CIDRMask(32, 32),
-				}
-				allowedips = append(allowedips, ipnet)
-			} else if iplib.Version(net.ParseIP(allowedIp)) == 6 && allowedIp != peer.Address6 {
-				ipnet := net.IPNet{
-					IP:   net.ParseIP(allowedIp),
-					Mask: net.CIDRMask(128, 128),
-				}
-				allowedips = append(allowedips, ipnet)
-			}
-		}
+		allowedips = append(allowedips, allowed)
 	}
 	// handle egress gateway peers
-	if peer.IsEgressGateway == "yes" {
+	if peer.IsEgressGateway {
 		//hasGateway = true
 		egressIPs := getEgressIPs(node, peer)
-		// remove internet gateway if server
-		if node.IsServer == "yes" {
-			for i := len(egressIPs) - 1; i >= 0; i-- {
-				if egressIPs[i].String() == "0.0.0.0/0" || egressIPs[i].String() == "::/0" {
-					egressIPs = append(egressIPs[:i], egressIPs[i+1:]...)
-				}
-			}
-		}
 		allowedips = append(allowedips, egressIPs...)
 	}
 	return allowedips

+ 1 - 1
logic/pro/networkuser.go

@@ -183,7 +183,7 @@ func IsUserNodeAllowed(nodes []models.Node, network, userID, nodeID string) bool
 	}
 
 	for i := range nodes {
-		if nodes[i].ID == nodeID {
+		if nodes[i].ID.String() == nodeID {
 			for j := range netUser.Nodes {
 				if netUser.Nodes[j] == nodeID {
 					return true

+ 2 - 2
logic/pro/networkuser_test.go

@@ -18,8 +18,8 @@ func TestNetworkUserLogic(t *testing.T) {
 		NetID:        "skynet",
 		AddressRange: "192.168.0.0/24",
 	}
-	nodes := []models.Node{
-		models.Node{ID: "coolnode"},
+	nodes := []models.LegacyNode{
+		models.LegacyNode{ID: "coolnode"},
 	}
 
 	clients := []models.ExtClient{

+ 2 - 2
logic/pro/proacls/nodes.go

@@ -9,14 +9,14 @@ import (
 // AdjustNodeAcls - adjusts ACLs based on a node's default value
 func AdjustNodeAcls(node *models.Node, networkNodes []models.Node) error {
 	networkID := nodeacls.NetworkID(node.Network)
-	nodeID := nodeacls.NodeID(node.ID)
+	nodeID := nodeacls.NodeID(node.ID.String())
 	currentACLs, err := nodeacls.FetchAllACLs(networkID)
 	if err != nil {
 		return err
 	}
 
 	for i := range networkNodes {
-		currentNodeID := nodeacls.NodeID(networkNodes[i].ID)
+		currentNodeID := nodeacls.NodeID(networkNodes[i].ID.String())
 		if currentNodeID == nodeID {
 			continue
 		}

+ 35 - 18
logic/relay.go

@@ -19,14 +19,18 @@ func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error)
 	if err != nil {
 		return returnnodes, models.Node{}, err
 	}
-	if node.OS != "linux" {
+	host, err := GetHost(node.HostID.String())
+	if err != nil {
+		return returnnodes, models.Node{}, err
+	}
+	if host.OS != "linux" {
 		return returnnodes, models.Node{}, fmt.Errorf("only linux machines can be relay nodes")
 	}
 	err = ValidateRelay(relay)
 	if err != nil {
 		return returnnodes, models.Node{}, err
 	}
-	node.IsRelay = "yes"
+	node.IsRelay = true
 	node.RelayAddrs = relay.RelayAddrs
 
 	node.SetLastModified()
@@ -34,7 +38,7 @@ func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error)
 	if err != nil {
 		return returnnodes, node, err
 	}
-	if err = database.Insert(node.ID, string(nodeData), database.NODES_TABLE_NAME); err != nil {
+	if err = database.Insert(node.ID.String(), string(nodeData), database.NODES_TABLE_NAME); err != nil {
 		return returnnodes, models.Node{}, err
 	}
 	returnnodes, err = SetRelayedNodes(true, node.Network, node.RelayAddrs)
@@ -52,21 +56,34 @@ func SetRelayedNodes(setRelayed bool, networkName string, addrs []string) ([]mod
 		return returnnodes, err
 	}
 	for _, node := range networkNodes {
-		if node.IsServer != "yes" {
-			for _, addr := range addrs {
-				if addr == node.Address || addr == node.Address6 {
-					if setRelayed {
-						node.IsRelayed = "yes"
-					} else {
-						node.IsRelayed = "no"
-					}
-					data, err := json.Marshal(&node)
-					if err != nil {
-						return returnnodes, err
-					}
-					database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
-					returnnodes = append(returnnodes, node)
+		for _, addr := range addrs {
+			if addr == node.Address.IP.String() || addr == node.Address6.IP.String() {
+				if setRelayed {
+					node.IsRelayed = true
+				} else {
+					node.IsRelayed = false
+				}
+				data, err := json.Marshal(&node)
+				if err != nil {
+					return returnnodes, err
 				}
+				database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
+				returnnodes = append(returnnodes, node)
+			}
+		}
+	}
+	return returnnodes, nil
+}
+func GetRelayedNodes(relayNode *models.Node) ([]models.Node, error) {
+	var returnnodes []models.Node
+	networkNodes, err := GetNetworkNodes(relayNode.Network)
+	if err != nil {
+		return returnnodes, err
+	}
+	for _, node := range networkNodes {
+		for _, addr := range relayNode.RelayAddrs {
+			if addr == node.Address.IP.String() || addr == node.Address6.IP.String() {
+				returnnodes = append(returnnodes, node)
 			}
 		}
 	}
@@ -111,7 +128,7 @@ func DeleteRelay(network, nodeid string) ([]models.Node, models.Node, error) {
 		return returnnodes, node, err
 	}
 
-	node.IsRelay = "no"
+	node.IsRelay = false
 	node.RelayAddrs = []string{}
 	node.SetLastModified()
 

+ 0 - 305
logic/server.go

@@ -1,21 +1,9 @@
 package logic
 
 import (
-	"encoding/json"
-	"errors"
-	"fmt"
-	"net"
-	"os"
-	"runtime"
 	"strings"
 
-	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-	"github.com/gravitl/netmaker/netclient/wireguard"
-	"github.com/gravitl/netmaker/servercfg"
-	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 )
 
 // EnterpriseCheckFuncs - can be set to run functions for EE
@@ -38,144 +26,6 @@ const KUBERNETES_LISTEN_PORT = 31821
 // KUBERNETES_SERVER_MTU - ideal mtu for kubernetes deployments right now
 const KUBERNETES_SERVER_MTU = 1024
 
-// ServerJoin - responsible for joining a server to a network
-func ServerJoin(networkSettings *models.Network) (models.Node, error) {
-	var returnNode models.Node
-	if networkSettings == nil || networkSettings.NetID == "" {
-		return returnNode, errors.New("no network provided")
-	}
-
-	var err error
-
-	var currentServers = GetServerNodes(networkSettings.NetID)
-	var serverCount = 1
-	if currentServers != nil {
-		serverCount = len(currentServers) + 1
-	}
-	var ishub = "no"
-
-	if networkSettings.IsPointToSite == "yes" {
-		nodes, err := GetNetworkNodes(networkSettings.NetID)
-		if err != nil || nodes == nil {
-			ishub = "yes"
-		} else {
-			sethub := true
-			for i := range nodes {
-				if nodes[i].IsHub == "yes" {
-					sethub = false
-				}
-			}
-			if sethub {
-				ishub = "yes"
-			}
-		}
-	}
-	var node = &models.Node{
-		IsServer:        "yes",
-		DNSOn:           "no",
-		IsStatic:        "yes",
-		Name:            fmt.Sprintf("%s-%d", models.NODE_SERVER_NAME, serverCount),
-		MacAddress:      servercfg.GetNodeID(),
-		ID:              "", // will be set to new uuid
-		UDPHolePunch:    "no",
-		IsLocal:         networkSettings.IsLocal,
-		LocalRange:      networkSettings.LocalRange,
-		OS:              runtime.GOOS,
-		Version:         servercfg.Version,
-		IsHub:           ishub,
-		NetworkSettings: *networkSettings,
-	}
-
-	SetNodeDefaults(node)
-
-	if servercfg.GetPlatform() == "Kubernetes" {
-		node.ListenPort = KUBERNETES_LISTEN_PORT
-		node.MTU = KUBERNETES_SERVER_MTU
-	}
-
-	if node.LocalRange != "" && node.LocalAddress == "" {
-		logger.Log(1, "local vpn, getting local address from range:", networkSettings.LocalRange)
-		node.LocalAddress, err = getServerLocalIP(networkSettings)
-		if err != nil {
-			node.LocalAddress = ""
-			node.IsLocal = "no"
-		}
-	}
-
-	if node.Endpoint == "" {
-		if node.IsLocal == "yes" && node.LocalAddress != "" {
-			node.Endpoint = node.LocalAddress
-		} else {
-			node.Endpoint, err = servercfg.GetPublicIP()
-		}
-		if err != nil || node.Endpoint == "" {
-			logger.Log(0, "Error setting server node Endpoint.")
-			return returnNode, err
-		}
-	}
-
-	var privateKey = ""
-
-	// Generate and set public/private WireGuard Keys
-	if privateKey == "" {
-		wgPrivatekey, err := wgtypes.GeneratePrivateKey()
-		if err != nil {
-			logger.Log(1, err.Error())
-			return returnNode, err
-		}
-		privateKey = wgPrivatekey.String()
-		node.PublicKey = wgPrivatekey.PublicKey().String()
-	}
-
-	node.Network = networkSettings.NetID
-
-	logger.Log(2, "adding a server instance on network", node.Network)
-	if err != nil {
-		return returnNode, err
-	}
-	err = SetNetworkNodesLastModified(node.Network)
-	if err != nil {
-		return returnNode, err
-	}
-
-	// get free port based on returned default listen port
-	node.ListenPort, err = ncutils.GetFreePort(node.ListenPort)
-	if err != nil {
-		logger.Log(2, "Error retrieving port:", err.Error())
-	} else {
-		logger.Log(1, "Set client port to", fmt.Sprintf("%d", node.ListenPort), "for network", node.Network)
-	}
-
-	// safety check. If returned node from server is local, but not currently configured as local, set to local addr
-	if node.IsLocal == "yes" && node.LocalRange != "" {
-		node.LocalAddress, err = ncutils.GetLocalIP(node.LocalRange)
-		if err != nil {
-			return returnNode, err
-		}
-		node.Endpoint = node.LocalAddress
-	}
-
-	if err = CreateNode(node); err != nil {
-		return returnNode, err
-	}
-	if err = StorePrivKey(node.ID, privateKey); err != nil {
-		return returnNode, err
-	}
-
-	peers, err := GetPeerUpdate(node)
-	if err != nil && !ncutils.IsEmptyRecord(err) {
-		logger.Log(1, "failed to retrieve peers")
-		return returnNode, err
-	}
-
-	err = wireguard.InitWireguard(node, privateKey, peers.Peers)
-	if err != nil {
-		return returnNode, err
-	}
-
-	return *node, nil
-}
-
 // EnterpriseCheck - Runs enterprise functions if presented
 func EnterpriseCheck() {
 	for _, check := range EnterpriseCheckFuncs {
@@ -183,163 +33,8 @@ func EnterpriseCheck() {
 	}
 }
 
-// ServerUpdate - updates the server
-// replaces legacy Checkin code
-func ServerUpdate(serverNode *models.Node, ifaceDelta bool) error {
-	if !IsLocalServer(serverNode) {
-		logger.Log(1, "skipping server update as not the leader")
-		return nil
-	}
-
-	var err = ServerPull(serverNode, ifaceDelta)
-	if isDeleteError(err) {
-		return DeleteNodeByID(serverNode, true)
-	} else if err != nil && !ifaceDelta {
-		err = ServerPull(serverNode, true)
-		if err != nil {
-			return err
-		}
-	}
-
-	actionCompleted := checkNodeActions(serverNode)
-	if actionCompleted == models.NODE_DELETE {
-		return errors.New("node has been removed")
-	}
-
-	return serverPush(serverNode)
-}
-
 // == Private ==
 
 func isDeleteError(err error) bool {
 	return err != nil && strings.Contains(err.Error(), models.NODE_DELETE)
 }
-
-func checkNodeActions(node *models.Node) string {
-	if node.Action == models.NODE_UPDATE_KEY {
-		err := setWGKeyConfig(node)
-		if err != nil {
-			logger.Log(1, "unable to process reset keys request:", err.Error())
-			return ""
-		}
-	}
-	if node.Action == models.NODE_DELETE {
-		err := DeleteNodeByID(node, true)
-		if err != nil {
-			logger.Log(1, "error deleting locally:", err.Error())
-		}
-		return models.NODE_DELETE
-	}
-	return ""
-}
-
-// == Private ==
-
-// ServerPull - performs a server pull
-func ServerPull(serverNode *models.Node, ifaceDelta bool) error {
-	if serverNode.IsServer != "yes" {
-		return fmt.Errorf("attempted pull from non-server node: %s - %s", serverNode.Name, serverNode.ID)
-	}
-
-	var err error
-	if serverNode.IPForwarding == "yes" {
-		if err = setIPForwardingLinux(); err != nil {
-			return err
-		}
-	}
-	serverNode.OS = runtime.GOOS
-
-	if ifaceDelta {
-		// check for interface change
-		// checks if address is in use by another interface
-		var oldIfaceName, isIfacePresent = isInterfacePresent(serverNode.Interface, serverNode.Address)
-		if !isIfacePresent {
-			if err = deleteInterface(oldIfaceName, serverNode.PostDown); err != nil {
-				logger.Log(1, "could not delete old interface", oldIfaceName)
-			}
-			logger.Log(1, "removed old interface", oldIfaceName)
-		}
-		if err = setWGConfig(serverNode, false); err != nil {
-			return err
-		}
-		// handle server side update
-		if err = UpdateNode(serverNode, serverNode); err != nil {
-			return err
-		}
-	} else {
-		if err = setWGConfig(serverNode, true); err != nil {
-			if errors.Is(err, os.ErrNotExist) {
-				return ServerPull(serverNode, true)
-			} else {
-				return err
-			}
-		}
-	}
-
-	return nil
-}
-
-func getServerLocalIP(networkSettings *models.Network) (string, error) {
-
-	var networkCIDR = networkSettings.LocalRange
-	var currentAddresses, _ = net.InterfaceAddrs()
-	var _, currentCIDR, cidrErr = net.ParseCIDR(networkCIDR)
-	if cidrErr != nil {
-		logger.Log(1, "error on server local IP, invalid CIDR provided:", networkCIDR)
-		return "", cidrErr
-	}
-	for _, addr := range currentAddresses {
-		ip, _, err := net.ParseCIDR(addr.String())
-		if err != nil {
-			continue
-		}
-		if currentCIDR.Contains(ip) {
-			logger.Log(1, "found local ip on network,", networkSettings.NetID, ", set to", ip.String())
-			return ip.String(), nil
-		}
-	}
-	return "", errors.New("could not find a local ip for server")
-}
-
-func serverPush(serverNode *models.Node) error {
-	serverNode.OS = runtime.GOOS
-	serverNode.SetLastCheckIn()
-	return UpdateNode(serverNode, serverNode)
-}
-
-// AddServerIDIfNotPresent - add's current server ID to DB if not present
-func AddServerIDIfNotPresent() error {
-	currentNodeID := servercfg.GetNodeID()
-	currentServerIDs := models.ServerIDs{}
-
-	record, err := database.FetchRecord(database.SERVERCONF_TABLE_NAME, server_id_key)
-	if err != nil && !database.IsEmptyRecord(err) {
-		return err
-	} else if err == nil {
-		if err = json.Unmarshal([]byte(record), &currentServerIDs); err != nil {
-			return err
-		}
-	}
-
-	if !StringSliceContains(currentServerIDs.ServerIDs, currentNodeID) {
-		currentServerIDs.ServerIDs = append(currentServerIDs.ServerIDs, currentNodeID)
-		data, err := json.Marshal(&currentServerIDs)
-		if err != nil {
-			return err
-		}
-		return database.Insert(server_id_key, string(data), database.SERVERCONF_TABLE_NAME)
-	}
-
-	return nil
-}
-
-// GetServerCount - fetches server count from DB
-func GetServerCount() int {
-	if record, err := database.FetchRecord(database.SERVERCONF_TABLE_NAME, server_id_key); err == nil {
-		currentServerIDs := models.ServerIDs{}
-		if err = json.Unmarshal([]byte(record), &currentServerIDs); err == nil {
-			return len(currentServerIDs.ServerIDs)
-		}
-	}
-	return 1
-}

+ 6 - 5
logic/telemetry.go

@@ -84,7 +84,7 @@ func fetchTelemetryData() (telemetryData, error) {
 	data.Users = getDBLength(database.USERS_TABLE_NAME)
 	data.Networks = getDBLength(database.NETWORKS_TABLE_NAME)
 	data.Version = servercfg.GetVersion()
-	data.Servers = GetServerCount()
+	//data.Servers = GetServerCount()
 	nodes, err := GetAllNodes()
 	if err == nil {
 		data.Nodes = len(nodes)
@@ -114,7 +114,11 @@ func setTelemetryTimestamp(telRecord *models.Telemetry) error {
 func getClientCount(nodes []models.Node) clientCount {
 	var count clientCount
 	for _, node := range nodes {
-		switch node.OS {
+		host, err := GetHost(node.HostID.String())
+		if err != nil {
+			continue
+		}
+		switch host.OS {
 		case "darwin":
 			count.MacOS += 1
 		case "windows":
@@ -124,9 +128,6 @@ func getClientCount(nodes []models.Node) clientCount {
 		case "freebsd":
 			count.FreeBSD += 1
 		}
-		if !(node.IsServer == "yes") {
-			count.NonServer += 1
-		}
 	}
 	return count
 }

+ 6 - 26
logic/util.go

@@ -16,7 +16,6 @@ import (
 	"github.com/c-robinson/iplib"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/ncutils"
 )
 
@@ -146,31 +145,6 @@ func StringSliceContains(slice []string, item string) bool {
 
 // == private ==
 
-// sets the network server peers of a given node
-func setNetworkServerPeers(serverNode *models.Node) {
-	if currentPeersList, err := getSystemPeers(serverNode); err == nil {
-		if currentPeersList == nil {
-			currentPeersList = make(map[string]string)
-		}
-		if database.SetPeers(currentPeersList, serverNode.Network) {
-			logger.Log(1, "set new peers on network", serverNode.Network)
-		}
-	} else {
-		logger.Log(1, "could not set peers on network", serverNode.Network, ":", err.Error())
-	}
-}
-
-// ShouldPublishPeerPorts - Gets ports from iface, sets, and returns true if they are different
-func ShouldPublishPeerPorts(serverNode *models.Node) bool {
-	if currentPeersList, err := getSystemPeers(serverNode); err == nil {
-		if database.SetPeers(currentPeersList, serverNode.Network) {
-			logger.Log(1, "set new peers on network", serverNode.Network)
-			return true
-		}
-	}
-	return false
-}
-
 // NormalCIDR - returns the first address of CIDR
 func NormalizeCIDR(address string) (string, error) {
 	ip, IPNet, err := net.ParseCIDR(address)
@@ -226,3 +200,9 @@ func CheckIfFileExists(filePath string) bool {
 	}
 	return true
 }
+
+// RemoveStringSlice - removes an element at given index i
+// from a given string slice
+func RemoveStringSlice(slice []string, i int) []string {
+	return append(slice[:i], slice[i+1:]...)
+}

+ 31 - 0
logic/version.go

@@ -0,0 +1,31 @@
+package logic
+
+import (
+	"strings"
+	"unicode"
+
+	"github.com/hashicorp/go-version"
+)
+
+const MinVersion = "v0.17.0"
+
+// IsVersionCompatible checks that the version passed is compabtible (>=) with MinVersion
+func IsVersionComptatible(ver string) bool {
+	// during dev, assume developers know what they are doing
+	if ver == "dev" {
+		return true
+	}
+	trimmed := strings.TrimFunc(ver, func(r rune) bool {
+		return !unicode.IsNumber(r)
+	})
+	v, err := version.NewVersion(trimmed)
+	if err != nil {
+		return false
+	}
+	constraint, err := version.NewConstraint(">= " + MinVersion)
+	if err != nil {
+		return false
+	}
+	return constraint.Check(v)
+
+}

+ 35 - 0
logic/version_test.go

@@ -0,0 +1,35 @@
+package logic
+
+import (
+	"testing"
+
+	"github.com/matryer/is"
+)
+
+func TestVersion(t *testing.T) {
+	t.Run("valid version", func(t *testing.T) {
+		is := is.New(t)
+		valid := IsVersionComptatible("v0.17.1-testing")
+		is.Equal(valid, true)
+	})
+	t.Run("dev version", func(t *testing.T) {
+		is := is.New(t)
+		valid := IsVersionComptatible("dev")
+		is.Equal(valid, true)
+	})
+	t.Run("invalid version", func(t *testing.T) {
+		is := is.New(t)
+		valid := IsVersionComptatible("v0.14.2-refactor")
+		is.Equal(valid, false)
+	})
+	t.Run("no version", func(t *testing.T) {
+		is := is.New(t)
+		valid := IsVersionComptatible("testing")
+		is.Equal(valid, false)
+	})
+	t.Run("incomplete version", func(t *testing.T) {
+		is := is.New(t)
+		valid := IsVersionComptatible("0.18")
+		is.Equal(valid, true)
+	})
+}

+ 5 - 176
logic/wireguard.go

@@ -1,70 +1,24 @@
 package logic
 
 import (
-	"os"
-	"os/exec"
-	"strings"
-
-	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-	"github.com/gravitl/netmaker/netclient/wireguard"
-	"golang.zx2c4.com/wireguard/wgctrl"
-	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 )
 
-// RemoveConf - removes a configuration for a given WireGuard interface
-func RemoveConf(iface string, printlog bool) error {
-	var err error
-	confPath := ncutils.GetNetclientPathSpecific() + iface + ".conf"
-	err = removeWGQuickConf(confPath, printlog)
-	return err
-}
-
-// HasPeerConnected - checks if a client node has connected over WG
-func HasPeerConnected(node *models.Node) bool {
-	client, err := wgctrl.New()
-	if err != nil {
-		return false
-	}
-	defer client.Close()
-	device, err := client.Device(node.Interface)
-	if err != nil {
-		return false
-	}
-	for _, peer := range device.Peers {
-		if peer.PublicKey.String() == node.PublicKey {
-			if peer.Endpoint != nil {
-				return true
-			}
-		}
-	}
-	return false
-}
-
 // IfaceDelta - checks if the new node causes an interface change
 func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool {
 	// single comparison statements
-	if newNode.Endpoint != currentNode.Endpoint ||
-		newNode.PublicKey != currentNode.PublicKey ||
-		newNode.Address != currentNode.Address ||
-		newNode.Address6 != currentNode.Address6 ||
+	if newNode.Address.String() != currentNode.Address.String() ||
+		newNode.Address6.String() != currentNode.Address6.String() ||
 		newNode.IsEgressGateway != currentNode.IsEgressGateway ||
 		newNode.IsIngressGateway != currentNode.IsIngressGateway ||
 		newNode.IsRelay != currentNode.IsRelay ||
-		newNode.UDPHolePunch != currentNode.UDPHolePunch ||
-		newNode.IsPending != currentNode.IsPending ||
-		newNode.ListenPort != currentNode.ListenPort ||
-		newNode.MTU != currentNode.MTU ||
 		newNode.PersistentKeepalive != currentNode.PersistentKeepalive ||
 		newNode.DNSOn != currentNode.DNSOn ||
-		newNode.Connected != currentNode.Connected ||
-		len(newNode.AllowedIPs) != len(currentNode.AllowedIPs) {
+		newNode.Connected != currentNode.Connected {
 		return true
 	}
-
 	// multi-comparison statements
-	if newNode.IsEgressGateway == "yes" {
+	if newNode.IsEgressGateway {
 		if len(currentNode.EgressGatewayRanges) != len(newNode.EgressGatewayRanges) {
 			return true
 		}
@@ -74,8 +28,7 @@ func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool {
 			}
 		}
 	}
-
-	if newNode.IsRelay == "yes" {
+	if newNode.IsRelay {
 		if len(currentNode.RelayAddrs) != len(newNode.RelayAddrs) {
 			return true
 		}
@@ -85,131 +38,7 @@ func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool {
 			}
 		}
 	}
-
-	for _, address := range newNode.AllowedIPs {
-		if !StringSliceContains(currentNode.AllowedIPs, address) {
-			return true
-		}
-	}
 	return false
 }
 
 // == Private Functions ==
-
-// gets the server peers locally
-func getSystemPeers(node *models.Node) (map[string]string, error) {
-	peers := make(map[string]string)
-
-	client, err := wgctrl.New()
-	if err != nil {
-		return peers, err
-	}
-	defer client.Close()
-	device, err := client.Device(node.Interface)
-	if err != nil {
-		return nil, err
-	}
-	if device.Peers != nil && len(device.Peers) > 0 {
-		for _, peer := range device.Peers {
-			if IsBase64(peer.PublicKey.String()) && peer.Endpoint != nil && CheckEndpoint(peer.Endpoint.String()) {
-				peers[peer.PublicKey.String()] = peer.Endpoint.String()
-			}
-		}
-	}
-	return peers, nil
-}
-func removeWGQuickConf(confPath string, printlog bool) error {
-	if _, err := ncutils.RunCmd("wg-quick down "+confPath, printlog); err != nil {
-		return err
-	}
-	return nil
-}
-
-func setWGConfig(node *models.Node, peerupdate bool) error {
-	peers, err := GetPeerUpdate(node)
-	if err != nil {
-		return err
-	}
-	privkey, err := FetchPrivKey(node.ID)
-	if err != nil {
-		return err
-	}
-	if peerupdate {
-		if err := wireguard.SetPeers(node.Interface, node, peers.Peers); err != nil {
-			logger.Log(0, "error updating peers", err.Error())
-		}
-		logger.Log(2, "updated peers on server", node.Name)
-	} else {
-		err = wireguard.InitWireguard(node, privkey, peers.Peers)
-		logger.Log(3, "finished setting wg config on server", node.Name)
-	}
-	return err
-}
-
-func setWGKeyConfig(node *models.Node) error {
-
-	privatekey, err := wgtypes.GeneratePrivateKey()
-	if err != nil {
-		return err
-	}
-	privkeystring := privatekey.String()
-	publickey := privatekey.PublicKey()
-	node.PublicKey = publickey.String()
-
-	err = StorePrivKey(node.ID, privkeystring)
-	if err != nil {
-		return err
-	}
-	if node.Action == models.NODE_UPDATE_KEY {
-		node.Action = models.NODE_NOOP
-	}
-
-	return setWGConfig(node, false)
-}
-
-func removeLocalServer(node *models.Node) error {
-
-	var err error
-	var ifacename = node.Interface
-	if err = RemovePrivKey(node.ID); err != nil {
-		logger.Log(1, "failed to remove server conf from db", node.ID)
-	}
-	if ifacename != "" {
-		if !ncutils.IsKernel() {
-			if err = RemoveConf(ifacename, true); err == nil {
-				logger.Log(1, "removed WireGuard interface:", ifacename)
-			}
-		} else {
-			ipExec, err := exec.LookPath("ip")
-			if err != nil {
-				return err
-			}
-			out, err := ncutils.RunCmd(ipExec+" link del "+ifacename, false)
-			dontprint := strings.Contains(out, "does not exist") || strings.Contains(out, "Cannot find device")
-			if err != nil && !dontprint {
-				logger.Log(1, "error running command:", ipExec, "link del", ifacename)
-				logger.Log(1, out)
-			}
-			if node.PostDown != "" {
-				ncutils.RunCmd(node.PostDown, false)
-			}
-		}
-	}
-	home := ncutils.GetNetclientPathSpecific()
-	if ncutils.FileExists(home + "netconfig-" + node.Network) {
-		_ = os.Remove(home + "netconfig-" + node.Network)
-	}
-	if ncutils.FileExists(home + "nettoken-" + node.Network) {
-		_ = os.Remove(home + "nettoken-" + node.Network)
-	}
-	if ncutils.FileExists(home + "secret-" + node.Network) {
-		_ = os.Remove(home + "secret-" + node.Network)
-	}
-	if ncutils.FileExists(home + "wgkey-" + node.Network) {
-		_ = os.Remove(home + "wgkey-" + node.Network)
-	}
-	if ncutils.FileExists(home + "nm-" + node.Network + ".conf") {
-		_ = os.Remove(home + "nm-" + node.Network + ".conf")
-	}
-	return err
-}

+ 25 - 19
logic/zombie.go

@@ -2,8 +2,10 @@ package logic
 
 import (
 	"context"
+	"net"
 	"time"
 
+	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
 )
@@ -16,22 +18,26 @@ const (
 )
 
 var (
-	zombies      []string
-	removeZombie chan string = make(chan (string), 10)
-	newZombie    chan string = make(chan (string), 10)
+	zombies      []uuid.UUID
+	removeZombie chan uuid.UUID = make(chan (uuid.UUID), 10)
+	newZombie    chan uuid.UUID = make(chan (uuid.UUID), 10)
 )
 
 // CheckZombies - checks if new node has same macaddress as existing node
 // if so, existing node is added to zombie node quarantine list
-func CheckZombies(newnode *models.Node) {
+func CheckZombies(newnode *models.Node, mac net.HardwareAddr) {
 	nodes, err := GetNetworkNodes(newnode.Network)
 	if err != nil {
 		logger.Log(1, "Failed to retrieve network nodes", newnode.Network, err.Error())
 		return
 	}
 	for _, node := range nodes {
-		if node.MacAddress == newnode.MacAddress {
-			logger.Log(0, "adding ", node.ID, " to zombie list")
+		host, err := GetHost(node.HostID.String())
+		if err != nil {
+
+		}
+		if host.MacAddress.String() == mac.String() {
+			logger.Log(0, "adding ", node.ID.String(), " to zombie list")
 			newZombie <- node.ID
 		}
 	}
@@ -46,14 +52,14 @@ func ManageZombies(ctx context.Context) {
 		case <-ctx.Done():
 			return
 		case id := <-newZombie:
-			logger.Log(1, "adding", id, "to zombie quaratine list")
+			logger.Log(1, "adding", id.String(), "to zombie quaratine list")
 			zombies = append(zombies, id)
 		case id := <-removeZombie:
 			found := false
 			if len(zombies) > 0 {
 				for i := len(zombies) - 1; i >= 0; i-- {
 					if zombies[i] == id {
-						logger.Log(1, "removing zombie from quaratine list", zombies[i])
+						logger.Log(1, "removing zombie from quaratine list", zombies[i].String())
 						zombies = append(zombies[:i], zombies[i+1:]...)
 						found = true
 					}
@@ -66,19 +72,19 @@ func ManageZombies(ctx context.Context) {
 			logger.Log(3, "checking for zombie nodes")
 			if len(zombies) > 0 {
 				for i := len(zombies) - 1; i >= 0; i-- {
-					node, err := GetNodeByID(zombies[i])
+					node, err := GetNodeByID(zombies[i].String())
 					if err != nil {
-						logger.Log(1, "error retrieving zombie node", zombies[i], err.Error())
-						logger.Log(1, "deleting ", node.Name, " from zombie list")
+						logger.Log(1, "error retrieving zombie node", zombies[i].String(), err.Error())
+						logger.Log(1, "deleting ", node.ID.String(), " from zombie list")
 						zombies = append(zombies[:i], zombies[i+1:]...)
 						continue
 					}
-					if time.Since(time.Unix(node.LastCheckIn, 0)) > time.Minute*ZOMBIE_DELETE_TIME {
-						if err := DeleteNodeByID(&node, true); err != nil {
-							logger.Log(1, "error deleting zombie node", zombies[i], err.Error())
+					if time.Since(node.LastCheckIn) > time.Minute*ZOMBIE_DELETE_TIME {
+						if err := DeleteNode(&node, true); err != nil {
+							logger.Log(1, "error deleting zombie node", zombies[i].String(), err.Error())
 							continue
 						}
-						logger.Log(1, "deleting zombie node", node.Name)
+						logger.Log(1, "deleting zombie node", node.ID.String())
 						zombies = append(zombies[:i], zombies[i+1:]...)
 					}
 				}
@@ -104,13 +110,13 @@ func InitializeZombies() {
 			if node.ID == othernode.ID {
 				continue
 			}
-			if node.MacAddress == othernode.MacAddress {
-				if node.LastCheckIn > othernode.LastCheckIn {
+			if node.HostID == othernode.HostID {
+				if node.LastCheckIn.After(othernode.LastCheckIn) {
 					zombies = append(zombies, othernode.ID)
-					logger.Log(1, "adding ", othernode.Name, " with ID ", othernode.ID, " to zombie list")
+					logger.Log(1, "adding", othernode.ID.String(), "to zombie list")
 				} else {
 					zombies = append(zombies, node.ID)
-					logger.Log(1, "adding ", node.Name, " with ID ", node.ID, " to zombie list")
+					logger.Log(1, "adding", node.ID.String(), "to zombie list")
 				}
 			}
 		}

+ 21 - 6
main.go

@@ -25,6 +25,7 @@ import (
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/serverctl"
+	stunserver "github.com/gravitl/netmaker/stun-server"
 )
 
 var version = "dev"
@@ -70,9 +71,6 @@ func initialize() { // Client Mode Prereq Check
 		logger.FatalLog("Error connecting to database: ", err.Error())
 	}
 	logger.Log(0, "database successfully connected")
-	if err = logic.AddServerIDIfNotPresent(); err != nil {
-		logger.Log(1, "failed to save server ID")
-	}
 
 	logic.SetJWTSecret()
 
@@ -111,9 +109,6 @@ func initialize() { // Client Mode Prereq Check
 		if uid != 0 {
 			logger.FatalLog("To run in client mode requires root privileges. Either disable client mode or run with sudo.")
 		}
-		if err := serverctl.InitServerNetclient(); err != nil {
-			logger.FatalLog("Did not find netclient to use CLIENT_MODE")
-		}
 	}
 	// initialize iptables to ensure gateways work correctly and mq is forwarded if containerized
 	if servercfg.ManageIPTables() != "off" {
@@ -171,6 +166,25 @@ func startControllers() {
 		logger.Log(0, "No Server Mode selected, so nothing is being served! Set Agent mode (AGENT_BACKEND) or Rest mode (REST_BACKEND) or MessageQueue (MESSAGEQUEUE_BACKEND) to 'true'.")
 	}
 
+	// starts the stun server
+	waitnetwork.Add(1)
+	go stunserver.Start(&waitnetwork)
+	if servercfg.IsProxyEnabled() {
+
+		waitnetwork.Add(1)
+		go func() {
+			defer waitnetwork.Done()
+			_, cancel := context.WithCancel(context.Background())
+			waitnetwork.Add(1)
+
+			//go nmproxy.Start(ctx, logic.ProxyMgmChan, servercfg.GetAPIHost())
+			quit := make(chan os.Signal, 1)
+			signal.Notify(quit, syscall.SIGTERM, os.Interrupt)
+			<-quit
+			cancel()
+		}()
+	}
+
 	waitnetwork.Wait()
 }
 
@@ -184,6 +198,7 @@ func runMessageQueue(wg *sync.WaitGroup) {
 	ctx, cancel := context.WithCancel(context.Background())
 	go mq.Keepalive(ctx)
 	go logic.ManageZombies(ctx)
+	go logic.PurgePendingNodes(ctx)
 	quit := make(chan os.Signal, 1)
 	signal.Notify(quit, syscall.SIGTERM, os.Interrupt)
 	<-quit

+ 105 - 0
models/api_host.go

@@ -0,0 +1,105 @@
+package models
+
+import "net"
+
+// ApiHost - the host struct for API usage
+type ApiHost struct {
+	ID              string   `json:"id"`
+	Verbosity       int      `json:"verbosity"`
+	FirewallInUse   string   `json:"firewallinuse"`
+	Version         string   `json:"version"`
+	Name            string   `json:"name"`
+	OS              string   `json:"os"`
+	Debug           bool     `json:"debug"`
+	IsStatic        bool     `json:"isstatic"`
+	ListenPort      int      `json:"listenport"`
+	LocalRange      string   `json:"localrange"`
+	LocalListenPort int      `json:"locallistenport"`
+	ProxyListenPort int      `json:"proxy_listen_port"`
+	MTU             int      `json:"mtu" yaml:"mtu"`
+	Interfaces      []Iface  `json:"interfaces" yaml:"interfaces"`
+	EndpointIP      string   `json:"endpointip" yaml:"endpointip"`
+	PublicKey       string   `json:"publickey"`
+	MacAddress      string   `json:"macaddress"`
+	InternetGateway string   `json:"internetgateway"`
+	Nodes           []string `json:"nodes"`
+	ProxyEnabled    bool     `json:"proxy_enabled" yaml:"proxy_enabled"`
+	IsDefault       bool     `json:"isdefault" yaml:"isdefault"`
+}
+
+// Host.ConvertNMHostToAPI - converts a Netmaker host to an API editable host
+func (h *Host) ConvertNMHostToAPI() *ApiHost {
+	a := ApiHost{}
+	a.Debug = h.Debug
+	a.EndpointIP = h.EndpointIP.String()
+	a.FirewallInUse = h.FirewallInUse
+	a.ID = h.ID.String()
+	a.Interfaces = h.Interfaces
+	a.InternetGateway = h.InternetGateway.String()
+	if isEmptyAddr(a.InternetGateway) {
+		a.InternetGateway = ""
+	}
+	a.IsStatic = h.IsStatic
+	a.ListenPort = h.ListenPort
+	a.LocalListenPort = h.LocalListenPort
+	a.LocalRange = h.LocalRange.String()
+	if isEmptyAddr(a.LocalRange) {
+		a.LocalRange = ""
+	}
+	a.MTU = h.MTU
+	a.MacAddress = h.MacAddress.String()
+	a.Name = h.Name
+	a.OS = h.OS
+	a.Nodes = h.Nodes
+	a.ProxyEnabled = h.ProxyEnabled
+	a.ProxyListenPort = h.ProxyListenPort
+	a.PublicKey = h.PublicKey.String()
+	a.Verbosity = h.Verbosity
+	a.Version = h.Version
+	a.IsDefault = h.IsDefault
+
+	return &a
+}
+
+// APIHost.ConvertAPIHostToNMHost - convert's a given apihost struct to
+// a Host struct
+func (a *ApiHost) ConvertAPIHostToNMHost(currentHost *Host) *Host {
+	h := Host{}
+	h.ID = currentHost.ID
+	h.HostPass = currentHost.HostPass
+	h.DaemonInstalled = currentHost.DaemonInstalled
+	h.EndpointIP = net.ParseIP(a.EndpointIP)
+	h.Debug = a.Debug
+	h.FirewallInUse = a.FirewallInUse
+	h.IPForwarding = currentHost.IPForwarding
+	h.Interface = currentHost.Interface
+	h.Interfaces = currentHost.Interfaces
+	h.InternetGateway = currentHost.InternetGateway
+	h.IsDocker = currentHost.IsDocker
+	h.IsK8S = currentHost.IsK8S
+	h.IsStatic = a.IsStatic
+	h.ListenPort = a.ListenPort
+	h.LocalListenPort = currentHost.ListenPort
+	h.MTU = a.MTU
+	h.MacAddress = currentHost.MacAddress
+	h.PublicKey = currentHost.PublicKey
+	h.Name = a.Name
+	h.Version = currentHost.Version
+	h.Verbosity = a.Verbosity
+	h.Nodes = currentHost.Nodes
+	h.TrafficKeyPublic = currentHost.TrafficKeyPublic
+	h.OS = currentHost.OS
+
+	if len(a.LocalRange) > 0 {
+		_, localRange, err := net.ParseCIDR(a.LocalRange)
+		if err == nil {
+			h.LocalRange = *localRange
+		}
+	} else if !isEmptyAddr(currentHost.LocalRange.String()) {
+		h.LocalRange = currentHost.LocalRange
+	}
+	h.ProxyEnabled = a.ProxyEnabled
+	h.IsDefault = a.IsDefault
+
+	return &h
+}

+ 175 - 0
models/api_node.go

@@ -0,0 +1,175 @@
+package models
+
+import (
+	"net"
+	"time"
+
+	"github.com/google/uuid"
+)
+
+// ApiNode is a stripped down Node DTO that exposes only required fields to external systems
+type ApiNode struct {
+	ID                      string   `json:"id,omitempty" validate:"required,min=5,id_unique"`
+	HostID                  string   `json:"hostid,omitempty" validate:"required,min=5,id_unique"`
+	Address                 string   `json:"address" validate:"omitempty,ipv4"`
+	Address6                string   `json:"address6" validate:"omitempty,ipv6"`
+	LocalAddress            string   `json:"localaddress" validate:"omitempty,ipv4"`
+	PostUp                  string   `json:"postup"`
+	PostDown                string   `json:"postdown"`
+	AllowedIPs              []string `json:"allowedips"`
+	PersistentKeepalive     int32    `json:"persistentkeepalive"`
+	LastModified            int64    `json:"lastmodified"`
+	ExpirationDateTime      int64    `json:"expdatetime"`
+	LastCheckIn             int64    `json:"lastcheckin"`
+	LastPeerUpdate          int64    `json:"lastpeerupdate"`
+	Network                 string   `json:"network"`
+	NetworkRange            string   `json:"networkrange"`
+	NetworkRange6           string   `json:"networkrange6"`
+	IsRelayed               bool     `json:"isrelayed"`
+	IsRelay                 bool     `json:"isrelay"`
+	IsEgressGateway         bool     `json:"isegressgateway"`
+	IsIngressGateway        bool     `json:"isingressgateway"`
+	EgressGatewayRanges     []string `json:"egressgatewayranges"`
+	EgressGatewayNatEnabled bool     `json:"egressgatewaynatenabled"`
+	RelayAddrs              []string `json:"relayaddrs"`
+	FailoverNode            string   `json:"failovernode"`
+	DNSOn                   bool     `json:"dnson"`
+	IsLocal                 bool     `json:"islocal"`
+	Server                  string   `json:"server"`
+	InternetGateway         string   `json:"internetgateway"`
+	Connected               bool     `json:"connected"`
+	PendingDelete           bool     `json:"pendingdelete"`
+	// == PRO ==
+	DefaultACL string `json:"defaultacl,omitempty" validate:"checkyesornoorunset"`
+	Failover   bool   `json:"failover"`
+}
+
+// ApiNode.ConvertToServerNode - converts an api node to a server node
+func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node {
+	convertedNode := Node{}
+	convertedNode.Network = a.Network
+	convertedNode.Server = a.Server
+	convertedNode.Action = currentNode.Action
+	convertedNode.Connected = a.Connected
+	convertedNode.ID, _ = uuid.Parse(a.ID)
+	convertedNode.HostID, _ = uuid.Parse(a.HostID)
+	convertedNode.PostUp = a.PostUp
+	convertedNode.PostDown = a.PostDown
+	convertedNode.IsLocal = a.IsLocal
+	convertedNode.IsRelay = a.IsRelay
+	convertedNode.IsRelayed = a.IsRelayed
+	convertedNode.PendingDelete = a.PendingDelete
+	convertedNode.Peers = currentNode.Peers
+	convertedNode.Failover = a.Failover
+	convertedNode.IsEgressGateway = a.IsEgressGateway
+	convertedNode.IsIngressGateway = a.IsIngressGateway
+	convertedNode.EgressGatewayRanges = a.EgressGatewayRanges
+	convertedNode.IngressGatewayRange = currentNode.IngressGatewayRange
+	convertedNode.IngressGatewayRange6 = currentNode.IngressGatewayRange6
+	convertedNode.DNSOn = a.DNSOn
+	convertedNode.EgressGatewayRequest = currentNode.EgressGatewayRequest
+	convertedNode.EgressGatewayNatEnabled = currentNode.EgressGatewayNatEnabled
+	convertedNode.PersistentKeepalive = time.Second * time.Duration(a.PersistentKeepalive)
+	convertedNode.RelayAddrs = a.RelayAddrs
+	convertedNode.DefaultACL = a.DefaultACL
+	convertedNode.OwnerID = currentNode.OwnerID
+	_, networkRange, err := net.ParseCIDR(a.NetworkRange)
+	if err == nil {
+		convertedNode.NetworkRange = *networkRange
+	}
+	_, networkRange6, err := net.ParseCIDR(a.NetworkRange6)
+	if err == nil {
+		convertedNode.NetworkRange6 = *networkRange6
+	}
+	if len(a.LocalAddress) > 0 {
+		_, localAddr, err := net.ParseCIDR(a.LocalAddress)
+		if err == nil {
+			convertedNode.LocalAddress = *localAddr
+		}
+	} else if !isEmptyAddr(currentNode.LocalAddress.String()) {
+		convertedNode.LocalAddress = currentNode.LocalAddress
+	}
+	udpAddr, err := net.ResolveUDPAddr("udp", a.InternetGateway)
+	if err == nil {
+		convertedNode.InternetGateway = udpAddr
+	}
+	_, addr, err := net.ParseCIDR(a.Address)
+	if err == nil {
+		convertedNode.Address = *addr
+	}
+	_, addr6, err := net.ParseCIDR(a.Address6)
+	if err == nil {
+		convertedNode.Address = *addr6
+	}
+	convertedNode.FailoverNode, _ = uuid.Parse(a.FailoverNode)
+	convertedNode.LastModified = time.Unix(a.LastModified, 0)
+	convertedNode.LastCheckIn = time.Unix(a.LastCheckIn, 0)
+	convertedNode.LastPeerUpdate = time.Unix(a.LastPeerUpdate, 0)
+	convertedNode.ExpirationDateTime = time.Unix(a.ExpirationDateTime, 0)
+	return &convertedNode
+}
+
+// Node.ConvertToAPINode - converts a node to an API node
+func (nm *Node) ConvertToAPINode() *ApiNode {
+	apiNode := ApiNode{}
+	apiNode.ID = nm.ID.String()
+	apiNode.HostID = nm.HostID.String()
+	apiNode.Address = nm.Address.String()
+	if isEmptyAddr(apiNode.Address) {
+		apiNode.Address = ""
+	}
+	apiNode.Address6 = nm.Address6.String()
+	if isEmptyAddr(apiNode.Address6) {
+		apiNode.Address6 = ""
+	}
+	apiNode.LocalAddress = nm.LocalAddress.String()
+	if isEmptyAddr(apiNode.LocalAddress) {
+		apiNode.LocalAddress = ""
+	}
+	apiNode.PostDown = nm.PostDown
+	apiNode.PostUp = nm.PostUp
+	apiNode.PersistentKeepalive = int32(nm.PersistentKeepalive.Seconds())
+	apiNode.LastModified = nm.LastModified.Unix()
+	apiNode.LastCheckIn = nm.LastCheckIn.Unix()
+	apiNode.LastPeerUpdate = nm.LastPeerUpdate.Unix()
+	apiNode.Network = nm.Network
+	apiNode.NetworkRange = nm.NetworkRange.String()
+	if isEmptyAddr(apiNode.NetworkRange) {
+		apiNode.NetworkRange = ""
+	}
+	apiNode.NetworkRange6 = nm.NetworkRange6.String()
+	if isEmptyAddr(apiNode.NetworkRange6) {
+		apiNode.NetworkRange6 = ""
+	}
+	apiNode.IsRelayed = nm.IsRelayed
+	apiNode.IsRelay = nm.IsRelay
+	apiNode.IsEgressGateway = nm.IsEgressGateway
+	apiNode.IsIngressGateway = nm.IsIngressGateway
+	apiNode.EgressGatewayRanges = nm.EgressGatewayRanges
+	apiNode.EgressGatewayNatEnabled = nm.EgressGatewayNatEnabled
+	apiNode.RelayAddrs = nm.RelayAddrs
+	apiNode.FailoverNode = nm.FailoverNode.String()
+	if isUUIDSet(apiNode.FailoverNode) {
+		apiNode.FailoverNode = ""
+	}
+	apiNode.DNSOn = nm.DNSOn
+	apiNode.IsLocal = nm.IsLocal
+	apiNode.Server = nm.Server
+	apiNode.InternetGateway = nm.InternetGateway.String()
+	if isEmptyAddr(apiNode.InternetGateway) {
+		apiNode.InternetGateway = ""
+	}
+	apiNode.Connected = nm.Connected
+	apiNode.PendingDelete = nm.PendingDelete
+	apiNode.DefaultACL = nm.DefaultACL
+	apiNode.Failover = nm.Failover
+	return &apiNode
+}
+
+func isEmptyAddr(addr string) bool {
+	return addr == "<nil>" || addr == ":0"
+}
+
+func isUUIDSet(uuid string) bool {
+	return uuid != "00000000-0000-0000-0000-000000000000"
+}

+ 2 - 0
models/extclient.go

@@ -14,4 +14,6 @@ type ExtClient struct {
 	LastModified           int64  `json:"lastmodified" bson:"lastmodified"`
 	Enabled                bool   `json:"enabled" bson:"enabled"`
 	OwnerID                string `json:"ownerid" bson:"ownerid"`
+	InternalIPAddr         string `json:"internal_ip_addr" bson:"internal_ip_addr"`
+	InternalIPAddr6        string `json:"internal_ip_addr6" bson:"internal_ip_addr6"`
 }

+ 62 - 0
models/host.go

@@ -0,0 +1,62 @@
+package models
+
+import (
+	"net"
+
+	"github.com/google/uuid"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+)
+
+// WIREGUARD_INTERFACE name of wireguard interface
+const WIREGUARD_INTERFACE = "netmaker"
+
+// Host - represents a host on the network
+type Host struct {
+	ID               uuid.UUID        `json:"id" yaml:"id"`
+	Verbosity        int              `json:"verbosity" yaml:"verbosity"`
+	FirewallInUse    string           `json:"firewallinuse" yaml:"firewallinuse"`
+	Version          string           `json:"version" yaml:"version"`
+	IPForwarding     bool             `json:"ipforwarding" yaml:"ipforwarding"`
+	DaemonInstalled  bool             `json:"daemoninstalled" yaml:"daemoninstalled"`
+	HostPass         string           `json:"hostpass" yaml:"hostpass"`
+	Name             string           `json:"name" yaml:"name"`
+	OS               string           `json:"os" yaml:"os"`
+	Interface        string           `json:"interface" yaml:"interface"`
+	Debug            bool             `json:"debug" yaml:"debug"`
+	ListenPort       int              `json:"listenport" yaml:"listenport"`
+	LocalAddress     net.IPNet        `json:"localaddress" yaml:"localaddress"`
+	LocalRange       net.IPNet        `json:"localrange" yaml:"localrange"`
+	LocalListenPort  int              `json:"locallistenport" yaml:"locallistenport"`
+	ProxyListenPort  int              `json:"proxy_listen_port" yaml:"proxy_listen_port"`
+	MTU              int              `json:"mtu" yaml:"mtu"`
+	PublicKey        wgtypes.Key      `json:"publickey" yaml:"publickey"`
+	MacAddress       net.HardwareAddr `json:"macaddress" yaml:"macaddress"`
+	TrafficKeyPublic []byte           `json:"traffickeypublic" yaml:"trafficekeypublic"`
+	InternetGateway  net.UDPAddr      `json:"internetgateway" yaml:"internetgateway"`
+	Nodes            []string         `json:"nodes" yaml:"nodes"`
+	Interfaces       []Iface          `json:"interfaces" yaml:"interfaces"`
+	EndpointIP       net.IP           `json:"endpointip" yaml:"endpointip"`
+	ProxyEnabled     bool             `json:"proxy_enabled" yaml:"proxy_enabled"`
+	IsDocker         bool             `json:"isdocker" yaml:"isdocker"`
+	IsK8S            bool             `json:"isk8s" yaml:"isk8s"`
+	IsStatic         bool             `json:"isstatic" yaml:"isstatic"`
+	IsDefault        bool             `json:"isdefault" yaml:"isdefault"`
+}
+
+// FormatBool converts a boolean to a [yes|no] string
+func FormatBool(b bool) string {
+	s := "no"
+	if b {
+		s = "yes"
+	}
+	return s
+}
+
+// ParseBool parses a [yes|no] string to boolean value
+func ParseBool(s string) bool {
+	b := false
+	if s == "yes" {
+		b = true
+	}
+	return b
+}

+ 12 - 7
models/metrics.go

@@ -1,15 +1,20 @@
 package models
 
-import "time"
+import (
+	"time"
+
+	proxy_models "github.com/gravitl/netclient/nmproxy/models"
+)
 
 // Metrics - metrics struct
 type Metrics struct {
-	Network       string            `json:"network" bson:"network" yaml:"network"`
-	NodeID        string            `json:"node_id" bson:"node_id" yaml:"node_id"`
-	NodeName      string            `json:"node_name" bson:"node_name" yaml:"node_name"`
-	IsServer      string            `json:"isserver" bson:"isserver" yaml:"isserver" validate:"checkyesorno"`
-	Connectivity  map[string]Metric `json:"connectivity" bson:"connectivity" yaml:"connectivity"`
-	FailoverPeers map[string]string `json:"needsfailover" bson:"needsfailover" yaml:"needsfailover"`
+	Network       string                         `json:"network" bson:"network" yaml:"network"`
+	NodeID        string                         `json:"node_id" bson:"node_id" yaml:"node_id"`
+	NodeName      string                         `json:"node_name" bson:"node_name" yaml:"node_name"`
+	IsServer      string                         `json:"isserver" bson:"isserver" yaml:"isserver" validate:"checkyesorno"`
+	Connectivity  map[string]Metric              `json:"connectivity" bson:"connectivity" yaml:"connectivity"`
+	FailoverPeers map[string]string              `json:"needsfailover" bson:"needsfailover" yaml:"needsfailover"`
+	ProxyMetrics  map[string]proxy_models.Metric `json:"proxy_metrics" bson:"proxy_metrics" yaml:"proxy_metrics"`
 }
 
 // Metric - holds a metric for data between nodes

+ 11 - 7
models/mqtt.go

@@ -1,15 +1,19 @@
 package models
 
-import "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+import (
+	proxy_models "github.com/gravitl/netclient/nmproxy/models"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+)
 
 // PeerUpdate - struct
 type PeerUpdate struct {
-	Network       string               `json:"network" bson:"network" yaml:"network"`
-	ServerVersion string               `json:"serverversion" bson:"serverversion" yaml:"serverversion"`
-	ServerAddrs   []ServerAddr         `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"`
-	Peers         []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"`
-	DNS           string               `json:"dns" bson:"dns" yaml:"dns"`
-	PeerIDs       PeerMap              `json:"peerids" bson:"peerids" yaml:"peerids"`
+	Network       string                           `json:"network" bson:"network" yaml:"network"`
+	ServerVersion string                           `json:"serverversion" bson:"serverversion" yaml:"serverversion"`
+	ServerAddrs   []ServerAddr                     `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"`
+	Peers         []wgtypes.PeerConfig             `json:"peers" bson:"peers" yaml:"peers"`
+	DNS           string                           `json:"dns" bson:"dns" yaml:"dns"`
+	PeerIDs       PeerMap                          `json:"peerids" bson:"peerids" yaml:"peerids"`
+	ProxyUpdate   proxy_models.ProxyManagerPayload `json:"proxy_update" bson:"proxy_update" yaml:"proxy_update"`
 }
 
 // KeyUpdate - key update struct

+ 252 - 138
models/node.go

@@ -7,7 +7,8 @@ import (
 	"strings"
 	"time"
 
-	"golang.org/x/crypto/bcrypt"
+	"github.com/google/uuid"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 )
 
 const (
@@ -42,19 +43,68 @@ var seededRand *rand.Rand = rand.New(
 // NodeCheckin - struct for node checkins with server
 type NodeCheckin struct {
 	Version   string
-	Connected string
+	Connected bool
 	Ifaces    []Iface
 }
 
 // Iface struct for local interfaces of a node
 type Iface struct {
-	Name    string
-	Address net.IPNet
+	Name          string    `json:"name"`
+	Address       net.IPNet `json:"address"`
+	AddressString string    `json:"addressString"`
+}
+
+// CommonNode - represents a commonn node data elements shared by netmaker and netclient
+type CommonNode struct {
+	ID                  uuid.UUID            `json:"id" yaml:"id"`
+	HostID              uuid.UUID            `json:"hostid" yaml:"hostid"`
+	Network             string               `json:"network" yaml:"network"`
+	NetworkRange        net.IPNet            `json:"networkrange" yaml:"networkrange"`
+	NetworkRange6       net.IPNet            `json:"networkrange6" yaml:"networkrange6"`
+	InternetGateway     *net.UDPAddr         `json:"internetgateway" yaml:"internetgateway"`
+	Server              string               `json:"server" yaml:"server"`
+	Connected           bool                 `json:"connected" yaml:"connected"`
+	Address             net.IPNet            `json:"address" yaml:"address"`
+	Address6            net.IPNet            `json:"address6" yaml:"address6"`
+	PostUp              string               `json:"postup" yaml:"postup"`
+	PostDown            string               `json:"postdown" yaml:"postdown"`
+	Action              string               `json:"action" yaml:"action"`
+	LocalAddress        net.IPNet            `json:"localaddress" yaml:"localaddress"`
+	IsLocal             bool                 `json:"islocal" yaml:"islocal"`
+	IsEgressGateway     bool                 `json:"isegressgateway" yaml:"isegressgateway"`
+	IsIngressGateway    bool                 `json:"isingressgateway" yaml:"isingressgateway"`
+	DNSOn               bool                 `json:"dnson" yaml:"dnson"`
+	PersistentKeepalive time.Duration        `json:"persistentkeepalive" yaml:"persistentkeepalive"`
+	Peers               []wgtypes.PeerConfig `json:"peers" yaml:"peers"`
+}
+
+// Node - a model of a network node
+type Node struct {
+	CommonNode
+	PendingDelete           bool                 `json:"pendingdelete" bson:"pendingdelete" yaml:"pendingdelete"`
+	LastModified            time.Time            `json:"lastmodified" bson:"lastmodified" yaml:"lastmodified"`
+	LastCheckIn             time.Time            `json:"lastcheckin" bson:"lastcheckin" yaml:"lastcheckin"`
+	LastPeerUpdate          time.Time            `json:"lastpeerupdate" bson:"lastpeerupdate" yaml:"lastpeerupdate"`
+	ExpirationDateTime      time.Time            `json:"expdatetime" bson:"expdatetime" yaml:"expdatetime"`
+	EgressGatewayRanges     []string             `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
+	EgressGatewayNatEnabled bool                 `json:"egressgatewaynatenabled" bson:"egressgatewaynatenabled" yaml:"egressgatewaynatenabled"`
+	EgressGatewayRequest    EgressGatewayRequest `json:"egressgatewayrequest" bson:"egressgatewayrequest" yaml:"egressgatewayrequest"`
+	IngressGatewayRange     string               `json:"ingressgatewayrange" bson:"ingressgatewayrange" yaml:"ingressgatewayrange"`
+	IngressGatewayRange6    string               `json:"ingressgatewayrange6" bson:"ingressgatewayrange6" yaml:"ingressgatewayrange6"`
+	IsRelayed               bool                 `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
+	IsRelay                 bool                 `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
+	RelayAddrs              []string             `json:"relayaddrs" bson:"relayaddrs" yaml:"relayaddrs"`
+	// == PRO ==
+	DefaultACL   string    `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
+	OwnerID      string    `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"`
+	FailoverNode uuid.UUID `json:"failovernode" bson:"failovernode" yaml:"failovernode"`
+	Failover     bool      `json:"failover" bson:"failover" yaml:"failover"`
 }
 
-// Node - struct for node model
-type Node struct {
+// LegacyNode - legacy struct for node model
+type LegacyNode struct {
 	ID                      string               `json:"id,omitempty" bson:"id,omitempty" yaml:"id,omitempty" validate:"required,min=5,id_unique"`
+	HostID                  string               `json:"hostid,omitempty" bson:"id,omitempty" yaml:"hostid,omitempty" validate:"required,min=5,id_unique"`
 	Address                 string               `json:"address" bson:"address" yaml:"address" validate:"omitempty,ipv4"`
 	Address6                string               `json:"address6" bson:"address6" yaml:"address6" validate:"omitempty,ipv6"`
 	LocalAddress            string               `json:"localaddress" bson:"localaddress" yaml:"localaddress" validate:"omitempty"`
@@ -63,6 +113,7 @@ type Node struct {
 	NetworkSettings         Network              `json:"networksettings" bson:"networksettings" yaml:"networksettings" validate:"-"`
 	ListenPort              int32                `json:"listenport" bson:"listenport" yaml:"listenport" validate:"omitempty,numeric,min=1024,max=65535"`
 	LocalListenPort         int32                `json:"locallistenport" bson:"locallistenport" yaml:"locallistenport" validate:"numeric,min=0,max=65535"`
+	ProxyListenPort         int32                `json:"proxy_listen_port" bson:"proxy_listen_port" yaml:"proxy_listen_port" validate:"numeric,min=0,max=65535"`
 	PublicKey               string               `json:"publickey" bson:"publickey" yaml:"publickey" validate:"required,base64"`
 	Endpoint                string               `json:"endpoint" bson:"endpoint" yaml:"endpoint" validate:"required,ip"`
 	PostUp                  string               `json:"postup" bson:"postup" yaml:"postup"`
@@ -110,6 +161,8 @@ type Node struct {
 	FirewallInUse   string      `json:"firewallinuse" bson:"firewallinuse" yaml:"firewallinuse"`
 	InternetGateway string      `json:"internetgateway" bson:"internetgateway" yaml:"internetgateway"`
 	Connected       string      `json:"connected" bson:"connected" yaml:"connected" validate:"checkyesorno"`
+	PendingDelete   bool        `json:"pendingdelete" bson:"pendingdelete" yaml:"pendingdelete"`
+	Proxy           bool        `json:"proxy" bson:"proxy" yaml:"proxy"`
 	// == PRO ==
 	DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
 	OwnerID    string `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"`
@@ -122,8 +175,10 @@ type NodesArray []Node
 // NodesArray.Len - gets length of node array
 func (a NodesArray) Len() int { return len(a) }
 
-// NodesArray.Less - gets returns lower rank of two node addresses
-func (a NodesArray) Less(i, j int) bool { return isLess(a[i].Address, a[j].Address) }
+// NodesArray.Less - gets returns lower rank of two node addressesFill
+func (a NodesArray) Less(i, j int) bool {
+	return isLess(a[i].Address.IP.String(), a[j].Address.IP.String())
+}
 
 // NodesArray.Swap - swaps two nodes in array
 func (a NodesArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
@@ -136,14 +191,14 @@ func isLess(ipA string, ipB string) bool {
 
 // Node.PrimaryAddress - return ipv4 address if present, else return ipv6
 func (node *Node) PrimaryAddress() string {
-	if node.Address != "" {
-		return node.Address
+	if node.Address.IP != nil {
+		return node.Address.IP.String()
 	}
-	return node.Address6
+	return node.Address6.IP.String()
 }
 
 // Node.SetDefaultConnected
-func (node *Node) SetDefaultConnected() {
+func (node *LegacyNode) SetDefaultConnected() {
 	if node.Connected == "" {
 		node.Connected = "yes"
 	}
@@ -153,84 +208,77 @@ func (node *Node) SetDefaultConnected() {
 }
 
 // Node.SetDefaultACL
-func (node *Node) SetDefaultACL() {
+func (node *LegacyNode) SetDefaultACL() {
 	if node.DefaultACL == "" {
 		node.DefaultACL = "yes"
 	}
 }
 
 // Node.SetDefaultMTU - sets default MTU of a node
-func (node *Node) SetDefaultMTU() {
+func (node *LegacyNode) SetDefaultMTU() {
 	if node.MTU == 0 {
 		node.MTU = 1280
 	}
 }
 
 // Node.SetDefaultNFTablesPresent - sets default for nftables check
-func (node *Node) SetDefaultNFTablesPresent() {
+func (node *LegacyNode) SetDefaultNFTablesPresent() {
 	if node.FirewallInUse == "" {
 		node.FirewallInUse = FIREWALL_IPTABLES // default to iptables
 	}
 }
 
-// Node.SetDefaulIsPending - sets ispending default
-func (node *Node) SetDefaulIsPending() {
-	if node.IsPending == "" {
-		node.IsPending = "no"
-	}
-}
-
 // Node.SetDefaultIsRelayed - set default is relayed
-func (node *Node) SetDefaultIsRelayed() {
+func (node *LegacyNode) SetDefaultIsRelayed() {
 	if node.IsRelayed == "" {
 		node.IsRelayed = "no"
 	}
 }
 
 // Node.SetDefaultIsRelayed - set default is relayed
-func (node *Node) SetDefaultIsHub() {
+func (node *LegacyNode) SetDefaultIsHub() {
 	if node.IsHub == "" {
 		node.IsHub = "no"
 	}
 }
 
 // Node.SetDefaultIsRelay - set default isrelay
-func (node *Node) SetDefaultIsRelay() {
+func (node *LegacyNode) SetDefaultIsRelay() {
 	if node.IsRelay == "" {
 		node.IsRelay = "no"
 	}
 }
 
 // Node.SetDefaultIsDocker - set default isdocker
-func (node *Node) SetDefaultIsDocker() {
+func (node *LegacyNode) SetDefaultIsDocker() {
 	if node.IsDocker == "" {
 		node.IsDocker = "no"
 	}
 }
 
 // Node.SetDefaultIsK8S - set default isk8s
-func (node *Node) SetDefaultIsK8S() {
+func (node *LegacyNode) SetDefaultIsK8S() {
 	if node.IsK8S == "" {
 		node.IsK8S = "no"
 	}
 }
 
 // Node.SetDefaultEgressGateway - sets default egress gateway status
-func (node *Node) SetDefaultEgressGateway() {
+func (node *LegacyNode) SetDefaultEgressGateway() {
 	if node.IsEgressGateway == "" {
 		node.IsEgressGateway = "no"
 	}
 }
 
 // Node.SetDefaultIngressGateway - sets default ingress gateway status
-func (node *Node) SetDefaultIngressGateway() {
+func (node *LegacyNode) SetDefaultIngressGateway() {
 	if node.IsIngressGateway == "" {
 		node.IsIngressGateway = "no"
 	}
 }
 
 // Node.SetDefaultAction - sets default action status
-func (node *Node) SetDefaultAction() {
+func (node *LegacyNode) SetDefaultAction() {
 	if node.Action == "" {
 		node.Action = NODE_NOOP
 	}
@@ -244,35 +292,35 @@ func (node *Node) SetDefaultAction() {
 //}
 
 // Node.SetIPForwardingDefault - set ip forwarding default
-func (node *Node) SetIPForwardingDefault() {
+func (node *LegacyNode) SetIPForwardingDefault() {
 	if node.IPForwarding == "" {
 		node.IPForwarding = "yes"
 	}
 }
 
 // Node.SetIsLocalDefault - set is local default
-func (node *Node) SetIsLocalDefault() {
+func (node *LegacyNode) SetIsLocalDefault() {
 	if node.IsLocal == "" {
 		node.IsLocal = "no"
 	}
 }
 
 // Node.SetDNSOnDefault - sets dns on default
-func (node *Node) SetDNSOnDefault() {
+func (node *LegacyNode) SetDNSOnDefault() {
 	if node.DNSOn == "" {
 		node.DNSOn = "yes"
 	}
 }
 
 // Node.SetIsServerDefault - sets node isserver default
-func (node *Node) SetIsServerDefault() {
+func (node *LegacyNode) SetIsServerDefault() {
 	if node.IsServer != "yes" {
 		node.IsServer = "no"
 	}
 }
 
 // Node.SetIsStaticDefault - set is static default
-func (node *Node) SetIsStaticDefault() {
+func (node *LegacyNode) SetIsStaticDefault() {
 	if node.IsServer == "yes" {
 		node.IsStatic = "yes"
 	} else if node.IsStatic != "yes" {
@@ -282,33 +330,33 @@ func (node *Node) SetIsStaticDefault() {
 
 // Node.SetLastModified - set last modified initial time
 func (node *Node) SetLastModified() {
-	node.LastModified = time.Now().Unix()
+	node.LastModified = time.Now()
 }
 
-// Node.SetLastCheckIn - time.Now().Unix()
+// Node.SetLastCheckIn - set checkin time of node
 func (node *Node) SetLastCheckIn() {
-	node.LastCheckIn = time.Now().Unix()
+	node.LastCheckIn = time.Now()
 }
 
 // Node.SetLastPeerUpdate - sets last peer update time
 func (node *Node) SetLastPeerUpdate() {
-	node.LastPeerUpdate = time.Now().Unix()
+	node.LastPeerUpdate = time.Now()
 }
 
 // Node.SetExpirationDateTime - sets node expiry time
 func (node *Node) SetExpirationDateTime() {
-	node.ExpirationDateTime = time.Now().Unix() + TEN_YEARS_IN_SECONDS
+	node.ExpirationDateTime = time.Now().Add(TEN_YEARS_IN_SECONDS)
 }
 
 // Node.SetDefaultName - sets a random name to node
-func (node *Node) SetDefaultName() {
+func (node *LegacyNode) SetDefaultName() {
 	if node.Name == "" {
 		node.Name = GenerateNodeName()
 	}
 }
 
 // Node.SetDefaultFailover - sets default value of failover status to no if not set
-func (node *Node) SetDefaultFailover() {
+func (node *LegacyNode) SetDefaultFailover() {
 	if node.Failover == "" {
 		node.Failover = "no"
 	}
@@ -318,84 +366,43 @@ func (node *Node) SetDefaultFailover() {
 func (newNode *Node) Fill(currentNode *Node) { // TODO add new field for nftables present
 	newNode.ID = currentNode.ID
 
-	if newNode.Address == "" {
+	// Revisit the logic for boolean values
+	// TODO ---- !!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	// TODO ---- !!!!!!!!!!!!!!!!!!!!!!!!!!
+	if newNode.Address.String() == "" {
 		newNode.Address = currentNode.Address
 	}
-	if newNode.Address6 == "" {
+	if newNode.Address6.String() == "" {
 		newNode.Address6 = currentNode.Address6
 	}
-	if newNode.LocalAddress == "" {
-		newNode.LocalAddress = currentNode.LocalAddress
-	}
-	if newNode.Name == "" {
-		newNode.Name = currentNode.Name
-	}
-	if newNode.ListenPort == 0 {
-		newNode.ListenPort = currentNode.ListenPort
-	}
-	if newNode.LocalListenPort == 0 {
-		newNode.LocalListenPort = currentNode.LocalListenPort
-	}
-	if newNode.PublicKey == "" {
-		newNode.PublicKey = currentNode.PublicKey
-	}
-	if newNode.Endpoint == "" {
-		newNode.Endpoint = currentNode.Endpoint
-	}
 	if newNode.PostUp == "" {
 		newNode.PostUp = currentNode.PostUp
 	}
 	if newNode.PostDown == "" {
 		newNode.PostDown = currentNode.PostDown
 	}
-	if newNode.AllowedIPs == nil {
-		newNode.AllowedIPs = currentNode.AllowedIPs
-	}
 	if newNode.PersistentKeepalive < 0 {
 		newNode.PersistentKeepalive = currentNode.PersistentKeepalive
 	}
-	if newNode.AccessKey == "" {
-		newNode.AccessKey = currentNode.AccessKey
-	}
-	if newNode.Interface == "" {
-		newNode.Interface = currentNode.Interface
-	}
-	if newNode.LastModified == 0 {
+	if newNode.LastModified != currentNode.LastModified {
 		newNode.LastModified = currentNode.LastModified
 	}
-	if newNode.ExpirationDateTime == 0 {
+	if newNode.ExpirationDateTime.IsZero() {
 		newNode.ExpirationDateTime = currentNode.ExpirationDateTime
 	}
-	if newNode.LastPeerUpdate == 0 {
+	if newNode.LastPeerUpdate.IsZero() {
 		newNode.LastPeerUpdate = currentNode.LastPeerUpdate
 	}
-	if newNode.LastCheckIn == 0 {
+	if newNode.LastCheckIn.IsZero() {
 		newNode.LastCheckIn = currentNode.LastCheckIn
 	}
-	if newNode.MacAddress == "" {
-		newNode.MacAddress = currentNode.MacAddress
-	}
-	if newNode.Password != "" {
-		err := bcrypt.CompareHashAndPassword([]byte(newNode.Password), []byte(currentNode.Password))
-		if err != nil && currentNode.Password != newNode.Password {
-			hash, err := bcrypt.GenerateFromPassword([]byte(newNode.Password), 5)
-			if err == nil {
-				newNode.Password = string(hash)
-			}
-		}
-	} else {
-		newNode.Password = currentNode.Password
-	}
 	if newNode.Network == "" {
 		newNode.Network = currentNode.Network
 	}
-	if newNode.IsPending == "" {
-		newNode.IsPending = currentNode.IsPending
-	}
-	if newNode.IsEgressGateway == "" {
+	if newNode.IsEgressGateway != currentNode.IsEgressGateway {
 		newNode.IsEgressGateway = currentNode.IsEgressGateway
 	}
-	if newNode.IsIngressGateway == "" {
+	if newNode.IsIngressGateway != currentNode.IsIngressGateway {
 		newNode.IsIngressGateway = currentNode.IsIngressGateway
 	}
 	if newNode.EgressGatewayRanges == nil {
@@ -407,73 +414,36 @@ func (newNode *Node) Fill(currentNode *Node) { // TODO add new field for nftable
 	if newNode.IngressGatewayRange6 == "" {
 		newNode.IngressGatewayRange6 = currentNode.IngressGatewayRange6
 	}
-	if newNode.IsStatic == "" {
-		newNode.IsStatic = currentNode.IsStatic
-	}
-	if newNode.UDPHolePunch == "" {
-		newNode.UDPHolePunch = currentNode.UDPHolePunch
-	}
-	if newNode.DNSOn == "" {
+	if newNode.DNSOn != currentNode.DNSOn {
 		newNode.DNSOn = currentNode.DNSOn
 	}
-	if newNode.IsLocal == "" {
+	if newNode.IsLocal != currentNode.IsLocal {
 		newNode.IsLocal = currentNode.IsLocal
 	}
-	if newNode.IPForwarding == "" {
-		newNode.IPForwarding = currentNode.IPForwarding
-	}
 	if newNode.Action == "" {
 		newNode.Action = currentNode.Action
 	}
-	if newNode.IsServer == "" {
-		newNode.IsServer = currentNode.IsServer
-	}
-	if newNode.IsServer == "yes" {
-		newNode.IsStatic = "yes"
-		newNode.Connected = "yes"
-	}
-	if newNode.MTU == 0 {
-		newNode.MTU = currentNode.MTU
-	}
-	if newNode.OS == "" {
-		newNode.OS = currentNode.OS
-	}
 	if newNode.RelayAddrs == nil {
 		newNode.RelayAddrs = currentNode.RelayAddrs
 	}
-	if newNode.IsRelay == "" {
+	if newNode.IsRelay != currentNode.IsRelay {
 		newNode.IsRelay = currentNode.IsRelay
 	}
-	if newNode.IsRelayed == "" {
+	if newNode.IsRelayed == currentNode.IsRelayed {
 		newNode.IsRelayed = currentNode.IsRelayed
 	}
-	if newNode.IsDocker == "" {
-		newNode.IsDocker = currentNode.IsDocker
-	}
-	if newNode.IsK8S == "" {
-		newNode.IsK8S = currentNode.IsK8S
-	}
-	if newNode.Version == "" {
-		newNode.Version = currentNode.Version
-	}
-	if newNode.IsHub == "" {
-		newNode.IsHub = currentNode.IsHub
-	}
 	if newNode.Server == "" {
 		newNode.Server = currentNode.Server
 	}
-	if newNode.Connected == "" {
+	if newNode.Connected != currentNode.Connected {
 		newNode.Connected = currentNode.Connected
 	}
 	if newNode.DefaultACL == "" {
 		newNode.DefaultACL = currentNode.DefaultACL
 	}
-
-	if newNode.Failover == "" {
+	if newNode.Failover != currentNode.Failover {
 		newNode.Failover = currentNode.Failover
 	}
-
-	newNode.TrafficKeys = currentNode.TrafficKeys
 }
 
 // StringWithCharset - returns random string inside defined charset
@@ -493,7 +463,7 @@ func IsIpv4Net(host string) bool {
 }
 
 // Node.NameInNodeCharset - returns if name is in charset below or not
-func (node *Node) NameInNodeCharSet() bool {
+func (node *LegacyNode) NameInNodeCharSet() bool {
 
 	charset := "abcdefghijklmnopqrstuvwxyz1234567890-"
 
@@ -516,3 +486,147 @@ func (node *Node) DoesACLAllow() bool {
 func (node *Node) DoesACLDeny() bool {
 	return node.DefaultACL == "no"
 }
+
+func (ln *LegacyNode) ConvertToNewNode() (*Host, *Node) {
+	var node Node
+	//host:= logic.GetHost(node.HostID)
+	var host Host
+	if host.ID.String() == "" {
+		host.ID = uuid.New()
+		host.FirewallInUse = ln.FirewallInUse
+		host.Version = ln.Version
+		host.IPForwarding = parseBool(ln.IPForwarding)
+		host.HostPass = ln.Password
+		host.Name = ln.Name
+		host.ListenPort = int(ln.ListenPort)
+		_, cidr, _ := net.ParseCIDR(ln.LocalAddress)
+		_, cidr, _ = net.ParseCIDR(ln.LocalRange)
+		host.LocalRange = *cidr
+		host.LocalListenPort = int(ln.LocalListenPort)
+		host.ProxyListenPort = int(ln.ProxyListenPort)
+		host.MTU = int(ln.MTU)
+		host.PublicKey, _ = wgtypes.ParseKey(ln.PublicKey)
+		host.MacAddress, _ = net.ParseMAC(ln.MacAddress)
+		host.TrafficKeyPublic = ln.TrafficKeys.Mine
+		gateway, _ := net.ResolveUDPAddr("udp", ln.InternetGateway)
+		host.InternetGateway = *gateway
+		id, _ := uuid.Parse(ln.ID)
+		host.Nodes = append(host.Nodes, id.String())
+		host.Interfaces = ln.Interfaces
+		host.EndpointIP = net.ParseIP(ln.Endpoint)
+		// host.ProxyEnabled = ln.Proxy // this will always be false..
+	}
+	id, _ := uuid.Parse(ln.ID)
+	node.ID = id
+	node.Network = ln.Network
+	_, cidr, _ := net.ParseCIDR(ln.NetworkSettings.AddressRange)
+	node.NetworkRange = *cidr
+	_, cidr, _ = net.ParseCIDR(ln.NetworkSettings.AddressRange6)
+	node.NetworkRange6 = *cidr
+	node.Server = ln.Server
+	node.Connected = parseBool(ln.Connected)
+	_, cidr, _ = net.ParseCIDR(ln.Address)
+	node.Address = *cidr
+	_, cidr, _ = net.ParseCIDR(ln.Address6)
+	node.Address6 = *cidr
+	node.PostUp = ln.PostUp
+	node.PostDown = ln.PostDown
+	node.Action = ln.Action
+	node.IsLocal = parseBool(ln.IsLocal)
+	node.IsEgressGateway = parseBool(ln.IsEgressGateway)
+	node.IsIngressGateway = parseBool(ln.IsIngressGateway)
+	node.DNSOn = parseBool(ln.DNSOn)
+
+	return &host, &node
+}
+
+// Node.Legacy converts node to legacy format
+func (n *Node) Legacy(h *Host, s *ServerConfig, net *Network) *LegacyNode {
+	l := LegacyNode{}
+	l.ID = n.ID.String()
+	l.HostID = h.ID.String()
+	l.Address = n.Address.String()
+	l.Address6 = n.Address6.String()
+	l.Interfaces = h.Interfaces
+	l.Name = h.Name
+	l.NetworkSettings = *net
+	l.ListenPort = int32(h.ListenPort)
+	l.LocalListenPort = int32(h.LocalListenPort)
+	l.ProxyListenPort = int32(h.ProxyListenPort)
+	l.PublicKey = h.PublicKey.String()
+	l.Endpoint = h.EndpointIP.String()
+	l.PostUp = n.PostUp
+	l.PostDown = n.PostDown
+	//l.AllowedIPs =
+	l.AccessKey = ""
+	l.Interface = WIREGUARD_INTERFACE
+	//l.LastModified =
+	//l.ExpirationDateTime
+	//l.LastPeerUpdate
+	//l.LastCheckIn
+	l.MacAddress = h.MacAddress.String()
+	l.Password = h.HostPass
+	l.Network = n.Network
+	//l.IsRelayed = formatBool(n.Is)
+	//l.IsRelay = formatBool(n.IsRelay)
+	//l.IsDocker = formatBool(n.IsDocker)
+	//l.IsK8S = formatBool(n.IsK8S)
+	l.IsEgressGateway = formatBool(n.IsEgressGateway)
+	l.IsIngressGateway = formatBool(n.IsIngressGateway)
+	//l.EgressGatewayRanges = n.EgressGatewayRanges
+	//l.EgressGatewayNatEnabled = n.EgressGatewayNatEnabled
+	//l.RelayAddrs = n.RelayAddrs
+	//l.FailoverNode = n.FailoverNode
+	//l.IngressGatewayRange = n.IngressGatewayRange
+	//l.IngressGatewayRange6 = n.IngressGatewayRange6
+	l.IsStatic = formatBool(h.IsStatic)
+	l.UDPHolePunch = formatBool(true)
+	l.DNSOn = formatBool(n.DNSOn)
+	l.Action = n.Action
+	l.IsLocal = formatBool(n.IsLocal)
+	l.LocalRange = h.LocalRange.String()
+	l.IPForwarding = formatBool(h.IPForwarding)
+	l.OS = h.OS
+	l.MTU = int32(h.MTU)
+	l.Version = h.Version
+	l.Server = n.Server
+	l.TrafficKeys.Mine = h.TrafficKeyPublic
+	l.TrafficKeys.Server = s.TrafficKey
+	l.FirewallInUse = h.FirewallInUse
+	l.InternetGateway = h.InternetGateway.String()
+	l.Connected = formatBool(n.Connected)
+	//l.PendingDelete = formatBool(n.PendingDelete)
+	l.Proxy = h.ProxyEnabled
+	l.DefaultACL = n.DefaultACL
+	l.OwnerID = n.OwnerID
+	//l.Failover = n.Failover
+	return &l
+}
+
+// Node.NetworkSettings updates a node with network settings
+func (node *Node) NetworkSettings(n Network) {
+	_, cidr, err := net.ParseCIDR(n.AddressRange)
+	if err == nil {
+		node.NetworkRange = *cidr
+	}
+	_, cidr, err = net.ParseCIDR(n.AddressRange6)
+	if err == nil {
+		node.NetworkRange6 = *cidr
+	}
+}
+
+func parseBool(s string) bool {
+	b := false
+	if s == "yes" {
+		b = true
+	}
+	return b
+}
+
+func formatBool(b bool) string {
+	s := "no"
+	if b {
+		s = "yes"
+	}
+	return s
+}

+ 23 - 3
models/structs.go

@@ -175,8 +175,8 @@ type RelayRequest struct {
 // ServerUpdateData - contains data to configure server
 // and if it should set peers
 type ServerUpdateData struct {
-	UpdatePeers bool `json:"updatepeers" bson:"updatepeers"`
-	Node        Node `json:"servernode" bson:"servernode"`
+	UpdatePeers bool       `json:"updatepeers" bson:"updatepeers"`
+	Node        LegacyNode `json:"servernode" bson:"servernode"`
 }
 
 // Telemetry - contains UUID of the server and timestamp of last send to posthog
@@ -202,12 +202,21 @@ type TrafficKeys struct {
 
 // NodeGet - struct for a single node get response
 type NodeGet struct {
-	Node         Node                 `json:"node" bson:"node" yaml:"node"`
+	Node         LegacyNode           `json:"node" bson:"node" yaml:"node"`
+	Host         Host                 `json:"host" yaml:"host"`
 	Peers        []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"`
 	ServerConfig ServerConfig         `json:"serverconfig" bson:"serverconfig" yaml:"serverconfig"`
 	PeerIDs      PeerMap              `json:"peerids,omitempty" bson:"peerids,omitempty" yaml:"peerids,omitempty"`
 }
 
+// NodeJoinResponse data returned to node in response to join
+type NodeJoinResponse struct {
+	Node         Node         `json:"node" bson:"node" yaml:"node"`
+	Host         Host         `json:"host" yaml:"host"`
+	ServerConfig ServerConfig `json:"serverconfig" bson:"serverconfig" yaml:"serverconfig"`
+	PeerIDs      PeerMap      `json:"peerids,omitempty" bson:"peerids,omitempty" yaml:"peerids,omitempty"`
+}
+
 // ServerConfig - struct for dealing with the server information for a netclient
 type ServerConfig struct {
 	CoreDNSAddr string `yaml:"corednsaddr"`
@@ -218,7 +227,11 @@ type ServerConfig struct {
 	Version     string `yaml:"version"`
 	MQPort      string `yaml:"mqport"`
 	Server      string `yaml:"server"`
+	Broker      string `yaml:"broker"`
 	Is_EE       bool   `yaml:"isee"`
+	StunPort    int    `yaml:"stun_port"`
+	StunHost    string `yaml:"stun_host"`
+	TrafficKey  []byte `yaml:"traffickey"`
 }
 
 // User.NameInCharset - returns if name is in charset below or not
@@ -236,3 +249,10 @@ func (user *User) NameInCharSet() bool {
 type ServerIDs struct {
 	ServerIDs []string `json:"server_ids"`
 }
+
+// JoinData - struct to hold data required for node to join a network on server
+type JoinData struct {
+	Host Host   `json:"host" yaml:"host"`
+	Node Node   `json:"node" yaml:"node"`
+	Key  string `json:"key" yaml:"key"`
+}

+ 2 - 2
mq/dynsec.go

@@ -189,8 +189,8 @@ func Configure() error {
 	return os.WriteFile(path, data, 0755)
 }
 
-// PublishEventToDynSecTopic - publishes the message to dynamic security topic
-func PublishEventToDynSecTopic(payload MqDynsecPayload) error {
+// publishes the message to dynamic security topic
+func publishEventToDynSecTopic(payload MqDynsecPayload) error {
 
 	d, err := json.Marshal(payload)
 	if err != nil {

+ 89 - 0
mq/dynsec_clients.go

@@ -0,0 +1,89 @@
+package mq
+
+// MqClient - type for taking in an MQ client's data
+type MqClient struct {
+	ID       string
+	Text     string
+	Password string
+	Networks []string
+}
+
+// ModifyClient - modifies an existing client's network roles
+func ModifyClient(client *MqClient) error {
+
+	roles := []MqDynSecRole{
+		{
+			Rolename: HostRole,
+			Priority: -1,
+		},
+	}
+
+	for i := range client.Networks {
+		roles = append(roles, MqDynSecRole{
+			Rolename: client.Networks[i],
+			Priority: -1,
+		},
+		)
+	}
+
+	event := MqDynsecPayload{
+		Commands: []MqDynSecCmd{
+			{
+				Command:  ModifyClientCmd,
+				Username: client.ID,
+				Textname: client.Text,
+				Roles:    roles,
+				Groups:   make([]MqDynSecGroup, 0),
+			},
+		},
+	}
+
+	return publishEventToDynSecTopic(event)
+}
+
+// DeleteMqClient - removes a client from the DynSec system
+func DeleteMqClient(hostID string) error {
+	event := MqDynsecPayload{
+		Commands: []MqDynSecCmd{
+			{
+				Command:  DeleteClientCmd,
+				Username: hostID,
+			},
+		},
+	}
+	return publishEventToDynSecTopic(event)
+}
+
+// CreateMqClient - creates an MQ DynSec client
+func CreateMqClient(client *MqClient) error {
+
+	roles := []MqDynSecRole{
+		{
+			Rolename: HostRole,
+			Priority: -1,
+		},
+	}
+
+	for i := range client.Networks {
+		roles = append(roles, MqDynSecRole{
+			Rolename: client.Networks[i],
+			Priority: -1,
+		},
+		)
+	}
+
+	event := MqDynsecPayload{
+		Commands: []MqDynSecCmd{
+			{
+				Command:  CreateClientCmd,
+				Username: client.ID,
+				Password: client.Password,
+				Textname: client.Text,
+				Roles:    roles,
+				Groups:   make([]MqDynSecGroup, 0),
+			},
+		},
+	}
+
+	return publishEventToDynSecTopic(event)
+}

+ 47 - 1
mq/dynsec_helper.go

@@ -19,6 +19,8 @@ const (
 	exporterRole = "exporter"
 	// constant for node role
 	NodeRole = "node"
+	// HostRole constant for host role
+	HostRole = "host"
 
 	// const for dynamic security file
 	dynamicSecurityFile = "dynamic-security.json"
@@ -64,7 +66,7 @@ var (
 				Acls:     fetchServerAcls(),
 			},
 			{
-				Rolename: NodeRole,
+				Rolename: HostRole,
 				Acls:     fetchNodeAcls(),
 			},
 			exporterMQRole,
@@ -182,6 +184,12 @@ func FetchNetworkAcls(network string) []Acl {
 			Priority: -1,
 			Allow:    true,
 		},
+		{
+			AclType:  "publishClientReceive",
+			Topic:    fmt.Sprintf("proxy/%s/#", network),
+			Priority: -1,
+			Allow:    true,
+		},
 		{
 			AclType:  "subscribePattern",
 			Topic:    "#",
@@ -197,6 +205,38 @@ func FetchNetworkAcls(network string) []Acl {
 	}
 }
 
+// DeleteNetworkRole - deletes a network role from DynSec system
+func DeleteNetworkRole(network string) error {
+	// Deletes the network role from MQ
+	event := MqDynsecPayload{
+		Commands: []MqDynSecCmd{
+			{
+				Command:  DeleteRoleCmd,
+				RoleName: network,
+			},
+		},
+	}
+
+	return publishEventToDynSecTopic(event)
+}
+
+// CreateNetworkRole - createss a network role from DynSec system
+func CreateNetworkRole(network string) error {
+	// Create Role with acls for the network
+	event := MqDynsecPayload{
+		Commands: []MqDynSecCmd{
+			{
+				Command:  CreateRoleCmd,
+				RoleName: network,
+				Textname: "Network wide role with Acls for nodes",
+				Acls:     FetchNetworkAcls(network),
+			},
+		},
+	}
+
+	return publishEventToDynSecTopic(event)
+}
+
 // serverAcls - fetches server role related acls
 func fetchServerAcls() []Acl {
 	return []Acl{
@@ -206,6 +246,12 @@ func fetchServerAcls() []Acl {
 			Priority: -1,
 			Allow:    true,
 		},
+		{
+			AclType:  "publishClientSend",
+			Topic:    "proxy/#",
+			Priority: -1,
+			Allow:    true,
+		},
 		{
 			AclType:  "publishClientSend",
 			Topic:    "update/#",

+ 46 - 47
mq/handlers.go

@@ -41,7 +41,7 @@ func Ping(client mqtt.Client, msg mqtt.Message) {
 		}
 		decrypted, decryptErr := decryptMsg(&node, msg.Payload())
 		if decryptErr != nil {
-			logger.Log(0, "error decrypting when updating node ", node.ID, decryptErr.Error())
+			logger.Log(0, "error decrypting when updating node ", node.ID.String(), decryptErr.Error())
 			return
 		}
 		var checkin models.NodeCheckin
@@ -49,16 +49,24 @@ func Ping(client mqtt.Client, msg mqtt.Message) {
 			logger.Log(1, "error unmarshaling payload ", err.Error())
 			return
 		}
+		host, err := logic.GetHost(node.HostID.String())
+		if err != nil {
+			logger.Log(0, "error retrieving host for node ", node.ID.String(), err.Error())
+			return
+		}
 		node.SetLastCheckIn()
-		node.Version = checkin.Version
+		host.Version = checkin.Version
 		node.Connected = checkin.Connected
-		node.Interfaces = checkin.Ifaces
+		host.Interfaces = checkin.Ifaces
+		for i := range host.Interfaces {
+			host.Interfaces[i].AddressString = host.Interfaces[i].Address.String()
+		}
 		if err := logic.UpdateNode(&node, &node); err != nil {
-			logger.Log(0, "error updating node", node.Name, node.ID, " on checkin", err.Error())
+			logger.Log(0, "error updating node", node.ID.String(), " on checkin", err.Error())
 			return
 		}
 
-		logger.Log(3, "ping processed for node", node.Name, node.ID)
+		logger.Log(3, "ping processed for node", node.ID.String())
 		// --TODO --set client version once feature is implemented.
 		//node.SetClientVersion(msg.Payload())
 	}()
@@ -89,8 +97,8 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
 		}
 		ifaceDelta := logic.IfaceDelta(&currentNode, &newNode)
 		if servercfg.Is_EE && ifaceDelta {
-			if err = logic.EnterpriseResetAllPeersFailovers(currentNode.ID, currentNode.Network); err != nil {
-				logger.Log(1, "failed to reset failover list during node update", currentNode.Name, currentNode.Network)
+			if err = logic.EnterpriseResetAllPeersFailovers(currentNode.ID.String(), currentNode.Network); err != nil {
+				logger.Log(1, "failed to reset failover list during node update", currentNode.ID.String(), currentNode.Network)
 			}
 		}
 		newNode.SetLastCheckIn()
@@ -99,11 +107,13 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
 			return
 		}
 		if ifaceDelta { // reduce number of unneeded updates, by only sending on iface changes
-			if err = PublishPeerUpdate(&currentNode, true); err != nil {
-				logger.Log(0, "error updating peers when node", currentNode.Name, currentNode.ID, "informed the server of an interface change", err.Error())
+			if err = PublishPeerUpdate(currentNode.Network, true); err != nil {
+				logger.Log(0, "error updating peers when node", currentNode.ID.String(), "informed the server of an interface change", err.Error())
 			}
 		}
-		logger.Log(1, "updated node", id, newNode.Name)
+
+		logger.Log(1, "updated node", id, newNode.ID.String())
+
 	}()
 }
 
@@ -136,31 +146,31 @@ func UpdateMetrics(client mqtt.Client, msg mqtt.Message) {
 			shouldUpdate := updateNodeMetrics(&currentNode, &newMetrics)
 
 			if err = logic.UpdateMetrics(id, &newMetrics); err != nil {
-				logger.Log(1, "faield to update node metrics", id, currentNode.Name, err.Error())
+				logger.Log(1, "faield to update node metrics", id, err.Error())
 				return
 			}
 			if servercfg.IsMetricsExporter() {
 				if err := pushMetricsToExporter(newMetrics); err != nil {
 					logger.Log(2, fmt.Sprintf("failed to push node: [%s] metrics to exporter, err: %v",
-						currentNode.Name, err))
+						currentNode.ID, err))
 				}
 			}
 
 			if newMetrics.Connectivity != nil {
 				err := logic.EnterpriseFailoverFunc(&currentNode)
 				if err != nil {
-					logger.Log(0, "failed to failover for node", currentNode.Name, "on network", currentNode.Network, "-", err.Error())
+					logger.Log(0, "failed to failover for node", currentNode.ID.String(), "on network", currentNode.Network, "-", err.Error())
 				}
 			}
 
 			if shouldUpdate {
-				logger.Log(2, "updating peers after node", currentNode.Name, currentNode.Network, "detected connectivity issues")
+				logger.Log(2, "updating peers after node", currentNode.ID.String(), currentNode.Network, "detected connectivity issues")
 				if err = PublishSinglePeerUpdate(&currentNode); err != nil {
-					logger.Log(0, "failed to publish update after failover peer change for node", currentNode.Name, currentNode.Network)
+					logger.Log(0, "failed to publish update after failover peer change for node", currentNode.ID.String(), currentNode.Network)
 				}
 			}
 
-			logger.Log(1, "updated node metrics", id, currentNode.Name)
+			logger.Log(1, "updated node metrics", id)
 		}()
 	}
 }
@@ -185,47 +195,29 @@ func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
 		}
 		switch decrypted[0] {
 		case ncutils.ACK:
-			currentServerNode, err := logic.GetNetworkServerLocal(currentNode.Network)
-			if err != nil {
-				return
-			}
-			if err := logic.ServerUpdate(&currentServerNode, false); err != nil {
-				logger.Log(1, "server node:", currentServerNode.ID, "failed update")
-				return
-			}
+			//do we still need this
 		case ncutils.DONE:
 			updateNodePeers(&currentNode)
 		}
 
-		logger.Log(1, "sent peer updates after signal received from", id, currentNode.Name)
+		logger.Log(1, "sent peer updates after signal received from", id)
 	}()
 }
 
 func updateNodePeers(currentNode *models.Node) {
-	currentServerNode, err := logic.GetNetworkServerLocal(currentNode.Network)
-	if err != nil {
-		logger.Log(1, "failed to get server node failed update\n", err.Error())
+	if err := PublishPeerUpdate(currentNode.Network, false); err != nil {
+		logger.Log(1, "error publishing peer update ", err.Error())
 		return
 	}
-	if err := logic.ServerUpdate(&currentServerNode, false); err != nil {
-		logger.Log(1, "server node:", currentServerNode.ID, "failed update")
-		return
-	}
-	if logic.IsLeader(&currentServerNode) {
-		if err := PublishPeerUpdate(currentNode, false); err != nil {
-			logger.Log(1, "error publishing peer update ", err.Error())
-			return
-		}
-	}
 }
 
 func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) bool {
 	if newMetrics.FailoverPeers == nil {
 		newMetrics.FailoverPeers = make(map[string]string)
 	}
-	oldMetrics, err := logic.GetMetrics(currentNode.ID)
+	oldMetrics, err := logic.GetMetrics(currentNode.ID.String())
 	if err != nil {
-		logger.Log(1, "error finding old metrics for node", currentNode.ID, currentNode.Name)
+		logger.Log(1, "error finding old metrics for node", currentNode.ID.String())
 		return false
 	}
 	if oldMetrics.FailoverPeers == nil {
@@ -233,8 +225,8 @@ func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) boo
 	}
 
 	var attachedClients []models.ExtClient
-	if currentNode.IsIngressGateway == "yes" {
-		clients, err := logic.GetExtClientsByID(currentNode.ID, currentNode.Network)
+	if currentNode.IsIngressGateway {
+		clients, err := logic.GetExtClientsByID(currentNode.ID.String(), currentNode.Network)
 		if err == nil {
 			attachedClients = clients
 		}
@@ -272,6 +264,13 @@ func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) boo
 		currMetric.ActualUptime = time.Duration(totalUpMinutes) * time.Minute
 		delete(oldMetrics.Connectivity, k) // remove from old data
 		newMetrics.Connectivity[k] = currMetric
+		if oldProxyMetric, ok := oldMetrics.ProxyMetrics[k]; ok {
+			newProxyMetric := newMetrics.ProxyMetrics[k]
+			newProxyMetric.TrafficSent += oldProxyMetric.TrafficSent
+			newProxyMetric.TrafficRecieved += oldProxyMetric.TrafficRecieved
+			newMetrics.ProxyMetrics[k] = newProxyMetric
+		}
+
 	}
 
 	// add nodes that need failover
@@ -281,12 +280,12 @@ func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) boo
 		return false
 	}
 	for _, node := range nodes {
-		if !newMetrics.Connectivity[node.ID].Connected &&
-			len(newMetrics.Connectivity[node.ID].NodeName) > 0 &&
-			node.Connected == "yes" &&
+		if !newMetrics.Connectivity[node.ID.String()].Connected &&
+			len(newMetrics.Connectivity[node.ID.String()].NodeName) > 0 &&
+			node.Connected == true &&
 			len(node.FailoverNode) > 0 &&
-			node.Failover != "yes" {
-			newMetrics.FailoverPeers[node.ID] = node.FailoverNode
+			!node.Failover {
+			newMetrics.FailoverPeers[node.ID.String()] = node.FailoverNode.String()
 		}
 	}
 	shouldUpdate := len(oldMetrics.FailoverPeers) == 0 && len(newMetrics.FailoverPeers) > 0

+ 135 - 101
mq/publishers.go

@@ -6,47 +6,68 @@ import (
 	"fmt"
 	"time"
 
+	proxy_models "github.com/gravitl/netclient/nmproxy/models"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/logic/metrics"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/serverctl"
 )
 
 // PublishPeerUpdate --- deterines and publishes a peer update to all the peers of a node
-func PublishPeerUpdate(newNode *models.Node, publishToSelf bool) error {
+func PublishPeerUpdate(network string, publishToSelf bool) error {
 	if !servercfg.IsMessageQueueBackend() {
 		return nil
 	}
-	networkNodes, err := logic.GetNetworkNodes(newNode.Network)
+	networkNodes, err := logic.GetNetworkNodes(network)
 	if err != nil {
 		logger.Log(1, "err getting Network Nodes", err.Error())
 		return err
 	}
 	for _, node := range networkNodes {
-
-		if node.IsServer == "yes" {
-			continue
-		}
-		if !publishToSelf && newNode.ID == node.ID {
-			//skip self
-			continue
-		}
 		err = PublishSinglePeerUpdate(&node)
 		if err != nil {
-			logger.Log(1, "failed to publish peer update to node", node.Name, "on network", node.Network, ":", err.Error())
+			logger.Log(1, "failed to publish peer update to node", node.ID.String(), "on network", node.Network, ":", err.Error())
 		}
 	}
 	return err
 }
 
+func PublishProxyPeerUpdate(node *models.Node) error {
+	proxyUpdate, err := logic.GetPeersForProxy(node, false)
+	if err != nil {
+		return err
+	}
+	proxyUpdate.Action = proxy_models.AddNetwork
+	err = ProxyUpdate(&proxyUpdate, node)
+	if err != nil {
+		logger.Log(1, "failed to send proxy update: ", err.Error())
+		return err
+	}
+	return nil
+}
+
 // PublishSinglePeerUpdate --- determines and publishes a peer update to one node
 func PublishSinglePeerUpdate(node *models.Node) error {
-	peerUpdate, err := logic.GetPeerUpdate(node)
+	host, err := logic.GetHost(node.HostID.String())
+	if err != nil {
+		return nil
+	}
+
+	peerUpdate, err := logic.GetPeerUpdate(node, host)
 	if err != nil {
 		return err
 	}
+	if host.ProxyEnabled {
+		proxyUpdate, err := logic.GetPeersForProxy(node, false)
+		if err != nil {
+			return err
+		}
+		proxyUpdate.Action = proxy_models.AddNetwork
+		peerUpdate.ProxyUpdate = proxyUpdate
+
+	}
+
 	data, err := json.Marshal(&peerUpdate)
 	if err != nil {
 		return err
@@ -56,19 +77,14 @@ func PublishSinglePeerUpdate(node *models.Node) error {
 
 // PublishPeerUpdate --- publishes a peer update to all the peers of a node
 func PublishExtPeerUpdate(node *models.Node) error {
-	var err error
-	if logic.IsLocalServer(node) {
-		if err = logic.ServerUpdate(node, false); err != nil {
-			logger.Log(1, "server node:", node.ID, "failed to update peers with ext clients")
-			return err
-		} else {
-			return nil
-		}
+	host, err := logic.GetHost(node.HostID.String())
+	if err != nil {
+		return nil
 	}
 	if !servercfg.IsMessageQueueBackend() {
 		return nil
 	}
-	peerUpdate, err := logic.GetPeerUpdate(node)
+	peerUpdate, err := logic.GetPeerUpdate(node, host)
 	if err != nil {
 		return err
 	}
@@ -76,23 +92,34 @@ func PublishExtPeerUpdate(node *models.Node) error {
 	if err != nil {
 		return err
 	}
+	if host.ProxyEnabled {
+		proxyUpdate, err := logic.GetPeersForProxy(node, false)
+		if err == nil {
+			peerUpdate.ProxyUpdate = proxyUpdate
+		}
+	}
+
 	if err = publish(node, fmt.Sprintf("peers/%s/%s", node.Network, node.ID), data); err != nil {
 		return err
 	}
-	go PublishPeerUpdate(node, false)
+	go PublishPeerUpdate(node.Network, false)
 	return nil
 }
 
 // NodeUpdate -- publishes a node update
 func NodeUpdate(node *models.Node) error {
-	if !servercfg.IsMessageQueueBackend() || node.IsServer == "yes" {
+	host, err := logic.GetHost(node.HostID.String())
+	if err != nil {
 		return nil
 	}
-	logger.Log(3, "publishing node update to "+node.Name)
-
-	if len(node.NetworkSettings.AccessKeys) > 0 {
-		node.NetworkSettings.AccessKeys = []models.AccessKey{} // not to be sent (don't need to spread access keys around the network; we need to know how to reach other nodes, not become them)
+	if !servercfg.IsMessageQueueBackend() {
+		return nil
 	}
+	logger.Log(3, "publishing node update to "+node.ID.String())
+
+	//if len(node.NetworkSettings.AccessKeys) > 0 {
+	//node.NetworkSettings.AccessKeys = []models.AccessKey{} // not to be sent (don't need to spread access keys around the network; we need to know how to reach other nodes, not become them)
+	//}
 
 	data, err := json.Marshal(node)
 	if err != nil {
@@ -100,7 +127,37 @@ func NodeUpdate(node *models.Node) error {
 		return err
 	}
 	if err = publish(node, fmt.Sprintf("update/%s/%s", node.Network, node.ID), data); err != nil {
-		logger.Log(2, "error publishing node update to peer ", node.ID, err.Error())
+		logger.Log(2, "error publishing node update to peer ", node.ID.String(), err.Error())
+		return err
+	}
+	if host.ProxyEnabled {
+		err = PublishProxyPeerUpdate(node)
+		if err != nil {
+			logger.Log(1, "failed to publish proxy update to node", node.ID.String(), "on network", node.Network, ":", err.Error())
+		}
+	}
+
+	return nil
+}
+
+// ProxyUpdate -- publishes updates to peers related to proxy
+func ProxyUpdate(proxyPayload *proxy_models.ProxyManagerPayload, node *models.Node) error {
+	host, err := logic.GetHost(node.HostID.String())
+	if err != nil {
+		return nil
+	}
+	if !servercfg.IsMessageQueueBackend() || !host.ProxyEnabled {
+		return nil
+	}
+	logger.Log(3, "publishing proxy update to "+node.ID.String())
+
+	data, err := json.Marshal(proxyPayload)
+	if err != nil {
+		logger.Log(2, "error marshalling node update ", err.Error())
+		return err
+	}
+	if err = publish(node, fmt.Sprintf("proxy/%s/%s", node.Network, node.ID), data); err != nil {
+		logger.Log(2, "error publishing proxy update to peer ", node.ID.String(), err.Error())
 		return err
 	}
 	return nil
@@ -131,39 +188,16 @@ func sendPeers() {
 			logger.Log(3, "error occurred on timer,", err.Error())
 		}
 
-		collectServerMetrics(networks[:])
+		//collectServerMetrics(networks[:])
 	}
 
 	for _, network := range networks {
-		serverNode, errN := logic.GetNetworkServerLocal(network.NetID)
-		if errN == nil {
-			serverNode.SetLastCheckIn()
-			if err := logic.UpdateNode(&serverNode, &serverNode); err != nil {
-				logger.Log(0, "failed checkin for server node", serverNode.Name, "on network", network.NetID, err.Error())
-			}
-		}
-		isLeader := logic.IsLeader(&serverNode)
-		if errN == nil && isLeader {
-			if network.DefaultUDPHolePunch == "yes" {
-				if logic.ShouldPublishPeerPorts(&serverNode) || force {
-					if force {
-						logger.Log(2, "sending scheduled peer update (5 min)")
-					}
-					err = PublishPeerUpdate(&serverNode, false)
-					if err != nil {
-						logger.Log(1, "error publishing udp port updates for network", network.NetID)
-						logger.Log(1, errN.Error())
-					}
-				}
-			}
-		} else {
-			if isLeader {
-				logger.Log(1, "unable to retrieve leader for network ", network.NetID)
-			}
-			logger.Log(2, "server checkin complete for server", serverNode.Name, "on network", network.NetID)
-			serverctl.SyncServerNetwork(network.NetID)
-			if errN != nil {
-				logger.Log(1, errN.Error())
+		if force {
+			logger.Log(2, "sending scheduled peer update (5 min)")
+			err = PublishPeerUpdate(network.NetID, false)
+			if err != nil {
+				logger.Log(1, "error publishing udp port updates for network", network.NetID)
+				logger.Log(1, err.Error())
 			}
 		}
 	}
@@ -178,54 +212,54 @@ func ServerStartNotify() error {
 	for i := range nodes {
 		nodes[i].Action = models.NODE_FORCE_UPDATE
 		if err = NodeUpdate(&nodes[i]); err != nil {
-			logger.Log(1, "error when notifying node", nodes[i].Name, " - ", nodes[i].ID, "of a server startup")
+			logger.Log(1, "error when notifying node", nodes[i].ID.String(), "of a server startup")
 		}
 	}
 	return nil
 }
 
 // function to collect and store metrics for server nodes
-func collectServerMetrics(networks []models.Network) {
-	if !servercfg.Is_EE {
-		return
-	}
-	if len(networks) > 0 {
-		for i := range networks {
-			currentNetworkNodes, err := logic.GetNetworkNodes(networks[i].NetID)
-			if err != nil {
-				continue
-			}
-			currentServerNodes := logic.GetServerNodes(networks[i].NetID)
-			if len(currentServerNodes) > 0 {
-				for i := range currentServerNodes {
-					if logic.IsLocalServer(&currentServerNodes[i]) {
-						serverMetrics := logic.CollectServerMetrics(currentServerNodes[i].ID, currentNetworkNodes)
-						if serverMetrics != nil {
-							serverMetrics.NodeName = currentServerNodes[i].Name
-							serverMetrics.NodeID = currentServerNodes[i].ID
-							serverMetrics.IsServer = "yes"
-							serverMetrics.Network = currentServerNodes[i].Network
-							if err = metrics.GetExchangedBytesForNode(&currentServerNodes[i], serverMetrics); err != nil {
-								logger.Log(1, fmt.Sprintf("failed to update exchanged bytes info for server: %s, err: %v",
-									currentServerNodes[i].Name, err))
-							}
-							updateNodeMetrics(&currentServerNodes[i], serverMetrics)
-							if err = logic.UpdateMetrics(currentServerNodes[i].ID, serverMetrics); err != nil {
-								logger.Log(1, "failed to update metrics for server node", currentServerNodes[i].ID)
-							}
-							if servercfg.IsMetricsExporter() {
-								logger.Log(2, "-------------> SERVER METRICS: ", fmt.Sprintf("%+v", serverMetrics))
-								if err := pushMetricsToExporter(*serverMetrics); err != nil {
-									logger.Log(2, "failed to push server metrics to exporter: ", err.Error())
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-}
+//func collectServerMetrics(networks []models.Network) {
+//	if !servercfg.Is_EE {
+//		return
+//	}
+//	if len(networks) > 0 {
+//		for i := range networks {
+//			currentNetworkNodes, err := logic.GetNetworkNodes(networks[i].NetID)
+//			if err != nil {
+//				continue
+//			}
+//			currentServerNodes := logic.GetServerNodes(networks[i].NetID)
+//			if len(currentServerNodes) > 0 {
+//				for i := range currentServerNodes {
+//					if logic.IsLocalServer(&currentServerNodes[i]) {
+//						serverMetrics := logic.CollectServerMetrics(currentServerNodes[i].ID, currentNetworkNodes)
+//						if serverMetrics != nil {
+//							serverMetrics.NodeName = currentServerNodes[i].Name
+//							serverMetrics.NodeID = currentServerNodes[i].ID
+//							serverMetrics.IsServer = "yes"
+//							serverMetrics.Network = currentServerNodes[i].Network
+//							if err = metrics.GetExchangedBytesForNode(&currentServerNodes[i], serverMetrics); err != nil {
+//								logger.Log(1, fmt.Sprintf("failed to update exchanged bytes info for server: %s, err: %v",
+//									currentServerNodes[i].Name, err))
+//							}
+//							updateNodeMetrics(&currentServerNodes[i], serverMetrics)
+//							if err = logic.UpdateMetrics(currentServerNodes[i].ID, serverMetrics); err != nil {
+//								logger.Log(1, "failed to update metrics for server node", currentServerNodes[i].ID)
+//							}
+//							if servercfg.IsMetricsExporter() {
+//								logger.Log(2, "-------------> SERVER METRICS: ", fmt.Sprintf("%+v", serverMetrics))
+//								if err := pushMetricsToExporter(*serverMetrics); err != nil {
+//									logger.Log(2, "failed to push server metrics to exporter: ", err.Error())
+//								}
+//							}
+//						}
+//					}
+//				}
+//			}
+//		}
+//	}
+//}
 
 func pushMetricsToExporter(metrics models.Metrics) error {
 	logger.Log(2, "----> Pushing metrics to exporter")

+ 12 - 4
mq/util.go

@@ -15,6 +15,10 @@ func decryptMsg(node *models.Node, msg []byte) ([]byte, error) {
 	if len(msg) <= 24 { // make sure message is of appropriate length
 		return nil, fmt.Errorf("recieved invalid message from broker %v", msg)
 	}
+	host, err := logic.GetHost(node.HostID.String())
+	if err != nil {
+		return nil, err
+	}
 
 	trafficKey, trafficErr := logic.RetrievePrivateTrafficKey() // get server private key
 	if trafficErr != nil {
@@ -24,12 +28,12 @@ func decryptMsg(node *models.Node, msg []byte) ([]byte, error) {
 	if err != nil {
 		return nil, err
 	}
-	nodePubTKey, err := ncutils.ConvertBytesToKey(node.TrafficKeys.Mine)
+	nodePubTKey, err := ncutils.ConvertBytesToKey(host.TrafficKeyPublic)
 	if err != nil {
 		return nil, err
 	}
 
-	if strings.Contains(node.Version, "0.10.0") {
+	if strings.Contains(host.Version, "0.10.0") {
 		return ncutils.BoxDecrypt(msg, nodePubTKey, serverPrivTKey)
 	}
 
@@ -48,12 +52,16 @@ func encryptMsg(node *models.Node, msg []byte) ([]byte, error) {
 		return nil, err
 	}
 
-	nodePubKey, err := ncutils.ConvertBytesToKey(node.TrafficKeys.Mine)
+	host, err := logic.GetHost(node.HostID.String())
+	if err != nil {
+		return nil, err
+	}
+	nodePubKey, err := ncutils.ConvertBytesToKey(host.TrafficKeyPublic)
 	if err != nil {
 		return nil, err
 	}
 
-	if strings.Contains(node.Version, "0.10.0") {
+	if strings.Contains(host.Version, "0.10.0") {
 		return ncutils.BoxEncrypt(msg, nodePubKey, serverPrivKey)
 	}
 

+ 0 - 9
netclient/Dockerfile

@@ -1,9 +0,0 @@
-FROM debian:latest
-
-RUN apt-get update && apt-get -y install systemd procps
-
-WORKDIR /root/
-
-COPY netclient .
-
-CMD ["./netclient checkin"]

+ 0 - 44
netclient/auth/auth.go

@@ -1,44 +0,0 @@
-package auth
-
-import (
-	"os"
-
-	"github.com/gravitl/netmaker/netclient/ncutils"
-	//    "os"
-)
-
-// StoreSecret - stores auth secret locally
-func StoreSecret(key string, network string) error {
-	d1 := []byte(key)
-	return os.WriteFile(ncutils.GetNetclientPathSpecific()+"secret-"+network, d1, 0600)
-}
-
-// RetrieveSecret - fetches secret locally
-func RetrieveSecret(network string) (string, error) {
-	dat, err := ncutils.GetFileWithRetry(ncutils.GetNetclientPathSpecific()+"secret-"+network, 3)
-	return string(dat), err
-}
-
-// StoreTrafficKey - stores traffic key
-func StoreTrafficKey(key *[32]byte, network string) error {
-	var data, err = ncutils.ConvertKeyToBytes(key)
-	if err != nil {
-		return err
-	}
-	return os.WriteFile(ncutils.GetNetclientPathSpecific()+"traffic-"+network, data, 0600)
-}
-
-// RetrieveTrafficKey - reads traffic file locally
-func RetrieveTrafficKey(network string) (*[32]byte, error) {
-	data, err := ncutils.GetFileWithRetry(ncutils.GetNetclientPathSpecific()+"traffic-"+network, 2)
-	if err != nil {
-		return nil, err
-	}
-	return ncutils.ConvertBytesToKey(data)
-}
-
-// Configuraion - struct for mac and pass
-type Configuration struct {
-	MacAddress string
-	Password   string
-}

+ 0 - 42
netclient/bin-maker.sh

@@ -1,42 +0,0 @@
-#!/bin/bash
-VERSION=${VERSION:-"develop"}
-echo "build with version tag: $VERSION"
-readonly __HOST_ARCH=${1:-"amd64"}  # change this for your machine.
-readonly __HOST_GOOSE=${2:-"linux"} # change this for your machine.
-readonly __EXEC_DIR=$(dirname "$(realpath $0)") && cd $__EXEC_DIR   
-
-__darwin=( arm64 amd64 )
-__linux=( amd64 arm arm64 mips mips64 mips64le mipsle ppc64 ppc64le riscv64 s390x 386 )
-__freebsd=( amd64 arm arm64 386 )
-__windows=( amd64 arm arm64 386 ) 
-
-function build
-{   
-    local _goarch=${1:-"None"} && if [[ $_goarch == "None" ]]; then exit 1; fi
-    local _goose="${2:-"None"}" && if [[ $_goose == "None" ]]; then exit 1; fi
-    local _goarm=${3:-""}
-    local _out=build/netclient-$_goose-$_goarch$_goarm && mkdir -p build
-    if [ "$_goarch" == "arm" ] && [ "$_goarm" == "" ]; then
-	    build $_goarch $_goose 5 && build $_goarch $_goose 6 && build $_goarch $_goose 7
-    else
-        
-        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
-}
-
-for arch in ${__linux[*]}; do build "$arch" "linux"; done
-
-for arch in ${__freebsd[*]}; do build "$arch" "freebsd"; done
-
-for arch in ${__darwin[*]}; do build "$arch" "darwin"; done
-
-for arch in ${__windows[*]}; do build "$arch" "windows"; done

+ 0 - 15
netclient/build/netclient.service

@@ -1,15 +0,0 @@
-[Unit]
-Description=Netclient Daemon
-Documentation=https://docs.netmaker.org https://k8s.netmaker.org
-After=network-online.target
-Wants=network-online.target 
-
-[Service]
-User=root
-Type=simple
-ExecStart=/sbin/netclient daemon
-Restart=on-failure
-RestartSec=15s
-
-[Install]
-WantedBy=multi-user.target

+ 0 - 148
netclient/cli_options/cmds.go

@@ -1,148 +0,0 @@
-package cli_options
-
-import (
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/netclient/command"
-	"github.com/gravitl/netmaker/netclient/config"
-	"github.com/urfave/cli/v2"
-)
-
-// GetCommands - return commands that CLI uses
-func GetCommands(cliFlags []cli.Flag) []*cli.Command {
-	return []*cli.Command{
-		{
-			Name:  "join",
-			Usage: "Join a Netmaker network.",
-			Flags: cliFlags,
-			Action: func(c *cli.Context) error {
-				parseVerbosity(c)
-				cfg, pvtKey, err := config.GetCLIConfig(c)
-				if err != nil {
-					return err
-				}
-				err = command.Join(&cfg, pvtKey)
-				return err
-			},
-		},
-		{
-			Name:  "leave",
-			Usage: "Leave a Netmaker network.",
-			Flags: cliFlags,
-			// the action, or code that will be executed when
-			// we execute our `ns` command
-			Action: func(c *cli.Context) error {
-				parseVerbosity(c)
-				cfg, _, err := config.GetCLIConfig(c)
-				if err != nil {
-					return err
-				}
-				err = command.Leave(&cfg)
-				return err
-			},
-		},
-		{
-			Name:  "pull",
-			Usage: "Pull latest configuration and peers from server.",
-			Flags: cliFlags,
-			// the action, or code that will be executed when
-			// we execute our `ns` command
-			Action: func(c *cli.Context) error {
-				parseVerbosity(c)
-				cfg, _, err := config.GetCLIConfig(c)
-				if err != nil {
-					return err
-				}
-				err = command.Pull(&cfg)
-				return err
-			},
-		},
-		{
-			Name:  "list",
-			Usage: "Get list of networks.",
-			Flags: cliFlags,
-			// the action, or code that will be executed when
-			// we execute our `ns` command
-			Action: func(c *cli.Context) error {
-				parseVerbosity(c)
-				cfg, _, err := config.GetCLIConfig(c)
-				if err != nil {
-					return err
-				}
-				err = command.List(cfg)
-				return err
-			},
-		},
-		{
-			Name:  "uninstall",
-			Usage: "Uninstall the netclient system service.",
-			Flags: cliFlags,
-			// the action, or code that will be executed when
-			// we execute our `ns` command
-			Action: func(c *cli.Context) error {
-				parseVerbosity(c)
-				err := command.Uninstall()
-				return err
-			},
-		},
-		{
-			Name:  "daemon",
-			Usage: "run netclient as daemon",
-			Flags: cliFlags,
-			Action: func(c *cli.Context) error {
-				// set max verbosity for daemon regardless
-				logger.Verbosity = 4
-				err := command.Daemon()
-				return err
-			},
-		},
-		{
-			Name:  "install",
-			Usage: "install binary and daemon",
-			Flags: cliFlags,
-			Action: func(c *cli.Context) error {
-				parseVerbosity(c)
-				return command.Install()
-			},
-		},
-		{
-			Name:  "connect",
-			Usage: "connect netclient to a given network if disconnected",
-			Flags: cliFlags,
-			Action: func(c *cli.Context) error {
-				parseVerbosity(c)
-				cfg, _, err := config.GetCLIConfig(c)
-				if err != nil {
-					return err
-				}
-				return command.Connect(cfg)
-			},
-		},
-		{
-			Name:  "disconnect",
-			Usage: "disconnect netclient from a given network if connected",
-			Flags: cliFlags,
-			Action: func(c *cli.Context) error {
-				parseVerbosity(c)
-				cfg, _, err := config.GetCLIConfig(c)
-				if err != nil {
-					return err
-				}
-				return command.Disconnect(cfg)
-			},
-		},
-	}
-}
-
-// == Private funcs ==
-
-func parseVerbosity(c *cli.Context) {
-	if c.Bool("v") {
-		logger.Verbosity = 1
-	} else if c.Bool("vv") {
-		logger.Verbosity = 2
-	} else if c.Bool("vvv") {
-		logger.Verbosity = 3
-	} else if c.Bool("vvvv") {
-		logger.Verbosity = 4
-	}
-}

+ 0 - 229
netclient/cli_options/flags.go

@@ -1,229 +0,0 @@
-package cli_options
-
-import "github.com/urfave/cli/v2"
-
-// GetFlags - Returns the flags used by cli
-func GetFlags(hostname string) []cli.Flag {
-	return []cli.Flag{
-		&cli.StringFlag{
-			Name:    "network",
-			Aliases: []string{"n"},
-			EnvVars: []string{"NETCLIENT_NETWORK"},
-			Value:   "all",
-			Usage:   "Network to perform specified action against.",
-		},
-		&cli.StringFlag{
-			Name:    "password",
-			Aliases: []string{"p"},
-			EnvVars: []string{"NETCLIENT_PASSWORD"},
-			Value:   "",
-			Usage:   "Password for authenticating with netmaker.",
-		},
-		&cli.StringFlag{
-			Name:    "endpoint",
-			Aliases: []string{"e"},
-			EnvVars: []string{"NETCLIENT_ENDPOINT"},
-			Value:   "",
-			Usage:   "Reachable (usually public) address for WireGuard (not the private WG address).",
-		},
-		&cli.StringFlag{
-			Name:    "macaddress",
-			Aliases: []string{"m"},
-			EnvVars: []string{"NETCLIENT_MACADDRESS"},
-			Value:   "",
-			Usage:   "Mac Address for this machine. Used as a unique identifier within Netmaker network.",
-		},
-		&cli.StringFlag{
-			Name:    "publickey",
-			Aliases: []string{"pubkey"},
-			EnvVars: []string{"NETCLIENT_PUBLICKEY"},
-			Value:   "",
-			Usage:   "Public Key for WireGuard Interface.",
-		},
-		&cli.StringFlag{
-			Name:    "privatekey",
-			Aliases: []string{"privkey"},
-			EnvVars: []string{"NETCLIENT_PRIVATEKEY"},
-			Value:   "",
-			Usage:   "Private Key for WireGuard Interface.",
-		},
-		&cli.StringFlag{
-			Name:    "port",
-			EnvVars: []string{"NETCLIENT_PORT"},
-			Value:   "",
-			Usage:   "Port for WireGuard Interface.",
-		},
-		&cli.IntFlag{
-			Name:    "keepalive",
-			EnvVars: []string{"NETCLIENT_KEEPALIVE"},
-			Value:   0,
-			Usage:   "Default PersistentKeepAlive for Peers in WireGuard Interface.",
-		},
-		&cli.StringFlag{
-			Name:    "operatingsystem",
-			Aliases: []string{"os"},
-			EnvVars: []string{"NETCLIENT_OS"},
-			Value:   "",
-			Usage:   "Operating system of machine (linux, darwin, windows, freebsd).",
-		},
-		&cli.StringFlag{
-			Name:    "publicipservice",
-			Aliases: []string{"ip-service"},
-			EnvVars: []string{"NETCLIENT_IP_SERVICE"},
-			Value:   "",
-			Usage:   "The service to call to obtain the public IP of the machine that is running netclient.",
-		},
-		&cli.StringFlag{
-			Name:    "name",
-			EnvVars: []string{"NETCLIENT_NAME"},
-			Value:   hostname,
-			Usage:   "Identifiable name for machine within Netmaker network.",
-		},
-		&cli.StringFlag{
-			Name:    "localaddress",
-			EnvVars: []string{"NETCLIENT_LOCALADDRESS"},
-			Value:   "",
-			Usage:   "Local address for machine. Can be used in place of Endpoint for machines on the same LAN.",
-		},
-		&cli.StringFlag{
-			Name:    "isstatic",
-			Aliases: []string{"st"},
-			EnvVars: []string{"NETCLIENT_IS_STATIC"},
-			Value:   "",
-			Usage:   "Indicates if client is static by default (will not change addresses automatically).",
-		},
-		&cli.StringFlag{
-			Name:    "address",
-			Aliases: []string{"a"},
-			EnvVars: []string{"NETCLIENT_ADDRESS"},
-			Value:   "",
-			Usage:   "WireGuard address for machine within Netmaker network.",
-		},
-		&cli.StringFlag{
-			Name:    "addressIPv6",
-			Aliases: []string{"a6"},
-			EnvVars: []string{"NETCLIENT_ADDRESSIPV6"},
-			Value:   "",
-			Usage:   "WireGuard address for machine within Netmaker network.",
-		},
-		&cli.StringFlag{
-			Name:    "interface",
-			Aliases: []string{"i"},
-			EnvVars: []string{"NETCLIENT_INTERFACE"},
-			Value:   "",
-			Usage:   "WireGuard local network interface name.",
-		},
-		&cli.StringFlag{
-			Name:    "apiserver",
-			EnvVars: []string{"NETCLIENT_API_SERVER"},
-			Value:   "",
-			Usage:   "Address + API Port (e.g. 1.2.3.4:8081) of Netmaker server.",
-		},
-		&cli.StringFlag{
-			Name:    "key",
-			Aliases: []string{"k"},
-			EnvVars: []string{"NETCLIENT_ACCESSKEY"},
-			Value:   "",
-			Usage:   "Access Key for signing up machine with Netmaker server during initial 'add'.",
-		},
-		&cli.StringFlag{
-			Name:    "token",
-			Aliases: []string{"t"},
-			EnvVars: []string{"NETCLIENT_ACCESSTOKEN"},
-			Value:   "",
-			Usage:   "Access Token for signing up machine with Netmaker server during initial 'add'.",
-		},
-		&cli.StringFlag{
-			Name:    "server",
-			Aliases: []string{"s"},
-			EnvVars: []string{"HOST_SERVER"},
-			Value:   "",
-			Usage:   "Host server (domain of API) [Example: api.example.com]. Do not include \"http(s)://\" use it for the Single Sign-on along with the network parameter",
-		},
-		&cli.StringFlag{
-			Name:    "user",
-			Aliases: []string{"u"},
-			EnvVars: []string{"USER_NAME"},
-			Value:   "",
-			Usage:   "User name provided upon joins if joining over basic auth is desired.",
-		},
-		&cli.StringFlag{
-			Name:    "localrange",
-			EnvVars: []string{"NETCLIENT_LOCALRANGE"},
-			Value:   "",
-			Usage:   "Local Range if network is local, for instance 192.168.1.0/24.",
-		},
-		&cli.StringFlag{
-			Name:    "dnson",
-			EnvVars: []string{"NETCLIENT_DNS"},
-			Value:   "yes",
-			Usage:   "Sets private dns if 'yes'. Ignores if 'no'. Will retrieve from network if unset.",
-		},
-		&cli.StringFlag{
-			Name:    "islocal",
-			EnvVars: []string{"NETCLIENT_IS_LOCAL"},
-			Value:   "",
-			Usage:   "Sets endpoint to local address if 'yes'. Ignores if 'no'. Will retrieve from network if unset.",
-		},
-		&cli.StringFlag{
-			Name:    "udpholepunch",
-			EnvVars: []string{"NETCLIENT_UDP_HOLEPUNCH"},
-			Value:   "",
-			Usage:   "Turns on udp holepunching if 'yes'. Ignores if 'no'. Will retrieve from network if unset.",
-		},
-		&cli.StringFlag{
-			Name:    "ipforwarding",
-			EnvVars: []string{"NETCLIENT_IPFORWARDING"},
-			Value:   "on",
-			Usage:   "Sets ip forwarding on if 'on'. Ignores if 'off'. On by default.",
-		},
-		&cli.StringFlag{
-			Name:    "postup",
-			EnvVars: []string{"NETCLIENT_POSTUP"},
-			Value:   "",
-			Usage:   "Sets PostUp command for WireGuard.",
-		},
-		&cli.StringFlag{
-			Name:    "postdown",
-			EnvVars: []string{"NETCLIENT_POSTDOWN"},
-			Value:   "",
-			Usage:   "Sets PostDown command for WireGuard.",
-		},
-		&cli.StringFlag{
-			Name:    "daemon",
-			EnvVars: []string{"NETCLIENT_DAEMON"},
-			Value:   "on",
-			Usage:   "Installs daemon if 'on'. Ignores if 'off'. On by default.",
-		},
-		&cli.StringFlag{
-			Name:    "roaming",
-			EnvVars: []string{"NETCLIENT_ROAMING"},
-			Value:   "yes",
-			Usage:   "Checks for IP changes if 'yes'. Ignores if 'no'. Yes by default.",
-		},
-		&cli.BoolFlag{
-			Name:    "verbosity-level-1",
-			Aliases: []string{"v"},
-			Value:   false,
-			Usage:   "Netclient Verbosity level 1.",
-		},
-		&cli.BoolFlag{
-			Name:    "verbosity-level-2",
-			Aliases: []string{"vv"},
-			Value:   false,
-			Usage:   "Netclient Verbosity level 2.",
-		},
-		&cli.BoolFlag{
-			Name:    "verbosity-level-3",
-			Aliases: []string{"vvv"},
-			Value:   false,
-			Usage:   "Netclient Verbosity level 3.",
-		},
-		&cli.BoolFlag{
-			Name:    "verbosity-level-4",
-			Aliases: []string{"vvvv"},
-			Value:   false,
-			Usage:   "Netclient Verbosity level 4.",
-		},
-	}
-}

+ 0 - 167
netclient/command/commands.go

@@ -1,167 +0,0 @@
-package command
-
-import (
-	"errors"
-	"fmt"
-	"strings"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/netclient/config"
-	"github.com/gravitl/netmaker/netclient/daemon"
-	"github.com/gravitl/netmaker/netclient/functions"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-)
-
-// Join - join command to run from cli
-func Join(cfg *config.ClientConfig, privateKey string) error {
-	var err error
-	//join network
-	if cfg.SsoServer != "" {
-		// User wants to get access key from the OIDC server
-		// Do that before the Joining Network flow by performing the end point auth flow
-		// if performed successfully an access key is obtained from the server and then we
-		// proceed with the usual flow 'pretending' that user is feeded us with an access token
-		if len(cfg.Network) == 0 || cfg.Network == "all" {
-			return fmt.Errorf("no network provided. Specify network with \"-n <net name>\"")
-		}
-		logger.Log(1, "Logging into %s via:", cfg.Network, cfg.SsoServer)
-		err = functions.JoinViaSSo(cfg, privateKey)
-		if err != nil {
-			logger.Log(0, "Join failed: ", err.Error())
-			return err
-		}
-
-		if cfg.AccessKey == "" {
-			return errors.New("login failed")
-		}
-	}
-
-	logger.Log(1, "Joining network: ", cfg.Network)
-	err = functions.JoinNetwork(cfg, privateKey)
-	if err != nil {
-		if !strings.Contains(err.Error(), "ALREADY_INSTALLED") {
-			logger.Log(0, "error installing: ", err.Error())
-			err = functions.WipeLocal(cfg)
-			if err != nil {
-				logger.Log(1, "error removing artifacts: ", err.Error())
-			}
-			if cfg.Daemon != "off" {
-				if ncutils.IsLinux() {
-					err = daemon.RemoveSystemDServices()
-				}
-				if err != nil {
-					logger.Log(1, "error removing services: ", err.Error())
-				}
-				if ncutils.IsFreeBSD() {
-					daemon.RemoveFreebsdDaemon()
-				}
-			}
-		}
-		if err != nil && strings.Contains(err.Error(), "ALREADY_INSTALLED") {
-			logger.Log(0, err.Error())
-			err = nil
-		}
-		return err
-	}
-	logger.Log(1, "joined", cfg.Network)
-
-	return err
-}
-
-// Leave - runs the leave command from cli
-func Leave(cfg *config.ClientConfig) error {
-	err := functions.LeaveNetwork(cfg.Network)
-	if err != nil {
-		logger.Log(1, "error attempting to leave network "+cfg.Network)
-	} else {
-		logger.Log(0, "success")
-	}
-	return err
-}
-
-// Pull - runs pull command from cli
-func Pull(cfg *config.ClientConfig) error {
-	var err error
-	var networks = []string{}
-	if cfg.Network == "all" {
-		logger.Log(0, "No network selected. Running Pull for all networks.")
-		networks, err = ncutils.GetSystemNetworks()
-		if err != nil {
-			logger.Log(1, "Error retrieving networks. Exiting.")
-			return err
-		}
-	} else {
-		networks = append(networks, cfg.Network)
-	}
-
-	var currentServers = make(map[string]config.ClientConfig)
-
-	for _, network := range networks {
-		currCfg, err := config.ReadConfig(network)
-		if err != nil {
-			logger.Log(1, "could not read config when pulling for network", network)
-			continue
-		}
-
-		_, err = functions.Pull(network, true)
-		if err != nil {
-			logger.Log(1, "error pulling network config for network: ", network, "\n", err.Error())
-		} else {
-			logger.Log(1, "pulled network config for "+network)
-		}
-
-		currentServers[currCfg.Server.Server] = *currCfg
-	}
-	daemon.Restart()
-	logger.Log(1, "reset network", cfg.Network, "and peer configs")
-	return err
-}
-
-// List - runs list command from cli
-func List(cfg config.ClientConfig) error {
-	_, err := functions.List(cfg.Network)
-	return err
-}
-
-// Uninstall - runs uninstall command from cli
-func Uninstall() error {
-	logger.Log(0, "uninstalling netclient...")
-	err := functions.Uninstall()
-	logger.Log(0, "uninstalled netclient")
-	return err
-}
-
-// Daemon - runs the daemon
-func Daemon() error {
-	err := functions.Daemon()
-	return err
-}
-
-// Install - installs binary and daemon
-func Install() error {
-	return functions.Install()
-}
-
-// Connect - re-instates a connection of a node
-func Connect(cfg config.ClientConfig) error {
-	networkName := cfg.Network
-	if networkName == "" {
-		networkName = cfg.Node.Network
-	}
-	if networkName == "all" {
-		return fmt.Errorf("no network specified")
-	}
-	return functions.Connect(networkName)
-}
-
-// Disconnect - disconnects a connection of a node
-func Disconnect(cfg config.ClientConfig) error {
-	networkName := cfg.Network
-	if networkName == "" {
-		networkName = cfg.Node.Network
-	}
-	if networkName == "all" {
-		return fmt.Errorf("no network specified")
-	}
-	return functions.Disconnect(networkName)
-}

+ 0 - 348
netclient/config/config.go

@@ -1,348 +0,0 @@
-package config
-
-import (
-	"crypto/ed25519"
-	"crypto/x509"
-	"crypto/x509/pkix"
-	"errors"
-	"fmt"
-	"log"
-	"os"
-	"sync"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/global_settings"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-	"github.com/urfave/cli/v2"
-	"gopkg.in/yaml.v3"
-)
-
-var (
-	configLock sync.Mutex
-)
-
-// ClientConfig - struct for dealing with client configuration
-type ClientConfig struct {
-	Server          models.ServerConfig `yaml:"server"`
-	Node            models.Node         `yaml:"node"`
-	NetworkSettings models.Network      `yaml:"networksettings"`
-	Network         string              `yaml:"network"`
-	Daemon          string              `yaml:"daemon"`
-	OperatingSystem string              `yaml:"operatingsystem"`
-	AccessKey       string              `yaml:"accesskey"`
-	PublicIPService string              `yaml:"publicipservice"`
-	SsoServer       string              `yaml:"sso"`
-}
-
-// RegisterRequest - struct for registation with netmaker server
-type RegisterRequest struct {
-	Key        ed25519.PrivateKey
-	CommonName pkix.Name
-}
-
-// RegisterResponse - the response to register function
-type RegisterResponse struct {
-	CA         x509.Certificate
-	CAPubKey   ed25519.PublicKey
-	Cert       x509.Certificate
-	CertPubKey ed25519.PublicKey
-	Broker     string
-	Port       string
-}
-
-// Write - writes the config of a client to disk
-func Write(config *ClientConfig, network string) error {
-	configLock.Lock()
-	defer configLock.Unlock()
-	if network == "" {
-		err := errors.New("no network provided - exiting")
-		return err
-	}
-	_, err := os.Stat(ncutils.GetNetclientPath() + "/config")
-	if os.IsNotExist(err) {
-		os.MkdirAll(ncutils.GetNetclientPath()+"/config", 0700)
-	} else if err != nil {
-		return err
-	}
-	home := ncutils.GetNetclientPathSpecific()
-
-	file := fmt.Sprintf(home + "netconfig-" + network)
-	f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
-	if err != nil {
-		return err
-	}
-	defer f.Close()
-
-	err = yaml.NewEncoder(f).Encode(config)
-	if err != nil {
-		return err
-	}
-	return f.Sync()
-}
-
-// ConfigFileExists - return true if config file exists
-func (config *ClientConfig) ConfigFileExists() bool {
-	home := ncutils.GetNetclientPathSpecific()
-
-	file := fmt.Sprintf(home + "netconfig-" + config.Network)
-	info, err := os.Stat(file)
-	if os.IsNotExist(err) {
-		return false
-	}
-	return !info.IsDir()
-}
-
-// ClientConfig.ReadConfig - used to read config from client disk into memory
-func (config *ClientConfig) ReadConfig() {
-
-	network := config.Network
-	if network == "" {
-		return
-	}
-
-	//home, err := homedir.Dir()
-	home := ncutils.GetNetclientPathSpecific()
-
-	file := fmt.Sprintf(home + "netconfig-" + network)
-	//f, err := os.Open(file)
-	f, err := os.OpenFile(file, os.O_RDONLY, 0600)
-	if err != nil {
-		logger.Log(1, "trouble opening file: ", err.Error())
-		if err = ReplaceWithBackup(network); err != nil {
-			log.Fatal(err)
-		}
-		f.Close()
-		f, err = os.Open(file)
-		if err != nil {
-			log.Fatal(err)
-		}
-
-	}
-	defer f.Close()
-	if err := yaml.NewDecoder(f).Decode(&config); err != nil {
-		logger.Log(0, "no config or invalid, replacing with backup")
-		if err = ReplaceWithBackup(network); err != nil {
-			log.Fatal(err)
-		}
-		f.Close()
-		f, err = os.Open(file)
-		if err != nil {
-			log.Fatal(err)
-		}
-		defer f.Close()
-		if err := yaml.NewDecoder(f).Decode(&config); err != nil {
-			log.Fatal(err)
-		}
-	}
-}
-
-// ModNodeConfig - overwrites the node inside client config on disk
-func ModNodeConfig(node *models.Node) error {
-	network := node.Network
-	if network == "" {
-		return errors.New("no network provided")
-	}
-	var modconfig ClientConfig
-	if FileExists(ncutils.GetNetclientPathSpecific() + "netconfig-" + network) {
-		useconfig, err := ReadConfig(network)
-		if err != nil {
-			return err
-		}
-		modconfig = *useconfig
-	}
-
-	modconfig.Node = (*node)
-	modconfig.NetworkSettings = node.NetworkSettings
-	return Write(&modconfig, network)
-}
-
-// ModNodeConfig - overwrites the server settings inside client config on disk
-func ModServerConfig(scfg *models.ServerConfig, network string) error {
-	var modconfig ClientConfig
-	if FileExists(ncutils.GetNetclientPathSpecific() + "netconfig-" + network) {
-		useconfig, err := ReadConfig(network)
-		if err != nil {
-			return err
-		}
-		modconfig = *useconfig
-	}
-
-	modconfig.Server = (*scfg)
-	return Write(&modconfig, network)
-}
-
-// SaveBackup - saves a backup file of a given network
-func SaveBackup(network string) error {
-
-	var configPath = ncutils.GetNetclientPathSpecific() + "netconfig-" + network
-	var backupPath = ncutils.GetNetclientPathSpecific() + "backup.netconfig-" + network
-	if FileExists(configPath) {
-		input, err := os.ReadFile(configPath)
-		if err != nil {
-			logger.Log(0, "failed to read ", configPath, " to make a backup")
-			return err
-		}
-		if err = os.WriteFile(backupPath, input, 0600); err != nil {
-			logger.Log(0, "failed to copy backup to ", backupPath)
-			return err
-		}
-	}
-	return nil
-}
-
-// ReplaceWithBackup - replaces netconfig file with backup
-func ReplaceWithBackup(network string) error {
-	var backupPath = ncutils.GetNetclientPathSpecific() + "backup.netconfig-" + network
-	var configPath = ncutils.GetNetclientPathSpecific() + "netconfig-" + network
-	if FileExists(backupPath) {
-		input, err := os.ReadFile(backupPath)
-		if err != nil {
-			logger.Log(0, "failed to read file ", backupPath, " to backup network: ", network)
-			return err
-		}
-		if err = os.WriteFile(configPath, input, 0600); err != nil {
-			logger.Log(0, "failed backup ", backupPath, " to ", configPath)
-			return err
-		}
-	}
-	logger.Log(0, "used backup file for network: ", network)
-	return nil
-}
-
-// GetCLIConfig - gets the cli flags as a config
-func GetCLIConfig(c *cli.Context) (ClientConfig, string, error) {
-	var cfg ClientConfig
-	if c.String("token") != "" {
-		accesstoken, err := ParseAccessToken(c.String("token"))
-		if err != nil {
-			return cfg, "", err
-		}
-		cfg.Network = accesstoken.ClientConfig.Network
-		cfg.Node.Network = accesstoken.ClientConfig.Network
-		cfg.AccessKey = accesstoken.ClientConfig.Key
-		cfg.Node.LocalRange = accesstoken.ClientConfig.LocalRange
-		//cfg.Server.Server = accesstoken.ServerConfig.Server
-		cfg.Server.API = accesstoken.APIConnString
-		if c.String("key") != "" {
-			cfg.AccessKey = c.String("key")
-		}
-		if c.String("network") != "all" {
-			cfg.Network = c.String("network")
-			cfg.Node.Network = c.String("network")
-		}
-		if c.String("localrange") != "" {
-			cfg.Node.LocalRange = c.String("localrange")
-		}
-		if c.String("corednsaddr") != "" {
-			cfg.Server.CoreDNSAddr = c.String("corednsaddr")
-		}
-		if c.String("apiserver") != "" {
-			cfg.Server.API = c.String("apiserver")
-		}
-	} else if c.String("server") != "" {
-		cfg.SsoServer = c.String("server")
-		cfg.Network = c.String("network")
-		cfg.Node.Network = c.String("network")
-		global_settings.User = c.String("user")
-	} else {
-		cfg.AccessKey = c.String("key")
-		cfg.Network = c.String("network")
-		cfg.Node.Network = c.String("network")
-		cfg.Node.LocalRange = c.String("localrange")
-		cfg.Server.CoreDNSAddr = c.String("corednsaddr")
-		cfg.Server.API = c.String("apiserver")
-	}
-	cfg.PublicIPService = c.String("publicipservice")
-	// populate the map as we're not running as a daemon so won't be building the map otherwise
-	// (and the map will be used by GetPublicIP()).
-	global_settings.PublicIPServices[cfg.Network] = cfg.PublicIPService
-	cfg.Node.Name = c.String("name")
-	cfg.Node.Interface = c.String("interface")
-	cfg.Node.Password = c.String("password")
-	cfg.Node.MacAddress = c.String("macaddress")
-	cfg.Node.LocalAddress = c.String("localaddress")
-	cfg.Node.Address = c.String("address")
-	cfg.Node.Address6 = c.String("address6")
-	//cfg.Node.Roaming = c.String("roaming")
-	cfg.Node.DNSOn = c.String("dnson")
-	cfg.Node.IsLocal = c.String("islocal")
-	cfg.Node.IsStatic = c.String("isstatic")
-	cfg.Node.PostUp = c.String("postup")
-	cfg.Node.PostDown = c.String("postdown")
-	cfg.Node.ListenPort = int32(c.Int("port"))
-	cfg.Node.PersistentKeepalive = int32(c.Int("keepalive"))
-	cfg.Node.PublicKey = c.String("publickey")
-	privateKey := c.String("privatekey")
-	cfg.Node.Endpoint = c.String("endpoint")
-	cfg.Node.IPForwarding = c.String("ipforwarding")
-	cfg.OperatingSystem = c.String("operatingsystem")
-	cfg.Daemon = c.String("daemon")
-	cfg.Node.UDPHolePunch = c.String("udpholepunch")
-	cfg.Node.MTU = int32(c.Int("mtu"))
-
-	return cfg, privateKey, nil
-}
-
-// ReadConfig - reads a config of a client from disk for specified network
-func ReadConfig(network string) (*ClientConfig, error) {
-	if network == "" {
-		err := errors.New("no network provided - exiting")
-		return nil, err
-	}
-	home := ncutils.GetNetclientPathSpecific()
-	file := fmt.Sprintf(home + "netconfig-" + network)
-	f, err := os.Open(file)
-	if err != nil {
-		if err = ReplaceWithBackup(network); err != nil {
-			return nil, err
-		}
-		f, err = os.Open(file)
-		if err != nil {
-			return nil, err
-		}
-	}
-	defer f.Close()
-
-	var cfg ClientConfig
-	decoder := yaml.NewDecoder(f)
-	err = decoder.Decode(&cfg)
-	if err != nil {
-		if err = ReplaceWithBackup(network); err != nil {
-			return nil, err
-		}
-		f.Close()
-		f, err = os.Open(file)
-		if err != nil {
-			return nil, err
-		}
-		defer f.Close()
-		if err := yaml.NewDecoder(f).Decode(&cfg); err != nil {
-			return nil, err
-		}
-	}
-
-	return &cfg, err
-}
-
-// FileExists - checks if a file exists on disk
-func FileExists(f string) bool {
-	info, err := os.Stat(f)
-	if os.IsNotExist(err) {
-		return false
-	}
-	return !info.IsDir()
-}
-
-// GetNode - parses a network specified client config for node data
-func GetNode(network string) models.Node {
-
-	modcfg, err := ReadConfig(network)
-	if err != nil {
-		log.Fatalf("Error: %v", err)
-	}
-	var node models.Node
-	node.Fill(&modcfg.Node)
-
-	return node
-}

+ 0 - 31
netclient/config/util.go

@@ -1,31 +0,0 @@
-package config
-
-import (
-	"encoding/base64"
-	"encoding/json"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/models"
-)
-
-var (
-	// GuiActive - indicates if gui is active or not
-	GuiActive = false
-	// GuiRun - holds function for main to call
-	GuiRun interface{}
-)
-
-// ParseAccessToken - used to parse the base64 encoded access token
-func ParseAccessToken(token string) (*models.AccessToken, error) {
-	tokenbytes, err := base64.StdEncoding.DecodeString(token)
-	if err != nil {
-		logger.Log(0, "error decoding token", err.Error())
-		return nil, err
-	}
-	var accesstoken models.AccessToken
-	if err := json.Unmarshal(tokenbytes, &accesstoken); err != nil {
-		logger.Log(0, "error decoding token", err.Error())
-		return nil, err
-	}
-	return &accesstoken, nil
-}

+ 0 - 94
netclient/daemon/common.go

@@ -1,94 +0,0 @@
-package daemon
-
-import (
-	"errors"
-	"fmt"
-	"os"
-	"runtime"
-	"syscall"
-	"time"
-
-	"github.com/gravitl/netmaker/netclient/ncutils"
-)
-
-// InstallDaemon - Calls the correct function to install the netclient as a daemon service on the given operating system.
-func InstallDaemon() error {
-
-	runtimeOS := runtime.GOOS
-	var err error
-
-	switch runtimeOS {
-	case "windows":
-		err = SetupWindowsDaemon()
-	case "darwin":
-		err = SetupMacDaemon()
-	case "linux":
-		err = SetupSystemDDaemon()
-	case "freebsd":
-		err = SetupFreebsdDaemon()
-	default:
-		err = errors.New("this os is not yet supported for daemon mode. Run join cmd with flag '--daemon off'")
-	}
-	return err
-}
-
-// Restart - restarts a system daemon
-func Restart() error {
-	if ncutils.IsWindows() {
-		RestartWindowsDaemon()
-		return nil
-	}
-	pid, err := ncutils.ReadPID()
-	if err != nil {
-		return fmt.Errorf("failed to find pid %w", err)
-	}
-	p, err := os.FindProcess(pid)
-	if err != nil {
-		return fmt.Errorf("failed to find running process for pid %d -- %w", pid, err)
-	}
-	if err := p.Signal(syscall.SIGHUP); err != nil {
-		return fmt.Errorf("SIGHUP failed -- %w", err)
-	}
-	return nil
-}
-
-// Start - starts system daemon
-func Start() error {
-	runtimeOS := runtime.GOOS
-	var err error
-	switch runtimeOS {
-	case "windows":
-		RestartWindowsDaemon()
-	case "darwin":
-		RestartLaunchD()
-	case "linux":
-		RestartSystemD()
-	case "freebsd":
-		FreebsdDaemon("restart")
-	default:
-		err = errors.New("this os is not yet supported for daemon mode. Run join cmd with flag '--daemon off'")
-	}
-	return err
-}
-
-// Stop - stops a system daemon
-func Stop() error {
-	runtimeOS := runtime.GOOS
-	var err error
-
-	time.Sleep(time.Second)
-
-	switch runtimeOS {
-	case "windows":
-		RunWinSWCMD("stop")
-	case "darwin":
-		StopLaunchD()
-	case "linux":
-		StopSystemD()
-	case "freebsd":
-		FreebsdDaemon("stop")
-	default:
-		err = errors.New("no OS daemon to stop")
-	}
-	return err
-}

+ 0 - 135
netclient/daemon/freebsd.go

@@ -1,135 +0,0 @@
-package daemon
-
-import (
-	"log"
-	"os"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-)
-
-// SetupFreebsdDaemon -- sets up daemon for freebsd
-func SetupFreebsdDaemon() error {
-	binarypath, err := os.Executable()
-	if err != nil {
-		return err
-	}
-
-	_, err = os.Stat("/etc/netclient/config")
-	if os.IsNotExist(err) {
-		os.MkdirAll("/etc/netclient/config", 0744)
-	} else if err != nil {
-		log.Println("couldnt find or create /etc/netclient")
-		return err
-	}
-	//install binary
-	if ncutils.FileExists(EXEC_DIR + "netclient") {
-		logger.Log(0, "updating netclient binary in ", EXEC_DIR)
-	}
-	err = ncutils.Copy(binarypath, EXEC_DIR+"netclient")
-	if err != nil {
-		logger.Log(0, err.Error())
-		return err
-	}
-
-	rcFile := `#!/bin/sh
-#
-# PROVIDE: netclient
-# REQUIRE: LOGIN
-# KEYWORD: shutdown
-
-# Description:
-#    This script runs netclient as a service as root on boot
-
-# How to use:
-#    Place this file in /usr/local/etc/rc.d/
-#    Add netclient="YES" to /etc/rc.config.d/netclient
-#    To pass args, add netclient_args="daemon" to /etc/rc.config.d/netclient
-
-# Freebsd rc library
-. /etc/rc.subr
-
-# General Info
-name="netclient"            # Safe name of program
-program_name="netclient"   # Name of exec
-title="netclient"          # Title to display in top/htop
-
-# RC.config vars
-load_rc_config $name      # Loading rc config vars
-: ${netclient_enable="YES"}  # Default: enable netclient
-: ${netclient_runAs="root"} # Default: Run Node-RED as root
-
-# Freebsd Setup
-rcvar=netclient_enable                   # Enables the rc.conf YES/NO flag
-pidfile="/var/run/${program_name}.pid" # File that allows the system to keep track of node-red status
-
-# Env Setup
-#export HOME=$( getent passwd "$netclient_runAs" | cut -d: -f6 ) # Gets the home directory of the runAs user
-
-# Command Setup
-exec_path="/sbin/${program_name}" # Path to the netclient exec
-output_file="/var/log/${program_name}.log" # Path to netclient logs
-
-# Command
-command="/usr/sbin/daemon"
-command_args="-r -t ${title} -u ${netclient_runAs} -o ${output_file} -P ${pidfile} ${exec_path} ${netclient_args}"
-
-# Loading Config
-load_rc_config ${name}
-run_rc_command "$1"
-`
-
-	rcConfig := `netclient="YES"
-netclient_args="daemon"`
-
-	rcbytes := []byte(rcFile)
-	if !ncutils.FileExists("/etc/rc.d/netclient") {
-		err := os.WriteFile("/etc/rc.d/netclient", rcbytes, 0744)
-		if err != nil {
-			return err
-		}
-		rcConfigbytes := []byte(rcConfig)
-		if !ncutils.FileExists("/etc/rc.conf.d/netclient") {
-			err := os.WriteFile("/etc/rc.conf.d/netclient", rcConfigbytes, 0644)
-			if err != nil {
-				return err
-			}
-			FreebsdDaemon("start")
-			return nil
-		}
-	}
-	return nil
-}
-
-// FreebsdDaemon - accepts args to service netclient and applies
-func FreebsdDaemon(command string) {
-	_, _ = ncutils.RunCmdFormatted("service netclient "+command, true)
-}
-
-// CleanupFreebsd - removes config files and netclient binary
-func CleanupFreebsd() {
-	ncutils.RunCmd("service netclient stop", false)
-	RemoveFreebsdDaemon()
-	if err := os.RemoveAll(ncutils.GetNetclientPath()); err != nil {
-		logger.Log(1, "Removing netclient configs: ", err.Error())
-	}
-	if err := os.Remove(EXEC_DIR + "netclient"); err != nil {
-		logger.Log(1, "Removing netclient binary: ", err.Error())
-	}
-}
-
-// RemoveFreebsdDaemon - remove freebsd daemon
-func RemoveFreebsdDaemon() {
-	if ncutils.FileExists("/etc/rc.d/netclient") {
-		err := os.Remove("/etc/rc.d/netclient")
-		if err != nil {
-			logger.Log(0, "Error removing /etc/rc.d/netclient. Please investigate.")
-		}
-	}
-	if ncutils.FileExists("/etc/rc.conf.d/netclient") {
-		err := os.Remove("/etc/rc.conf.d/netclient")
-		if err != nil {
-			logger.Log(0, "Error removing /etc/rc.conf.d/netclient. Please investigate.")
-		}
-	}
-}

+ 0 - 117
netclient/daemon/macos.go

@@ -1,117 +0,0 @@
-package daemon
-
-import (
-	"log"
-	"os"
-	"time"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-)
-
-const MAC_SERVICE_NAME = "com.gravitl.netclient"
-const MAC_EXEC_DIR = "/usr/local/bin/"
-
-// SetupMacDaemon - Creates a daemon service from the netclient under LaunchAgents for MacOS
-func SetupMacDaemon() error {
-
-	binarypath, err := os.Executable()
-	if err != nil {
-		return err
-	}
-
-	if ncutils.FileExists(MAC_EXEC_DIR + "netclient") {
-		logger.Log(0, "updating netclient binary in", MAC_EXEC_DIR)
-	}
-	err = ncutils.Copy(binarypath, MAC_EXEC_DIR+"netclient")
-	if err != nil {
-		logger.Log(0, err.Error())
-		return err
-	}
-
-	err = CreateMacService(MAC_SERVICE_NAME)
-	if err != nil {
-		return err
-	}
-	_, err = ncutils.RunCmd("launchctl load /Library/LaunchDaemons/"+MAC_SERVICE_NAME+".plist", true)
-	return err
-}
-
-// CleanupMac - Removes the netclient checkin daemon from LaunchDaemons
-func CleanupMac() {
-	_, err := ncutils.RunCmd("launchctl unload /Library/LaunchDaemons/"+MAC_SERVICE_NAME+".plist", true)
-	if ncutils.FileExists("/Library/LaunchDaemons/" + MAC_SERVICE_NAME + ".plist") {
-		err = os.Remove("/Library/LaunchDaemons/" + MAC_SERVICE_NAME + ".plist")
-	}
-	if err != nil {
-		logger.Log(1, err.Error())
-	}
-
-	os.RemoveAll(ncutils.GetNetclientPath())
-	os.Remove(MAC_EXEC_DIR + "netclient")
-}
-
-// RestartLaunchD - restart launch daemon
-func RestartLaunchD() {
-	ncutils.RunCmd("launchctl unload /Library/LaunchDaemons/"+MAC_SERVICE_NAME+".plist", true)
-	time.Sleep(time.Second >> 2)
-	ncutils.RunCmd("launchctl load /Library/LaunchDaemons/"+MAC_SERVICE_NAME+".plist", true)
-}
-
-// StopLaunchD - stop launch daemon
-func StopLaunchD() {
-	ncutils.RunCmd("launchctl unload  /Library/LaunchDaemons/"+MAC_SERVICE_NAME+".plist", true)
-}
-
-// CreateMacService - Creates the mac service file for LaunchDaemons
-func CreateMacService(servicename string) error {
-	_, err := os.Stat("/Library/LaunchDaemons")
-	if os.IsNotExist(err) {
-		os.Mkdir("/Library/LaunchDaemons", 0755)
-	} else if err != nil {
-		log.Println("couldnt find or create /Library/LaunchDaemons")
-		return err
-	}
-	daemonstring := MacDaemonString()
-	daemonbytes := []byte(daemonstring)
-
-	if !ncutils.FileExists("/Library/LaunchDaemons/com.gravitl.netclient.plist") {
-		err = os.WriteFile("/Library/LaunchDaemons/com.gravitl.netclient.plist", daemonbytes, 0644)
-	}
-	return err
-}
-
-// MacDaemonString - the file contents for the mac netclient daemon service (launchdaemon)
-func MacDaemonString() string {
-	return `<?xml version='1.0' encoding='UTF-8'?>
-<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\" >
-<plist version='1.0'>
-<dict>
-	<key>Label</key><string>com.gravitl.netclient</string>
-	<key>ProgramArguments</key>
-		<array>
-			<string>/usr/local/bin/netclient</string>
-			<string>daemon</string>
-		</array>
-	<key>StandardOutPath</key><string>/var/log/com.gravitl.netclient.log</string>
-	<key>StandardErrorPath</key><string>/var/log/com.gravitl.netclient.log</string>
-	<key>RunAtLoad</key>
-	<true/>
-	<key>KeepAlive</key>
-	<true/>
-	<key>AbandonProcessGroup</key><true/>
-	<key>EnvironmentVariables</key>
-		<dict>
-			<key>PATH</key>
-			<string>/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
-		</dict>
-</dict>
-</plist>
-`
-}
-
-// MacTemplateData - struct to represent the mac service
-type MacTemplateData struct {
-	Label    string
-	Interval string
-}

+ 0 - 139
netclient/daemon/systemd.go

@@ -1,139 +0,0 @@
-package daemon
-
-import (
-	//"github.com/davecgh/go-spew/spew"
-
-	"log"
-	"os"
-	"path/filepath"
-	"time"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-)
-
-const EXEC_DIR = "/sbin/"
-
-// SetupSystemDDaemon - sets system daemon for supported machines
-func SetupSystemDDaemon() error {
-
-	if ncutils.IsWindows() {
-		return nil
-	}
-	binarypath, err := os.Executable()
-	if err != nil {
-		return err
-	}
-
-	_, err = os.Stat("/etc/netclient/config")
-	if os.IsNotExist(err) {
-		os.MkdirAll("/etc/netclient/config", 0744)
-	} else if err != nil {
-		log.Println("couldnt find or create /etc/netclient")
-		return err
-	}
-	//install binary
-	if ncutils.FileExists(EXEC_DIR + "netclient") {
-		logger.Log(0, "updating netclient binary in", EXEC_DIR)
-	}
-	err = ncutils.Copy(binarypath, EXEC_DIR+"netclient")
-	if err != nil {
-		logger.Log(0, err.Error())
-		return err
-	}
-
-	systemservice := `[Unit]
-Description=Netclient Daemon
-Documentation=https://docs.netmaker.org https://k8s.netmaker.org
-After=network-online.target
-Wants=network-online.target
-
-[Service]
-User=root
-Type=simple
-ExecStartPre=/bin/sleep 17
-ExecStart=/sbin/netclient daemon
-Restart=on-failure
-RestartSec=15s
-
-[Install]
-WantedBy=multi-user.target
-`
-
-	servicebytes := []byte(systemservice)
-
-	if !ncutils.FileExists("/etc/systemd/system/netclient.service") {
-		err = os.WriteFile("/etc/systemd/system/netclient.service", servicebytes, 0644)
-		if err != nil {
-			logger.Log(0, err.Error())
-			return err
-		}
-	}
-	_, _ = ncutils.RunCmd("systemctl enable netclient.service", true)
-	_, _ = ncutils.RunCmd("systemctl daemon-reload", true)
-	_, _ = ncutils.RunCmd("systemctl start netclient.service", true)
-	return nil
-}
-
-// RestartSystemD - restarts systemd service
-func RestartSystemD() {
-	logger.Log(1, "restarting netclient.service")
-	time.Sleep(time.Second)
-	_, _ = ncutils.RunCmd("systemctl restart netclient.service", true)
-}
-
-// CleanupLinux - cleans up neclient configs
-func CleanupLinux() {
-	if _, err := ncutils.RunCmd("systemctl stop netclient", false); err != nil {
-		logger.Log(0, "failed to stop netclient service", err.Error())
-	}
-	RemoveSystemDServices()
-	if err := os.RemoveAll(ncutils.GetNetclientPath()); err != nil {
-		logger.Log(1, "Removing netclient configs: ", err.Error())
-	}
-	if err := os.Remove(EXEC_DIR + "netclient"); err != nil {
-		logger.Log(1, "Removing netclient binary: ", err.Error())
-	}
-}
-
-// StopSystemD - tells system to stop systemd
-func StopSystemD() {
-	ncutils.RunCmd("systemctl stop netclient.service", false)
-}
-
-// RemoveSystemDServices - removes the systemd services on a machine
-func RemoveSystemDServices() error {
-	//sysExec, err := exec.LookPath("systemctl")
-	var err error
-	if !ncutils.IsWindows() && isOnlyService() {
-		if err != nil {
-			logger.Log(0, err.Error())
-		}
-		ncutils.RunCmd("systemctl disable netclient.service", false)
-		ncutils.RunCmd("systemctl disable netclient.timer", false)
-		if ncutils.FileExists("/etc/systemd/system/netclient.service") {
-			err = os.Remove("/etc/systemd/system/netclient.service")
-			if err != nil {
-				logger.Log(0, "Error removing /etc/systemd/system/netclient.service. Please investigate.")
-			}
-		}
-		if ncutils.FileExists("/etc/systemd/system/netclient.timer") {
-			err = os.Remove("/etc/systemd/system/netclient.timer")
-			if err != nil {
-				logger.Log(0, "Error removing /etc/systemd/system/netclient.timer. Please investigate.")
-			}
-		}
-		ncutils.RunCmd("systemctl daemon-reload", false)
-		ncutils.RunCmd("systemctl reset-failed", false)
-		logger.Log(0, "removed systemd remnants if any existed")
-	}
-	return nil
-}
-
-func isOnlyService() bool {
-	files, err := filepath.Glob("/etc/netclient/config/netconfig-*")
-	if err != nil {
-		return false
-	}
-	return len(files) == 0
-}

+ 0 - 106
netclient/daemon/windows.go

@@ -1,106 +0,0 @@
-package daemon
-
-import (
-	"fmt"
-	"log"
-	"os"
-	"strings"
-	"time"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-)
-
-// SetupWindowsDaemon - sets up the Windows daemon service
-func SetupWindowsDaemon() error {
-
-	if ncutils.FileExists(ncutils.GetNetclientPathSpecific() + "winsw.xml") {
-		logger.Log(0, "updating netclient service")
-	}
-	if err := writeServiceConfig(); err != nil {
-		return err
-	}
-
-	if ncutils.FileExists(ncutils.GetNetclientPathSpecific() + "winsw.exe") {
-		logger.Log(0, "updating netclient binary")
-	}
-	err := ncutils.GetEmbedded()
-	if err != nil {
-		return err
-	}
-	logger.Log(0, "finished daemon setup")
-	//get exact formatted commands
-	RunWinSWCMD("install")
-	time.Sleep(time.Millisecond)
-	RunWinSWCMD("start")
-
-	return nil
-}
-
-// RestartWindowsDaemon - restarts windows service
-func RestartWindowsDaemon() {
-	RunWinSWCMD("stop")
-	time.Sleep(time.Millisecond)
-	RunWinSWCMD("start")
-}
-
-// CleanupWindows - cleans up windows files
-func CleanupWindows() {
-	if !ncutils.FileExists(ncutils.GetNetclientPathSpecific() + "winsw.xml") {
-		writeServiceConfig()
-	}
-	RunWinSWCMD("stop")
-	RunWinSWCMD("uninstall")
-	os.RemoveAll(ncutils.GetNetclientPath())
-	log.Println("Netclient on Windows, uninstalled")
-}
-
-func writeServiceConfig() error {
-	serviceConfigPath := ncutils.GetNetclientPathSpecific() + "winsw.xml"
-	scriptString := fmt.Sprintf(`<service>
-<id>netclient</id>
-<name>Netclient</name>
-<description>Connects Windows nodes to one or more Netmaker networks.</description>
-<executable>%v</executable>
-<arguments>daemon</arguments>
-<log mode="roll"></log>
-</service>
-`, strings.Replace(ncutils.GetNetclientPathSpecific()+"netclient.exe", `\\`, `\`, -1))
-	if !ncutils.FileExists(serviceConfigPath) {
-		err := os.WriteFile(serviceConfigPath, []byte(scriptString), 0600)
-		if err != nil {
-			return err
-		}
-		logger.Log(0, "wrote the daemon config file to the Netclient directory")
-	}
-	return nil
-}
-
-// RunWinSWCMD - Run a command with the winsw.exe tool (start, stop, install, uninstall)
-func RunWinSWCMD(command string) {
-
-	// check if command allowed
-	allowedCommands := map[string]bool{
-		"start":     true,
-		"stop":      true,
-		"install":   true,
-		"uninstall": true,
-	}
-	if !allowedCommands[command] {
-		logger.Log(0, "command "+command+" unsupported by winsw")
-		return
-	}
-
-	// format command
-	dirPath := strings.Replace(ncutils.GetNetclientPathSpecific(), `\\`, `\`, -1)
-	winCmd := fmt.Sprintf(`"%swinsw.exe" "%s"`, dirPath, command)
-	logger.Log(0, "running "+command+" of Windows Netclient daemon")
-
-	// run command and log for success/failure
-	out, err := ncutils.RunCmdFormatted(winCmd, true)
-	if err != nil {
-		logger.Log(0, "error with "+command+" of Windows Netclient daemon: "+err.Error()+" : "+out)
-	} else {
-		logger.Log(0, "successfully ran "+command+" of Windows Netclient daemon")
-	}
-}

+ 0 - 78
netclient/functions/clientconfig.go

@@ -1,78 +0,0 @@
-package functions
-
-import (
-	"strconv"
-	"strings"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/netclient/config"
-	"github.com/gravitl/netmaker/netclient/functions/upgrades"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-)
-
-// UpdateClientConfig - function is called on daemon start to update clientConfig if required
-// Usage :  set update required to true and and update logic to function
-func UpdateClientConfig() {
-	defer upgrades.ReleaseUpgrades()
-	upgrades.InitializeUpgrades()
-
-	networks, _ := ncutils.GetSystemNetworks()
-	if len(networks) == 0 {
-		return
-	}
-	logger.Log(0, "checking for netclient updates...")
-	for _, network := range networks {
-		cfg := config.ClientConfig{}
-		cfg.Network = network
-		cfg.ReadConfig()
-		major, minor, _ := Version(cfg.Node.Version)
-		if major == 0 && minor < 14 {
-			logger.Log(0, "schema of network", cfg.Network, "is out of date and cannot be updated\n Correct behaviour of netclient cannot be guaranteed")
-			continue
-		}
-		configChanged := false
-		for _, u := range upgrades.Upgrades {
-			if ncutils.StringSliceContains(u.RequiredVersions, cfg.Node.Version) {
-				logger.Log(0, "upgrading node", cfg.Node.Name, "on network", cfg.Node.Network, "from", cfg.Node.Version, "to", u.NewVersion)
-				upgrades.UpgradeFunction(u.OP)(&cfg)
-				cfg.Node.Version = u.NewVersion
-				configChanged = true
-			}
-		}
-		if configChanged {
-			//save and publish
-			if err := PublishNodeUpdate(&cfg); err != nil {
-				logger.Log(0, "error publishing node update during schema change", err.Error())
-			}
-			if err := config.ModNodeConfig(&cfg.Node); err != nil {
-				logger.Log(0, "error saving local config for node,", cfg.Node.Name, ", on network,", cfg.Node.Network)
-			}
-		}
-	}
-	logger.Log(0, "finished updates")
-}
-
-// Version - parse version string into component parts
-// version string must be semantic version of form 1.2.3 or v1.2.3
-// otherwise 0, 0, 0 will be returned.
-func Version(version string) (int, int, int) {
-	var major, minor, patch int
-	var errMajor, errMinor, errPatch error
-	parts := strings.Split(version, ".")
-	//ensure semantic version
-	if len(parts) < 3 {
-		return major, minor, patch
-	}
-	if strings.Contains(parts[0], "v") {
-		majors := strings.Split(parts[0], "v")
-		major, errMajor = strconv.Atoi(majors[1])
-	} else {
-		major, errMajor = strconv.Atoi(parts[0])
-	}
-	minor, errMinor = strconv.Atoi(parts[1])
-	patch, errPatch = strconv.Atoi(parts[2])
-	if errMajor != nil || errMinor != nil || errPatch != nil {
-		return 0, 0, 0
-	}
-	return major, minor, patch
-}

+ 0 - 425
netclient/functions/common.go

@@ -1,425 +0,0 @@
-package functions
-
-import (
-	"bytes"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"io"
-	"log"
-	"net"
-	"net/http"
-	"os"
-	"path/filepath"
-	"strconv"
-	"strings"
-	"time"
-
-	"golang.zx2c4.com/wireguard/wgctrl"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/config"
-	"github.com/gravitl/netmaker/netclient/daemon"
-	"github.com/gravitl/netmaker/netclient/local"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-	"github.com/gravitl/netmaker/netclient/wireguard"
-)
-
-// LINUX_APP_DATA_PATH - linux path
-const LINUX_APP_DATA_PATH = "/etc/netmaker"
-
-// HTTP_TIMEOUT - timeout in seconds for http requests
-const HTTP_TIMEOUT = 30
-
-// HTTPClient - http client to be reused by all
-var HTTPClient http.Client
-
-// SetHTTPClient -sets http client with sane default
-func SetHTTPClient() {
-	HTTPClient = http.Client{
-		Timeout: HTTP_TIMEOUT * time.Second,
-	}
-}
-
-// ListPorts - lists ports of WireGuard devices
-func ListPorts() error {
-	wgclient, err := wgctrl.New()
-	if err != nil {
-		return err
-	}
-	defer wgclient.Close()
-	devices, err := wgclient.Devices()
-	if err != nil {
-		return err
-	}
-	fmt.Println("Here are your ports:")
-	for _, i := range devices {
-		fmt.Println(i.ListenPort)
-	}
-	return err
-}
-
-func getPrivateAddr() (string, error) {
-
-	var localIPStr string
-	conn, err := net.Dial("udp", "8.8.8.8:80")
-	if err == nil {
-		defer conn.Close()
-
-		localAddr := conn.LocalAddr().(*net.UDPAddr)
-		localIP := localAddr.IP
-		localIPStr = localIP.String()
-	}
-	if localIPStr == "" {
-		localIPStr, err = getPrivateAddrBackup()
-	}
-
-	if localIPStr == "" {
-		err = errors.New("could not find local ip")
-	}
-	if net.ParseIP(localIPStr).To16() != nil {
-		localIPStr = "[" + localIPStr + "]"
-	}
-
-	return localIPStr, err
-}
-
-func getPrivateAddrBackup() (string, error) {
-	ifaces, err := net.Interfaces()
-	if err != nil {
-		return "", err
-	}
-	var local string
-	found := false
-	for _, i := range ifaces {
-		if i.Flags&net.FlagUp == 0 {
-			continue // interface down
-		}
-		if i.Flags&net.FlagLoopback != 0 {
-			continue // loopback interface
-		}
-		addrs, err := i.Addrs()
-		if err != nil {
-			return "", err
-		}
-		for _, addr := range addrs {
-			var ip net.IP
-			switch v := addr.(type) {
-			case *net.IPNet:
-				if !found {
-					ip = v.IP
-					local = ip.String()
-					found = true
-				}
-			case *net.IPAddr:
-				if !found {
-					ip = v.IP
-					local = ip.String()
-					found = true
-				}
-			}
-		}
-	}
-	if !found {
-		err := errors.New("local ip address not found")
-		return "", err
-	}
-	return local, err
-}
-
-func getInterfaces() (*[]models.Iface, error) {
-	ifaces, err := net.Interfaces()
-	if err != nil {
-		return nil, err
-	}
-	var data []models.Iface
-	var link models.Iface
-	for _, iface := range ifaces {
-		if iface.Flags&net.FlagUp == 0 {
-			continue // interface down
-		}
-		if iface.Flags&net.FlagLoopback != 0 {
-			continue // loopback interface
-		}
-		addrs, err := iface.Addrs()
-		if err != nil {
-			return nil, err
-		}
-		for _, addr := range addrs {
-			link.Name = iface.Name
-			_, cidr, err := net.ParseCIDR(addr.String())
-			if err != nil {
-				continue
-			}
-			link.Address = *cidr
-			data = append(data, link)
-		}
-	}
-	return &data, nil
-}
-
-// GetNode - gets node locally
-func GetNode(network string) models.Node {
-
-	modcfg, err := config.ReadConfig(network)
-	if err != nil {
-		log.Fatalf("Error: %v", err)
-	}
-
-	return modcfg.Node
-}
-
-// Uninstall - uninstalls networks from client
-func Uninstall() error {
-	networks, err := ncutils.GetSystemNetworks()
-	if err != nil {
-		logger.Log(1, "unable to retrieve networks: ", err.Error())
-		logger.Log(1, "continuing uninstall without leaving networks")
-	} else {
-		for _, network := range networks {
-			err = LeaveNetwork(network)
-			if err != nil {
-				logger.Log(1, "encounter issue leaving network", network, ":", err.Error())
-			}
-		}
-	}
-	err = nil
-
-	// clean up OS specific stuff
-	if ncutils.IsWindows() {
-		daemon.CleanupWindows()
-	} else if ncutils.IsMac() {
-		daemon.CleanupMac()
-	} else if ncutils.IsLinux() {
-		daemon.CleanupLinux()
-	} else if ncutils.IsFreeBSD() {
-		daemon.CleanupFreebsd()
-	} else if !ncutils.IsKernel() {
-		logger.Log(1, "manual cleanup required")
-	}
-
-	return err
-}
-
-// LeaveNetwork - client exits a network
-func LeaveNetwork(network string) error {
-	cfg, err := config.ReadConfig(network)
-	if err != nil {
-		return err
-	}
-	logger.Log(2, "deleting node from server")
-	if err := deleteNodeFromServer(cfg); err != nil {
-		logger.Log(0, "error deleting node from server", err.Error())
-	}
-	logger.Log(2, "deleting wireguard interface")
-	if err := deleteLocalNetwork(cfg); err != nil {
-		logger.Log(0, "error deleting wireguard interface", err.Error())
-	}
-	logger.Log(2, "deleting configuration files")
-	if err := WipeLocal(cfg); err != nil {
-		logger.Log(0, "error deleting local network files", err.Error())
-	}
-	logger.Log(2, "removing dns entries")
-	if err := removeHostDNS(cfg.Node.Interface, ncutils.IsWindows()); err != nil {
-		logger.Log(0, "failed to delete dns entries for", cfg.Node.Interface, err.Error())
-	}
-	logger.Log(2, "restarting daemon")
-	return daemon.Restart()
-}
-
-func deleteNodeFromServer(cfg *config.ClientConfig) error {
-	node := cfg.Node
-	if node.IsServer == "yes" {
-		return errors.New("attempt to delete server node ... not permitted")
-	}
-	token, err := Authenticate(cfg)
-	if err != nil {
-		return fmt.Errorf("unable to authenticate %w", err)
-	}
-	url := "https://" + cfg.Server.API + "/api/nodes/" + cfg.Network + "/" + cfg.Node.ID
-	response, err := API("", http.MethodDelete, url, token)
-	if err != nil {
-		return fmt.Errorf("error deleting node on server: %w", err)
-	}
-	if response.StatusCode != http.StatusOK {
-		bodybytes, _ := io.ReadAll(response.Body)
-		defer response.Body.Close()
-		return fmt.Errorf("error deleting node from network %s on server %s %s", cfg.Network, response.Status, string(bodybytes))
-	}
-	return nil
-}
-
-func deleteLocalNetwork(cfg *config.ClientConfig) error {
-	wgClient, wgErr := wgctrl.New()
-	if wgErr != nil {
-		return wgErr
-	}
-	removeIface := cfg.Node.Interface
-	queryAddr := cfg.Node.PrimaryAddress()
-	if ncutils.IsMac() {
-		var macIface string
-		macIface, wgErr = local.GetMacIface(queryAddr)
-		if wgErr == nil && removeIface != "" {
-			removeIface = macIface
-		}
-	}
-	dev, devErr := wgClient.Device(removeIface)
-	if devErr != nil {
-		return fmt.Errorf("error flushing routes %w", devErr)
-	}
-	local.FlushPeerRoutes(removeIface, queryAddr, dev.Peers[:])
-	_, cidr, cidrErr := net.ParseCIDR(cfg.NetworkSettings.AddressRange)
-	if cidrErr != nil {
-		return fmt.Errorf("error flushing routes %w", cidrErr)
-	}
-	local.RemoveCIDRRoute(removeIface, queryAddr, cidr)
-	return nil
-}
-
-// DeleteInterface - delete an interface of a network
-func DeleteInterface(ifacename string, postdown string) error {
-	return wireguard.RemoveConf(ifacename, true)
-}
-
-// WipeLocal - wipes local instance
-func WipeLocal(cfg *config.ClientConfig) error {
-	if err := wireguard.RemoveConf(cfg.Node.Interface, true); err == nil {
-		logger.Log(1, "network:", cfg.Node.Network, "removed WireGuard interface: ", cfg.Node.Interface)
-	} else if strings.Contains(err.Error(), "does not exist") {
-		err = nil
-	}
-	dir := ncutils.GetNetclientPathSpecific()
-	fail := false
-	files, err := filepath.Glob(dir + "*" + cfg.Node.Network)
-	if err != nil {
-		logger.Log(0, "no matching files", err.Error())
-		fail = true
-	}
-	for _, file := range files {
-		if err := os.Remove(file); err != nil {
-			logger.Log(0, "failed to delete file", file, err.Error())
-			fail = true
-		}
-	}
-
-	if cfg.Node.Interface != "" {
-		if ncutils.FileExists(dir + cfg.Node.Interface + ".conf") {
-			if err := os.Remove(dir + cfg.Node.Interface + ".conf"); err != nil {
-				logger.Log(0, err.Error())
-				fail = true
-			}
-		}
-	}
-	if fail {
-		return errors.New("not all files were deleted")
-	}
-	return nil
-}
-
-// GetNetmakerPath - gets netmaker path locally
-func GetNetmakerPath() string {
-	return LINUX_APP_DATA_PATH
-}
-
-// API function to interact with netmaker api endpoints. response from endpoint is returned
-func API(data any, method, url, authorization string) (*http.Response, error) {
-	var request *http.Request
-	var err error
-	if data != "" {
-		payload, err := json.Marshal(data)
-		if err != nil {
-			return nil, fmt.Errorf("error encoding data %w", err)
-		}
-		request, err = http.NewRequest(method, url, bytes.NewBuffer(payload))
-		if err != nil {
-			return nil, fmt.Errorf("error creating http request %w", err)
-		}
-		request.Header.Set("Content-Type", "application/json")
-	} else {
-		request, err = http.NewRequest(method, url, nil)
-		if err != nil {
-			return nil, fmt.Errorf("error creating http request %w", err)
-		}
-	}
-	if authorization != "" {
-		request.Header.Set("authorization", "Bearer "+authorization)
-	}
-	request.Header.Set("requestfrom", "node")
-	return HTTPClient.Do(request)
-}
-
-// Authenticate authenticates with api to permit subsequent interactions with the api
-func Authenticate(cfg *config.ClientConfig) (string, error) {
-
-	pass, err := os.ReadFile(ncutils.GetNetclientPathSpecific() + "secret-" + cfg.Network)
-	if err != nil {
-		return "", fmt.Errorf("could not read secrets file %w", err)
-	}
-	data := models.AuthParams{
-		MacAddress: cfg.Node.MacAddress,
-		ID:         cfg.Node.ID,
-		Password:   string(pass),
-	}
-	url := "https://" + cfg.Server.API + "/api/nodes/adm/" + cfg.Network + "/authenticate"
-	response, err := API(data, http.MethodPost, url, "")
-	if err != nil {
-		return "", err
-	}
-	defer response.Body.Close()
-	if response.StatusCode != http.StatusOK {
-		bodybytes, _ := io.ReadAll(response.Body)
-		return "", fmt.Errorf("failed to authenticate %s %s", response.Status, string(bodybytes))
-	}
-	resp := models.SuccessResponse{}
-	if err := json.NewDecoder(response.Body).Decode(&resp); err != nil {
-		return "", fmt.Errorf("error decoding respone %w", err)
-	}
-	tokenData := resp.Response.(map[string]interface{})
-	token := tokenData["AuthToken"]
-	return token.(string), nil
-}
-
-// RegisterWithServer calls the register endpoint with privatekey and commonname - api returns ca and client certificate
-func SetServerInfo(cfg *config.ClientConfig) error {
-	cfg, err := config.ReadConfig(cfg.Network)
-	if err != nil {
-		return err
-	}
-	url := "https://" + cfg.Server.API + "/api/server/getserverinfo"
-	logger.Log(1, "server at "+url)
-
-	token, err := Authenticate(cfg)
-	if err != nil {
-		return err
-	}
-	response, err := API("", http.MethodGet, url, token)
-	if err != nil {
-		return err
-	}
-	if response.StatusCode != http.StatusOK {
-		return errors.New(response.Status)
-	}
-	var resp models.ServerConfig
-	if err := json.NewDecoder(response.Body).Decode(&resp); err != nil {
-		return errors.New("unmarshal cert error " + err.Error())
-	}
-
-	// set broker information on register
-	cfg.Server.Server = resp.Server
-	cfg.Server.MQPort = resp.MQPort
-
-	if err = config.ModServerConfig(&cfg.Server, cfg.Node.Network); err != nil {
-		logger.Log(0, "error overwriting config with broker information: "+err.Error())
-	}
-
-	return nil
-}
-
-func informPortChange(node *models.Node) {
-	if node.ListenPort == 0 {
-		logger.Log(0, "network:", node.Network, "UDP hole punching enabled for node", node.Name)
-	} else {
-		logger.Log(0, "network:", node.Network, "node", node.Name, "is using port", strconv.Itoa(int(node.ListenPort)))
-	}
-}

+ 0 - 60
netclient/functions/connection.go

@@ -1,60 +0,0 @@
-package functions
-
-import (
-	"fmt"
-
-	"github.com/gravitl/netmaker/netclient/config"
-	"github.com/gravitl/netmaker/netclient/daemon"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-	"github.com/gravitl/netmaker/netclient/wireguard"
-)
-
-// Connect - will attempt to connect a node on given network
-func Connect(network string) error {
-	cfg, err := config.ReadConfig(network)
-	if err != nil {
-		return err
-	}
-	if cfg.Node.Connected == "yes" {
-		return fmt.Errorf("node already connected")
-	}
-	cfg.Node.Connected = "yes"
-	filePath := ncutils.GetNetclientPathSpecific() + cfg.Node.Interface + ".conf"
-
-	if err = wireguard.ApplyConf(&cfg.Node, cfg.Node.Interface, filePath); err != nil {
-		return err
-	}
-	if err := setupMQTTSingleton(cfg); err != nil {
-		return err
-	}
-	if err := PublishNodeUpdate(cfg); err != nil {
-		return err
-	}
-	daemon.Restart()
-	return nil
-}
-
-// Disconnect - attempts to disconnect a node on given network
-func Disconnect(network string) error {
-	cfg, err := config.ReadConfig(network)
-	if err != nil {
-		return err
-	}
-	if cfg.Node.Connected == "no" {
-		return fmt.Errorf("node already disconnected")
-	}
-	cfg.Node.Connected = "no"
-	filePath := ncutils.GetNetclientPathSpecific() + cfg.Node.Interface + ".conf"
-
-	if err = wireguard.ApplyConf(&cfg.Node, cfg.Node.Interface, filePath); err != nil {
-		return err
-	}
-	if err := setupMQTTSingleton(cfg); err != nil {
-		return err
-	}
-	if err := PublishNodeUpdate(cfg); err != nil {
-		return err
-	}
-	daemon.Restart()
-	return nil
-}

+ 0 - 350
netclient/functions/daemon.go

@@ -1,350 +0,0 @@
-package functions
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"os"
-	"os/signal"
-	"strings"
-	"sync"
-	"syscall"
-	"time"
-
-	mqtt "github.com/eclipse/paho.mqtt.golang"
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/mq"
-	"github.com/gravitl/netmaker/netclient/auth"
-	"github.com/gravitl/netmaker/netclient/config"
-	"github.com/gravitl/netmaker/netclient/global_settings"
-	"github.com/gravitl/netmaker/netclient/local"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-	"github.com/gravitl/netmaker/netclient/wireguard"
-	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
-)
-
-var messageCache = new(sync.Map)
-
-var serverSet map[string]bool
-
-var mqclient mqtt.Client
-
-const lastNodeUpdate = "lnu"
-const lastPeerUpdate = "lpu"
-
-type cachedMessage struct {
-	Message  string
-	LastSeen time.Time
-}
-
-// Daemon runs netclient daemon from command line
-func Daemon() error {
-	logger.Log(0, "netclient daemon started -- version:", ncutils.Version)
-	UpdateClientConfig()
-	if err := ncutils.SavePID(); err != nil {
-		return err
-	}
-	// reference required to eliminate unused statticcheck
-	serverSet = make(map[string]bool)
-	serverSet["dummy"] = false
-	// set ipforwarding on startup
-	err := local.SetIPForwarding()
-	if err != nil {
-		logger.Log(0, err.Error())
-	}
-
-	// == add waitgroup and cancel for checkin routine ==
-	wg := sync.WaitGroup{}
-	quit := make(chan os.Signal, 1)
-	reset := make(chan os.Signal, 1)
-	signal.Notify(quit, syscall.SIGTERM, os.Interrupt)
-	signal.Notify(reset, syscall.SIGHUP)
-	cancel := startGoRoutines(&wg)
-	for {
-		select {
-		case <-quit:
-			cancel()
-			logger.Log(0, "shutting down netclient daemon")
-			wg.Wait()
-			if mqclient != nil {
-				mqclient.Disconnect(250)
-			}
-			logger.Log(0, "shutdown complete")
-			return nil
-		case <-reset:
-			logger.Log(0, "received reset")
-			cancel()
-			wg.Wait()
-			if mqclient != nil {
-				mqclient.Disconnect(250)
-			}
-			logger.Log(0, "restarting daemon")
-			cancel = startGoRoutines(&wg)
-		}
-	}
-}
-
-func startGoRoutines(wg *sync.WaitGroup) context.CancelFunc {
-	ctx, cancel := context.WithCancel(context.Background())
-	serverSet := make(map[string]bool)
-	networks, _ := ncutils.GetSystemNetworks()
-	for _, network := range networks {
-		logger.Log(3, "initializing network", network)
-		cfg := config.ClientConfig{}
-		cfg.Network = network
-		cfg.ReadConfig()
-		if cfg.Node.Connected == "yes" {
-			if err := wireguard.ApplyConf(&cfg.Node, cfg.Node.Interface, ncutils.GetNetclientPathSpecific()+cfg.Node.Interface+".conf"); err != nil {
-				logger.Log(0, "failed to start ", cfg.Node.Interface, "wg interface", err.Error())
-			}
-			if cfg.PublicIPService != "" {
-				global_settings.PublicIPServices[network] = cfg.PublicIPService
-			}
-		}
-
-		server := cfg.Server.Server
-		if !serverSet[server] {
-			// == subscribe to all nodes for each on machine ==
-			serverSet[server] = true
-			logger.Log(1, "started daemon for server ", server)
-			local.SetNetmakerDomainRoute(cfg.Server.API)
-			wg.Add(1)
-			go messageQueue(ctx, wg, &cfg)
-		}
-	}
-	wg.Add(1)
-	go Checkin(ctx, wg)
-	return cancel
-}
-
-// UpdateKeys -- updates private key and returns new publickey
-func UpdateKeys(nodeCfg *config.ClientConfig, client mqtt.Client) error {
-	logger.Log(0, "interface:", nodeCfg.Node.Interface, "received message to update wireguard keys for network ", nodeCfg.Network)
-	key, err := wgtypes.GeneratePrivateKey()
-	if err != nil {
-		logger.Log(0, "network:", nodeCfg.Node.Network, "error generating privatekey ", err.Error())
-		return err
-	}
-	file := ncutils.GetNetclientPathSpecific() + nodeCfg.Node.Interface + ".conf"
-	if err := wireguard.UpdatePrivateKey(file, key.String()); err != nil {
-		logger.Log(0, "network:", nodeCfg.Node.Network, "error updating wireguard key ", err.Error())
-		return err
-	}
-	if storeErr := wireguard.StorePrivKey(key.String(), nodeCfg.Network); storeErr != nil {
-		logger.Log(0, "network:", nodeCfg.Network, "failed to save private key", storeErr.Error())
-		return storeErr
-	}
-
-	nodeCfg.Node.PublicKey = key.PublicKey().String()
-	PublishNodeUpdate(nodeCfg)
-	return nil
-}
-
-// == Private ==
-
-// sets MQ client subscriptions for a specific node config
-// should be called for each node belonging to a given server
-func setSubscriptions(client mqtt.Client, nodeCfg *config.ClientConfig) {
-	if token := client.Subscribe(fmt.Sprintf("update/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID), 0, mqtt.MessageHandler(NodeUpdate)); token.WaitTimeout(mq.MQ_TIMEOUT*time.Second) && token.Error() != nil {
-		if token.Error() == nil {
-			logger.Log(0, "network:", nodeCfg.Node.Network, "connection timeout")
-		} else {
-			logger.Log(0, "network:", nodeCfg.Node.Network, token.Error().Error())
-		}
-		return
-	}
-	logger.Log(3, fmt.Sprintf("subscribed to node updates for node %s update/%s/%s", nodeCfg.Node.Name, nodeCfg.Node.Network, nodeCfg.Node.ID))
-	if token := client.Subscribe(fmt.Sprintf("peers/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID), 0, mqtt.MessageHandler(UpdatePeers)); token.Wait() && token.Error() != nil {
-		logger.Log(0, "network", nodeCfg.Node.Network, token.Error().Error())
-		return
-	}
-	logger.Log(3, fmt.Sprintf("subscribed to peer updates for node %s peers/%s/%s", nodeCfg.Node.Name, nodeCfg.Node.Network, nodeCfg.Node.ID))
-}
-
-// on a delete usually, pass in the nodecfg to unsubscribe client broker communications
-// for the node in nodeCfg
-func unsubscribeNode(client mqtt.Client, nodeCfg *config.ClientConfig) {
-	client.Unsubscribe(fmt.Sprintf("update/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID))
-	var ok = true
-	if token := client.Unsubscribe(fmt.Sprintf("update/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID)); token.WaitTimeout(mq.MQ_TIMEOUT*time.Second) && token.Error() != nil {
-		if token.Error() == nil {
-			logger.Log(1, "network:", nodeCfg.Node.Network, "unable to unsubscribe from updates for node ", nodeCfg.Node.Name, "\n", "connection timeout")
-		} else {
-			logger.Log(1, "network:", nodeCfg.Node.Network, "unable to unsubscribe from updates for node ", nodeCfg.Node.Name, "\n", token.Error().Error())
-		}
-		ok = false
-	}
-	if token := client.Unsubscribe(fmt.Sprintf("peers/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID)); token.WaitTimeout(mq.MQ_TIMEOUT*time.Second) && token.Error() != nil {
-		if token.Error() == nil {
-			logger.Log(1, "network:", nodeCfg.Node.Network, "unable to unsubscribe from peer updates for node", nodeCfg.Node.Name, "\n", "connection timeout")
-		} else {
-			logger.Log(1, "network:", nodeCfg.Node.Network, "unable to unsubscribe from peer updates for node", nodeCfg.Node.Name, "\n", token.Error().Error())
-		}
-		ok = false
-	}
-	if ok {
-		logger.Log(1, "network:", nodeCfg.Node.Network, "successfully unsubscribed node ", nodeCfg.Node.ID, " : ", nodeCfg.Node.Name)
-	}
-}
-
-// sets up Message Queue and subsribes/publishes updates to/from server
-// the client should subscribe to ALL nodes that exist on server locally
-func messageQueue(ctx context.Context, wg *sync.WaitGroup, cfg *config.ClientConfig) {
-	defer wg.Done()
-	logger.Log(0, "network:", cfg.Node.Network, "netclient message queue started for server:", cfg.Server.Server)
-	err := setupMQTT(cfg)
-	if err != nil {
-		logger.Log(0, "unable to connect to broker", cfg.Server.Server, err.Error())
-		return
-	}
-	//defer mqclient.Disconnect(250)
-	<-ctx.Done()
-	logger.Log(0, "shutting down message queue for server", cfg.Server.Server)
-}
-
-// func setMQTTSingenton creates a connection to broker for single use (ie to publish a message)
-// only to be called from cli (eg. connect/disconnect, join, leave) and not from daemon ---
-func setupMQTTSingleton(cfg *config.ClientConfig) error {
-	opts := mqtt.NewClientOptions()
-	server := cfg.Server.Server
-	port := cfg.Server.MQPort
-	pass, err := os.ReadFile(ncutils.GetNetclientPathSpecific() + "secret-" + cfg.Network)
-	if err != nil {
-		return fmt.Errorf("could not read secrets file %w", err)
-	}
-	opts.AddBroker("wss://" + server + ":" + port)
-	opts.SetUsername(cfg.Node.ID)
-	opts.SetPassword(string(pass))
-	mqclient = mqtt.NewClient(opts)
-	var connecterr error
-	opts.SetClientID(ncutils.MakeRandomString(23))
-	if token := mqclient.Connect(); !token.WaitTimeout(30*time.Second) || token.Error() != nil {
-		logger.Log(0, "unable to connect to broker, retrying ...")
-		if token.Error() == nil {
-			connecterr = errors.New("connect timeout")
-		} else {
-			connecterr = token.Error()
-		}
-	}
-	return connecterr
-}
-
-// setupMQTT creates a connection to broker and returns client
-// this function is primarily used to create a connection to publish to the broker
-func setupMQTT(cfg *config.ClientConfig) error {
-	opts := mqtt.NewClientOptions()
-	server := cfg.Server.Server
-	port := cfg.Server.MQPort
-	pass, err := os.ReadFile(ncutils.GetNetclientPathSpecific() + "secret-" + cfg.Network)
-	if err != nil {
-		return fmt.Errorf("could not read secrets file %w", err)
-	}
-	opts.AddBroker(fmt.Sprintf("wss://%s:%s", server, port))
-	opts.SetUsername(cfg.Node.ID)
-	opts.SetPassword(string(pass))
-	opts.SetClientID(ncutils.MakeRandomString(23))
-	opts.SetDefaultPublishHandler(All)
-	opts.SetAutoReconnect(true)
-	opts.SetConnectRetry(true)
-	opts.SetConnectRetryInterval(time.Second << 2)
-	opts.SetKeepAlive(time.Minute >> 1)
-	opts.SetWriteTimeout(time.Minute)
-
-	opts.SetOnConnectHandler(func(client mqtt.Client) {
-		networks, err := ncutils.GetSystemNetworks()
-		if err != nil {
-			logger.Log(0, "error retriving networks", err.Error())
-		}
-		for _, network := range networks {
-			var currNodeCfg config.ClientConfig
-			currNodeCfg.Network = network
-			currNodeCfg.ReadConfig()
-			setSubscriptions(client, &currNodeCfg)
-		}
-	})
-	opts.SetOrderMatters(true)
-	opts.SetResumeSubs(true)
-	opts.SetConnectionLostHandler(func(c mqtt.Client, e error) {
-		logger.Log(0, "network:", cfg.Node.Network, "detected broker connection lost for", cfg.Server.Server)
-	})
-	mqclient = mqtt.NewClient(opts)
-	var connecterr error
-	for count := 0; count < 3; count++ {
-		connecterr = nil
-		if token := mqclient.Connect(); !token.WaitTimeout(30*time.Second) || token.Error() != nil {
-			logger.Log(0, "unable to connect to broker, retrying ...")
-			if token.Error() == nil {
-				connecterr = errors.New("connect timeout")
-			} else {
-				connecterr = token.Error()
-			}
-			if err := checkBroker(cfg.Server.Server, cfg.Server.MQPort); err != nil {
-				logger.Log(0, "could not connect to broker", cfg.Server.Server, err.Error())
-			}
-		}
-	}
-	if connecterr != nil {
-		logger.Log(0, "failed to establish connection to broker: ", connecterr.Error())
-		return connecterr
-	}
-
-	return nil
-}
-
-// publishes a message to server to update peers on this peer's behalf
-func publishSignal(nodeCfg *config.ClientConfig, signal byte) error {
-	if err := publish(nodeCfg, fmt.Sprintf("signal/%s", nodeCfg.Node.ID), []byte{signal}, 1); err != nil {
-		return err
-	}
-	return nil
-}
-
-func parseNetworkFromTopic(topic string) string {
-	return strings.Split(topic, "/")[1]
-}
-
-// should only ever use node client configs
-func decryptMsg(nodeCfg *config.ClientConfig, msg []byte) ([]byte, error) {
-	if len(msg) <= 24 { // make sure message is of appropriate length
-		return nil, fmt.Errorf("recieved invalid message from broker %v", msg)
-	}
-
-	// setup the keys
-	diskKey, keyErr := auth.RetrieveTrafficKey(nodeCfg.Node.Network)
-	if keyErr != nil {
-		return nil, keyErr
-	}
-
-	serverPubKey, err := ncutils.ConvertBytesToKey(nodeCfg.Node.TrafficKeys.Server)
-	if err != nil {
-		return nil, err
-	}
-
-	return ncutils.DeChunk(msg, serverPubKey, diskKey)
-}
-
-// == Message Caches ==
-
-func insert(network, which, cache string) {
-	var newMessage = cachedMessage{
-		Message:  cache,
-		LastSeen: time.Now(),
-	}
-	messageCache.Store(fmt.Sprintf("%s%s", network, which), newMessage)
-}
-
-func read(network, which string) string {
-	val, isok := messageCache.Load(fmt.Sprintf("%s%s", network, which))
-	if isok {
-		var readMessage = val.(cachedMessage) // fetch current cached message
-		if readMessage.LastSeen.IsZero() {
-			return ""
-		}
-		if time.Now().After(readMessage.LastSeen.Add(time.Hour * 24)) { // check if message has been there over a minute
-			messageCache.Delete(fmt.Sprintf("%s%s", network, which)) // remove old message if expired
-			return ""
-		}
-		return readMessage.Message // return current message if not expired
-	}
-	return ""
-}

+ 0 - 19
netclient/functions/install.go

@@ -1,19 +0,0 @@
-package functions
-
-import (
-	"time"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/netclient/daemon"
-)
-
-//Install - installs binary/daemon
-func Install() error {
-	daemon.Stop()
-	if err := daemon.InstallDaemon(); err != nil {
-		logger.Log(0, "error installing daemon", err.Error())
-		return err
-	}
-	time.Sleep(time.Second * 5)
-	return daemon.Restart()
-}

+ 0 - 412
netclient/functions/join.go

@@ -1,412 +0,0 @@
-package functions
-
-import (
-	"crypto/rand"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"io"
-	"log"
-	"net/http"
-	"os"
-	"os/signal"
-	"runtime"
-	"strings"
-	"syscall"
-
-	"github.com/gorilla/websocket"
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
-	"github.com/gravitl/netmaker/netclient/auth"
-	"github.com/gravitl/netmaker/netclient/config"
-	"github.com/gravitl/netmaker/netclient/daemon"
-	"github.com/gravitl/netmaker/netclient/global_settings"
-	"github.com/gravitl/netmaker/netclient/local"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-	"github.com/gravitl/netmaker/netclient/wireguard"
-	"golang.org/x/crypto/nacl/box"
-	"golang.org/x/term"
-	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
-)
-
-// JoinViaSso - Handles the Single Sign-On flow on the end point VPN client side
-// Contacts the server provided by the user (and thus specified in cfg.SsoServer)
-// get the URL to authenticate with a provider and shows the user the URL.
-// Then waits for user to authenticate with the URL.
-// Upon user successful auth flow finished - server should return access token to the requested network
-// Otherwise the error message is sent which can be displayed to the user
-func JoinViaSSo(cfg *config.ClientConfig, privateKey string) error {
-
-	// User must tell us which network he is joining
-	if cfg.Node.Network == "" {
-		return errors.New("no network provided")
-	}
-
-	// Prepare a channel for interrupt
-	// Channel to listen for interrupt signal to terminate gracefully
-	interrupt := make(chan os.Signal, 1)
-	// Notify the interrupt channel for SIGINT
-	signal.Notify(interrupt, os.Interrupt)
-
-	// Web Socket is used, construct the URL accordingly ...
-	socketUrl := fmt.Sprintf("wss://%s/api/oauth/node-handler", cfg.SsoServer)
-	// Dial the netmaker server controller
-	conn, _, err := websocket.DefaultDialer.Dial(socketUrl, nil)
-	if err != nil {
-		logger.Log(0, fmt.Sprintf("error connecting to %s : %s", cfg.Server.API, err.Error()))
-		return err
-	}
-	// Don't forget to close when finished
-	defer conn.Close()
-	// Find and set node MacAddress
-	if cfg.Node.MacAddress == "" {
-		macs, err := ncutils.GetMacAddr()
-		if err != nil {
-			//if macaddress can't be found set to random string
-			cfg.Node.MacAddress = ncutils.MakeRandomString(18)
-		} else {
-			cfg.Node.MacAddress = macs[0]
-		}
-	}
-
-	var loginMsg promodels.LoginMsg
-	loginMsg.Mac = cfg.Node.MacAddress
-	loginMsg.Network = cfg.Node.Network
-	if global_settings.User != "" {
-		fmt.Printf("Continuing with user, %s.\nPlease input password:\n", global_settings.User)
-		pass, err := term.ReadPassword(int(syscall.Stdin))
-		if err != nil || string(pass) == "" {
-			logger.FatalLog("no password provided, exiting")
-		}
-		loginMsg.User = global_settings.User
-		loginMsg.Password = string(pass)
-		fmt.Println("attempting login...")
-	}
-
-	msgTx, err := json.Marshal(loginMsg)
-	if err != nil {
-		logger.Log(0, fmt.Sprintf("failed to marshal message %+v", loginMsg))
-		return err
-	}
-	err = conn.WriteMessage(websocket.TextMessage, []byte(msgTx))
-	if err != nil {
-		logger.FatalLog("Error during writing to websocket:", err.Error())
-		return err
-	}
-
-	// if user provided, server will handle authentication
-	if loginMsg.User == "" {
-		// We are going to get instructions on how to authenticate
-		// Wait to receive something from server
-		_, msg, err := conn.ReadMessage()
-		if err != nil {
-			return err
-		}
-		// Print message from the netmaker controller to the user
-		fmt.Printf("Please visit:\n %s \n to authenticate", string(msg))
-	}
-
-	// Now the user is authenticating and we need to block until received
-	// An answer from the server.
-	// Server waits ~5 min - If takes too long timeout will be triggered by the server
-	done := make(chan struct{})
-	defer close(done)
-	// Following code will run in a separate go routine
-	// it reads a message from the server which either contains 'AccessToken:' string or not
-	// if not - then it contains an Error to display.
-	// if yes - then AccessToken is to be used to proceed joining the network
-	go func() {
-		for {
-			msgType, msg, err := conn.ReadMessage()
-			if err != nil {
-				if msgType < 0 {
-					logger.Log(1, "received close message from server")
-					done <- struct{}{}
-					return
-				}
-				// Error reading a message from the server
-				if !strings.Contains(err.Error(), "normal") {
-					logger.Log(0, "read:", err.Error())
-				}
-				return
-			}
-
-			if msgType == websocket.CloseMessage {
-				logger.Log(1, "received close message from server")
-				done <- struct{}{}
-				return
-			}
-			// Get the access token from the response
-			if strings.Contains(string(msg), "AccessToken: ") {
-				// Access was granted
-				rxToken := strings.TrimPrefix(string(msg), "AccessToken: ")
-				accesstoken, err := config.ParseAccessToken(rxToken)
-				if err != nil {
-					logger.Log(0, fmt.Sprintf("failed to parse received access token %s,err=%s\n", accesstoken, err.Error()))
-					return
-				}
-
-				cfg.Network = accesstoken.ClientConfig.Network
-				cfg.Node.Network = accesstoken.ClientConfig.Network
-				cfg.AccessKey = accesstoken.ClientConfig.Key
-				cfg.Node.LocalRange = accesstoken.ClientConfig.LocalRange
-				//cfg.Server.Server = accesstoken.ServerConfig.Server
-				cfg.Server.API = accesstoken.APIConnString
-			} else {
-				// Access was not granted. Display a message from the server
-				logger.Log(0, "Message from server:", string(msg))
-				cfg.AccessKey = ""
-				return
-			}
-		}
-	}()
-
-	for {
-		select {
-		case <-done:
-			logger.Log(1, "finished")
-			return nil
-		case <-interrupt:
-			logger.Log(0, "interrupt received, closing connection")
-			// Cleanly close the connection by sending a close message and then
-			// waiting (with timeout) for the server to close the connection.
-			err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
-			if err != nil {
-				logger.Log(0, "write close:", err.Error())
-				return err
-			}
-			return nil
-		}
-	}
-}
-
-// JoinNetwork - helps a client join a network
-func JoinNetwork(cfg *config.ClientConfig, privateKey string) error {
-	if cfg.Node.Network == "" {
-		return errors.New("no network provided")
-	}
-
-	var err error
-	if local.HasNetwork(cfg.Network) {
-		err := errors.New("ALREADY_INSTALLED. Netclient appears to already be installed for " + cfg.Network + ". To re-install, please remove by executing 'sudo netclient leave -n " + cfg.Network + "'. Then re-run the install command.")
-		return err
-	}
-
-	err = config.Write(cfg, cfg.Network)
-	if err != nil {
-		return err
-	}
-	if cfg.Node.Password == "" {
-		cfg.Node.Password = logic.GenPassWord()
-	}
-	//check if ListenPort was set on command line
-	if cfg.Node.ListenPort != 0 {
-		cfg.Node.UDPHolePunch = "no"
-	}
-	var trafficPubKey, trafficPrivKey, errT = box.GenerateKey(rand.Reader) // generate traffic keys
-	if errT != nil {
-		return errT
-	}
-
-	// == handle keys ==
-	if err = auth.StoreSecret(cfg.Node.Password, cfg.Node.Network); err != nil {
-		return err
-	}
-
-	if err = auth.StoreTrafficKey(trafficPrivKey, cfg.Node.Network); err != nil {
-		return err
-	}
-
-	trafficPubKeyBytes, err := ncutils.ConvertKeyToBytes(trafficPubKey)
-	if err != nil {
-		return err
-	} else if trafficPubKeyBytes == nil {
-		return fmt.Errorf("traffic key is nil")
-	}
-
-	cfg.Node.TrafficKeys.Mine = trafficPubKeyBytes
-	cfg.Node.TrafficKeys.Server = nil
-	// == end handle keys ==
-
-	if cfg.Node.LocalAddress == "" {
-		intIP, err := getPrivateAddr()
-		if err == nil {
-			cfg.Node.LocalAddress = intIP
-		} else {
-			logger.Log(1, "network:", cfg.Network, "error retrieving private address: ", err.Error())
-		}
-	}
-	if len(cfg.Node.Interfaces) == 0 {
-		ip, err := getInterfaces()
-		if err != nil {
-			logger.Log(0, "failed to retrive local interfaces", err.Error())
-		} else {
-			cfg.Node.Interfaces = *ip
-		}
-	}
-
-	// set endpoint if blank. set to local if local net, retrieve from function if not
-	if cfg.Node.Endpoint == "" {
-		if cfg.Node.IsLocal == "yes" && cfg.Node.LocalAddress != "" {
-			cfg.Node.Endpoint = cfg.Node.LocalAddress
-		} else {
-			cfg.Node.Endpoint, err = ncutils.GetPublicIP(cfg.Server.API)
-		}
-		if err != nil || cfg.Node.Endpoint == "" {
-			logger.Log(0, "network:", cfg.Network, "error setting cfg.Node.Endpoint.")
-			return err
-		}
-	}
-	// Generate and set public/private WireGuard Keys
-	if privateKey == "" {
-		wgPrivatekey, err := wgtypes.GeneratePrivateKey()
-		if err != nil {
-			log.Fatal(err)
-		}
-		privateKey = wgPrivatekey.String()
-		cfg.Node.PublicKey = wgPrivatekey.PublicKey().String()
-	}
-	// Find and set node MacAddress
-	if cfg.Node.MacAddress == "" {
-		macs, err := ncutils.GetMacAddr()
-		if err != nil || len(macs) == 0 {
-			//if macaddress can't be found set to random string
-			cfg.Node.MacAddress = ncutils.MakeRandomString(18)
-		} else {
-			cfg.Node.MacAddress = macs[0]
-		}
-	}
-
-	if ncutils.IsFreeBSD() {
-		cfg.Node.UDPHolePunch = "no"
-		cfg.Node.FirewallInUse = models.FIREWALL_IPTABLES // nftables not supported by FreeBSD
-	}
-
-	if cfg.Node.FirewallInUse == "" {
-		if ncutils.IsNFTablesPresent() {
-			cfg.Node.FirewallInUse = models.FIREWALL_NFTABLES
-		} else if ncutils.IsIPTablesPresent() {
-			cfg.Node.FirewallInUse = models.FIREWALL_IPTABLES
-		} else {
-			cfg.Node.FirewallInUse = models.FIREWALL_NONE
-		}
-	}
-
-	// make sure name is appropriate, if not, give blank name
-	cfg.Node.Name = formatName(cfg.Node)
-	cfg.Node.OS = runtime.GOOS
-	cfg.Node.Version = ncutils.Version
-	cfg.Node.AccessKey = cfg.AccessKey
-	//not sure why this is needed ... setnode defaults should take care of this on server
-	cfg.Node.IPForwarding = "yes"
-	logger.Log(0, "joining "+cfg.Network+" at "+cfg.Server.API)
-	url := "https://" + cfg.Server.API + "/api/nodes/" + cfg.Network
-	response, err := API(cfg.Node, http.MethodPost, url, cfg.AccessKey)
-	if err != nil {
-		return fmt.Errorf("error creating node %w", err)
-	}
-	defer response.Body.Close()
-	if response.StatusCode != http.StatusOK {
-		bodybytes, _ := io.ReadAll(response.Body)
-		return fmt.Errorf("error creating node %s %s", response.Status, string(bodybytes))
-	}
-	var nodeGET models.NodeGet
-	if err := json.NewDecoder(response.Body).Decode(&nodeGET); err != nil {
-		//not sure the next line will work as response.Body probably needs to be reset before it can be read again
-		bodybytes, _ := io.ReadAll(response.Body)
-		return fmt.Errorf("error decoding node from server %w %s", err, string(bodybytes))
-	}
-	node := nodeGET.Node
-	if nodeGET.Peers == nil {
-		nodeGET.Peers = []wgtypes.PeerConfig{}
-	}
-
-	// safety check. If returned node from server is local, but not currently configured as local, set to local addr
-	if cfg.Node.IsLocal != "yes" && node.IsLocal == "yes" && node.LocalRange != "" {
-		node.LocalAddress, err = ncutils.GetLocalIP(node.LocalRange)
-		if err != nil {
-			return err
-		}
-		node.Endpoint = node.LocalAddress
-	}
-	if ncutils.IsFreeBSD() {
-		node.UDPHolePunch = "no"
-		cfg.Node.IsStatic = "yes"
-	}
-	cfg.Server = nodeGET.ServerConfig
-
-	err = wireguard.StorePrivKey(privateKey, cfg.Network)
-	if err != nil {
-		return err
-	}
-	if node.IsPending == "yes" {
-		logger.Log(0, "network:", cfg.Network, "node is marked as PENDING.")
-		logger.Log(0, "network:", cfg.Network, "awaiting approval from Admin before configuring WireGuard.")
-		if cfg.Daemon != "off" {
-			return daemon.InstallDaemon()
-		}
-	}
-	logger.Log(1, "network:", cfg.Node.Network, "node created on remote server...updating configs")
-	err = ncutils.ModPort(&node)
-	if err != nil {
-		return err
-	}
-	informPortChange(&node)
-
-	err = config.ModNodeConfig(&node)
-	if err != nil {
-		return err
-	}
-	err = config.ModServerConfig(&cfg.Server, node.Network)
-	if err != nil {
-		return err
-	}
-	// attempt to make backup
-	if err = config.SaveBackup(node.Network); err != nil {
-		logger.Log(0, "network:", node.Network, "failed to make backup, node will not auto restore if config is corrupted")
-	}
-
-	local.SetNetmakerDomainRoute(cfg.Server.API)
-	cfg.Node = node
-	logger.Log(0, "starting wireguard")
-	err = wireguard.InitWireguard(&node, privateKey, nodeGET.Peers[:])
-	if err != nil {
-		return err
-	}
-	if cfg.Server.Server == "" {
-		return errors.New("did not receive broker address from registration")
-	}
-	if cfg.Daemon == "install" || ncutils.IsFreeBSD() {
-		err = daemon.InstallDaemon()
-		if err != nil {
-			return err
-		}
-	}
-
-	if err := daemon.Restart(); err != nil {
-		logger.Log(3, "daemon restart failed:", err.Error())
-		if err := daemon.Start(); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-// format name appropriately. Set to blank on failure
-func formatName(node models.Node) string {
-	// Logic to properly format name
-	if !node.NameInNodeCharSet() {
-		node.Name = ncutils.DNSFormatString(node.Name)
-	}
-	if len(node.Name) > models.MAX_NAME_LENGTH {
-		node.Name = ncutils.ShortenString(node.Name, models.MAX_NAME_LENGTH)
-	}
-	if !node.NameInNodeCharSet() || len(node.Name) > models.MAX_NAME_LENGTH {
-		logger.Log(1, "network:", node.Network, "could not properly format name: "+node.Name)
-		logger.Log(1, "network:", node.Network, "setting name to blank")
-		node.Name = ""
-	}
-	return node.Name
-}

+ 0 - 148
netclient/functions/list.go

@@ -1,148 +0,0 @@
-package functions
-
-import (
-	"encoding/json"
-	"fmt"
-	"io"
-	"net/http"
-
-	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/config"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-)
-
-// Peer - the peer struct for list
-type Peer struct {
-	Name           string    `json:"name,omitempty"`
-	Interface      string    `json:"interface,omitempty"`
-	PrivateIPv4    string    `json:"private_ipv4,omitempty"`
-	PrivateIPv6    string    `json:"private_ipv6,omitempty"`
-	PublicKey      string    `json:"public_key,omitempty"`
-	PublicEndpoint string    `json:"public_endpoint,omitempty"`
-	Addresses      []address `json:"addresses,omitempty"`
-}
-
-// Network - the local node network representation for list command
-type Network struct {
-	Name        string `json:"name"`
-	ID          string `json:"node_id"`
-	CurrentNode Peer   `json:"current_node"`
-	Peers       []Peer `json:"peers"`
-}
-
-type address struct {
-	CIDR string `json:"cidr,omitempty"`
-	IP   string `json:"ip,omitempty"`
-}
-
-// List - lists the current peers for the local node with name and node ID
-func List(network string) ([]Network, error) {
-	nets := []Network{}
-	var err error
-	var networks []string
-	if network == "all" {
-		networks, err = ncutils.GetSystemNetworks()
-		if err != nil {
-			return nil, err
-		}
-	} else {
-		networks = append(networks, network)
-	}
-
-	for _, network := range networks {
-		net, err := getNetwork(network)
-		if err != nil {
-			logger.Log(1, network+": Could not retrieve network configuration.")
-			return nil, err
-		}
-		peers, err := getPeers(network)
-		if err == nil && len(peers) > 0 {
-			net.Peers = peers
-		}
-		nets = append(nets, net)
-	}
-
-	jsoncfg, _ := json.Marshal(struct {
-		Networks []Network `json:"networks"`
-	}{nets})
-	fmt.Println(string(jsoncfg))
-
-	return nets, nil
-}
-
-func getNetwork(network string) (Network, error) {
-	cfg, err := config.ReadConfig(network)
-	if err != nil {
-		return Network{}, fmt.Errorf("reading configuration for network %v: %w", network, err)
-	}
-	// peers, err := getPeers(network)
-	peers := []Peer{}
-	/*	if err != nil {
-		return Network{}, fmt.Errorf("listing peers for network %v: %w", network, err)
-	}*/
-	return Network{
-		Name:  network,
-		ID:    cfg.Node.ID,
-		Peers: peers,
-		CurrentNode: Peer{
-			Name:           cfg.Node.Name,
-			Interface:      cfg.Node.Interface,
-			PrivateIPv4:    cfg.Node.Address,
-			PrivateIPv6:    cfg.Node.Address6,
-			PublicEndpoint: cfg.Node.Endpoint,
-		},
-	}, nil
-}
-
-func getPeers(network string) ([]Peer, error) {
-	cfg, err := config.ReadConfig(network)
-	if err != nil {
-		return []Peer{}, err
-	}
-	token, err := Authenticate(cfg)
-	if err != nil {
-		return nil, err
-	}
-	url := "https://" + cfg.Server.API + "/api/nodes/" + cfg.Network + "/" + cfg.Node.ID
-	response, err := API("", http.MethodGet, url, token)
-	if err != nil {
-		return nil, err
-	}
-	if response.StatusCode != http.StatusOK {
-		bytes, err := io.ReadAll(response.Body)
-		if err != nil {
-			fmt.Println(err)
-		}
-		return nil, (fmt.Errorf("%s %w", string(bytes), err))
-	}
-	defer response.Body.Close()
-	var nodeGET models.NodeGet
-	if err := json.NewDecoder(response.Body).Decode(&nodeGET); err != nil {
-		return nil, fmt.Errorf("error decoding node %w", err)
-	}
-	if nodeGET.Peers == nil {
-		nodeGET.Peers = []wgtypes.PeerConfig{}
-	}
-
-	peers := []Peer{}
-	for _, peer := range nodeGET.Peers {
-		var addresses = []address{}
-		for j := range peer.AllowedIPs {
-			newAddress := address{
-				CIDR: peer.AllowedIPs[j].String(),
-				IP:   peer.AllowedIPs[j].IP.String(),
-			}
-			addresses = append(addresses, newAddress)
-		}
-		peers = append(peers, Peer{
-			PublicKey:      peer.PublicKey.String(),
-			PublicEndpoint: peer.Endpoint.String(),
-			Addresses:      addresses,
-		})
-	}
-
-	return peers, nil
-}

+ 0 - 63
netclient/functions/localport.go

@@ -1,63 +0,0 @@
-//go:build !freebsd
-
-package functions
-
-import (
-	"strconv"
-
-	"golang.zx2c4.com/wireguard/wgctrl"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/netclient/config"
-	"github.com/gravitl/netmaker/netclient/local"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-)
-
-// GetLocalListenPort - Gets the port running on the local interface
-func GetLocalListenPort(ifacename string) (int32, error) {
-	client, err := wgctrl.New()
-	if err != nil {
-		logger.Log(0, "failed to start wgctrl")
-		return 0, err
-	}
-	defer client.Close()
-	device, err := client.Device(ifacename)
-	if err != nil {
-		logger.Log(0, "failed to parse interface", ifacename)
-		return 0, err
-	}
-	return int32(device.ListenPort), nil
-}
-
-// UpdateLocalListenPort - check local port, if different, mod config and publish
-func UpdateLocalListenPort(nodeCfg *config.ClientConfig) error {
-	var err error
-	ifacename := getRealIface(nodeCfg.Node.Interface, nodeCfg.Node.Address)
-	localPort, err := GetLocalListenPort(ifacename)
-	if err != nil {
-		logger.Log(1, "network:", nodeCfg.Node.Network, "error encountered checking local listen port: ", ifacename, err.Error())
-	} else if nodeCfg.Node.LocalListenPort != localPort && localPort != 0 {
-		logger.Log(1, "network:", nodeCfg.Node.Network, "local port has changed from ", strconv.Itoa(int(nodeCfg.Node.LocalListenPort)), " to ", strconv.Itoa(int(localPort)))
-		nodeCfg.Node.LocalListenPort = localPort
-		err = config.ModNodeConfig(&nodeCfg.Node)
-		if err != nil {
-			return err
-		}
-		if err := PublishNodeUpdate(nodeCfg); err != nil {
-			logger.Log(0, "could not publish local port change", err.Error())
-		}
-	}
-	return err
-}
-
-func getRealIface(ifacename string, address string) string {
-	var deviceiface = ifacename
-	var err error
-	if ncutils.IsMac() { // if node is Mac (Darwin) get the tunnel name first
-		deviceiface, err = local.GetMacIface(address)
-		if err != nil || deviceiface == "" {
-			deviceiface = ifacename
-		}
-	}
-	return deviceiface
-}

+ 0 - 50
netclient/functions/localport_freebsd.go

@@ -1,50 +0,0 @@
-//go:build freebsd
-// +build freebsd
-
-package functions
-
-import (
-	"errors"
-	"strconv"
-	"strings"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/netclient/config"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-)
-
-// GetLocalListenPort - Gets the port running on the local interface
-func GetLocalListenPort(ifacename string) (int32, error) {
-	portstring, err := ncutils.RunCmd("wg show "+ifacename+" listen-port", false)
-	if err != nil {
-		return 0, err
-	}
-	portstring = strings.TrimSuffix(portstring, "\n")
-	i, err := strconv.ParseInt(portstring, 10, 32)
-	if err != nil {
-		return 0, err
-	} else if i == 0 {
-		return 0, errors.New("parsed port is unset or invalid")
-	}
-	return int32(i), nil
-}
-
-// UpdateLocalListenPort - check local port, if different, mod config and publish
-func UpdateLocalListenPort(nodeCfg *config.ClientConfig) error {
-	var err error
-	localPort, err := GetLocalListenPort(nodeCfg.Node.Interface)
-	if err != nil {
-		logger.Log(1, "network:", nodeCfg.Node.Network, "error encountered checking local listen port for interface : ", nodeCfg.Node.Interface, err.Error())
-	} else if nodeCfg.Node.LocalListenPort != localPort && localPort != 0 {
-		logger.Log(1, "network:", nodeCfg.Node.Network, "local port has changed from ", strconv.Itoa(int(nodeCfg.Node.LocalListenPort)), " to ", strconv.Itoa(int(localPort)))
-		nodeCfg.Node.LocalListenPort = localPort
-		err = config.ModNodeConfig(&nodeCfg.Node)
-		if err != nil {
-			return err
-		}
-		if err := PublishNodeUpdate(nodeCfg); err != nil {
-			logger.Log(0, "network:", nodeCfg.Node.Network, "could not publish local port change")
-		}
-	}
-	return err
-}

+ 0 - 333
netclient/functions/mqhandlers.go

@@ -1,333 +0,0 @@
-package functions
-
-import (
-	"encoding/json"
-	"errors"
-	"fmt"
-	"net"
-	"os"
-	"runtime"
-	"strings"
-	"time"
-
-	mqtt "github.com/eclipse/paho.mqtt.golang"
-	"github.com/guumaster/hostctl/pkg/file"
-	"github.com/guumaster/hostctl/pkg/parser"
-	"github.com/guumaster/hostctl/pkg/types"
-	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/config"
-	"github.com/gravitl/netmaker/netclient/local"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-	"github.com/gravitl/netmaker/netclient/wireguard"
-)
-
-// All -- mqtt message hander for all ('#') topics
-var All mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
-	logger.Log(0, "default message handler -- received message but not handling")
-	logger.Log(0, "topic: "+string(msg.Topic()))
-	// logger.Log(0, "Message: " + string(msg.Payload()))
-}
-
-// NodeUpdate -- mqtt message handler for /update/<NodeID> topic
-func NodeUpdate(client mqtt.Client, msg mqtt.Message) {
-	var newNode models.Node
-	var nodeCfg config.ClientConfig
-	var network = parseNetworkFromTopic(msg.Topic())
-	nodeCfg.Network = network
-	nodeCfg.ReadConfig()
-
-	data, dataErr := decryptMsg(&nodeCfg, msg.Payload())
-	if dataErr != nil {
-		return
-	}
-	err := json.Unmarshal([]byte(data), &newNode)
-	if err != nil {
-		logger.Log(0, "error unmarshalling node update data"+err.Error())
-		return
-	}
-
-	// see if cache hit, if so skip
-	var currentMessage = read(newNode.Network, lastNodeUpdate)
-	if currentMessage == string(data) {
-		return
-	}
-	insert(newNode.Network, lastNodeUpdate, string(data)) // store new message in cache
-	logger.Log(0, "network:", newNode.Network, "received message to update node "+newNode.Name)
-
-	// ensure that OS never changes
-	newNode.OS = runtime.GOOS
-	// check if interface needs to delta
-	ifaceDelta := ncutils.IfaceDelta(&nodeCfg.Node, &newNode)
-	shouldDNSChange := nodeCfg.Node.DNSOn != newNode.DNSOn
-	hubChange := nodeCfg.Node.IsHub != newNode.IsHub
-	keepaliveChange := nodeCfg.Node.PersistentKeepalive != newNode.PersistentKeepalive
-
-	nodeCfg.Node = newNode
-	switch newNode.Action {
-	case models.NODE_DELETE:
-		logger.Log(0, "network:", nodeCfg.Node.Network, " received delete request for %s", nodeCfg.Node.Name)
-		unsubscribeNode(client, &nodeCfg)
-		if err = LeaveNetwork(nodeCfg.Node.Network); err != nil {
-			if !strings.Contains("rpc error", err.Error()) {
-				logger.Log(0, "failed to leave, please check that local files for network", nodeCfg.Node.Network, "were removed")
-				return
-			}
-		}
-		logger.Log(0, nodeCfg.Node.Name, "was removed from network", nodeCfg.Node.Network)
-		return
-	case models.NODE_UPDATE_KEY:
-		// == get the current key for node ==
-		oldPrivateKey, retErr := wireguard.RetrievePrivKey(nodeCfg.Network)
-		if retErr != nil {
-			break
-		}
-		if err := UpdateKeys(&nodeCfg, client); err != nil {
-			logger.Log(0, "err updating wireguard keys, reusing last key\n", err.Error())
-			if key, parseErr := wgtypes.ParseKey(oldPrivateKey); parseErr == nil {
-				wireguard.StorePrivKey(key.String(), nodeCfg.Network)
-				nodeCfg.Node.PublicKey = key.PublicKey().String()
-			}
-		}
-		ifaceDelta = true
-	case models.NODE_FORCE_UPDATE:
-		ifaceDelta = true
-	case models.NODE_NOOP:
-	default:
-	}
-	// Save new config
-	nodeCfg.Node.Action = models.NODE_NOOP
-	if err := config.Write(&nodeCfg, nodeCfg.Network); err != nil {
-		logger.Log(0, nodeCfg.Node.Network, "error updating node configuration: ", err.Error())
-	}
-	nameserver := nodeCfg.Server.CoreDNSAddr
-	privateKey, err := wireguard.RetrievePrivKey(newNode.Network)
-	if err != nil {
-		logger.Log(0, "error reading PrivateKey "+err.Error())
-		return
-	}
-	cfgFile := ncutils.GetNetclientPathSpecific() + nodeCfg.Node.Interface + ".conf"
-
-	if newNode.ListenPort != nodeCfg.Node.LocalListenPort {
-		if err := wireguard.RemoveConf(newNode.Interface, false); err != nil {
-			logger.Log(0, "error remove interface", newNode.Interface, err.Error())
-		}
-		err = ncutils.ModPort(&newNode)
-		if err != nil {
-			logger.Log(0, "network:", nodeCfg.Node.Network, "error modifying node port on", newNode.Name, "-", err.Error())
-			return
-		}
-		ifaceDelta = true
-		informPortChange(&newNode)
-	}
-	if err := wireguard.UpdateWgInterface(cfgFile, privateKey, nameserver, newNode); err != nil {
-		logger.Log(0, "error updating wireguard config "+err.Error())
-		return
-	}
-	if keepaliveChange {
-		wireguard.UpdateKeepAlive(cfgFile, newNode.PersistentKeepalive)
-	}
-	logger.Log(0, "applying WG conf to "+cfgFile)
-	err = wireguard.ApplyConf(&nodeCfg.Node, nodeCfg.Node.Interface, cfgFile)
-	if err != nil {
-		logger.Log(0, "error restarting wg after node update -", err.Error())
-		return
-	}
-
-	time.Sleep(time.Second)
-	//	if newNode.DNSOn == "yes" {
-	//		for _, server := range newNode.NetworkSettings.DefaultServerAddrs {
-	//			if server.IsLeader {
-	//				go local.SetDNSWithRetry(newNode, server.Address)
-	//				break
-	//			}
-	//		}
-	//	}
-	if ifaceDelta { // if a change caused an ifacedelta we need to notify the server to update the peers
-		doneErr := publishSignal(&nodeCfg, ncutils.DONE)
-		if doneErr != nil {
-			logger.Log(0, "network:", nodeCfg.Node.Network, "could not notify server to update peers after interface change")
-		} else {
-			logger.Log(0, "network:", nodeCfg.Node.Network, "signalled finished interface update to server")
-		}
-	} else if hubChange {
-		doneErr := publishSignal(&nodeCfg, ncutils.DONE)
-		if doneErr != nil {
-			logger.Log(0, "network:", nodeCfg.Node.Network, "could not notify server to update peers after hub change")
-		} else {
-			logger.Log(0, "network:", nodeCfg.Node.Network, "signalled finished hub update to server")
-		}
-	}
-	// deal with DNS
-	if newNode.DNSOn != "yes" && shouldDNSChange && nodeCfg.Node.Interface != "" {
-		logger.Log(0, "network:", nodeCfg.Node.Network, "settng DNS off")
-		if err := removeHostDNS(nodeCfg.Node.Interface, ncutils.IsWindows()); err != nil {
-			logger.Log(0, "network:", nodeCfg.Node.Network, "error removing netmaker profile from /etc/hosts "+err.Error())
-		}
-		//		_, err := ncutils.RunCmd("/usr/bin/resolvectl revert "+nodeCfg.Node.Interface, true)
-		//		if err != nil {
-		//			logger.Log(0, "error applying dns" + err.Error())
-		//		}
-	}
-	_ = UpdateLocalListenPort(&nodeCfg)
-}
-
-// UpdatePeers -- mqtt message handler for peers/<Network>/<NodeID> topic
-func UpdatePeers(client mqtt.Client, msg mqtt.Message) {
-	var peerUpdate models.PeerUpdate
-	var network = parseNetworkFromTopic(msg.Topic())
-	var cfg = config.ClientConfig{}
-	cfg.Network = network
-	cfg.ReadConfig()
-
-	data, dataErr := decryptMsg(&cfg, msg.Payload())
-	if dataErr != nil {
-		return
-	}
-	err := json.Unmarshal([]byte(data), &peerUpdate)
-	if err != nil {
-		logger.Log(0, "error unmarshalling peer data")
-		return
-	}
-	// see if cached hit, if so skip
-	var currentMessage = read(peerUpdate.Network, lastPeerUpdate)
-	if currentMessage == string(data) {
-		return
-	}
-	insert(peerUpdate.Network, lastPeerUpdate, string(data))
-	// check version
-	if peerUpdate.ServerVersion != ncutils.Version {
-		logger.Log(0, "server/client version mismatch server: ", peerUpdate.ServerVersion, " client: ", ncutils.Version)
-	}
-	if peerUpdate.ServerVersion != cfg.Server.Version {
-		logger.Log(1, "updating server version")
-		cfg.Server.Version = peerUpdate.ServerVersion
-		config.Write(&cfg, cfg.Network)
-	}
-	cfgFile := ncutils.GetNetclientPathSpecific() + cfg.Node.Interface + ".conf"
-	internetGateway, err := wireguard.UpdateWgPeers(cfgFile, peerUpdate.Peers)
-	if err != nil {
-		logger.Log(0, "error updating wireguard peers"+err.Error())
-		return
-	}
-	// check if internet gateway has changed
-	oldGateway, err := net.ResolveUDPAddr("udp", cfg.Node.InternetGateway)
-
-	// note: may want to remove second part (oldGateway == &net.UDPAddr{})
-	// since it's a pointer, will never be true
-	if err != nil || (oldGateway == &net.UDPAddr{}) {
-		oldGateway = nil
-	}
-	if (internetGateway == nil && oldGateway != nil) || (internetGateway != nil && internetGateway.String() != oldGateway.String()) {
-		cfg.Node.InternetGateway = internetGateway.String()
-		if err := config.ModNodeConfig(&cfg.Node); err != nil {
-			logger.Log(0, "failed to save internet gateway", err.Error())
-		}
-		if err := wireguard.ApplyConf(&cfg.Node, cfg.Node.Interface, cfgFile); err != nil {
-			logger.Log(0, "error applying internet gateway", err.Error())
-		}
-		UpdateLocalListenPort(&cfg)
-		return
-	}
-	queryAddr := cfg.Node.PrimaryAddress()
-
-	// err = wireguard.SyncWGQuickConf(cfg.Node.Interface, file)
-	var iface = cfg.Node.Interface
-	if ncutils.IsMac() {
-		iface, err = local.GetMacIface(queryAddr)
-		if err != nil {
-			logger.Log(0, "error retrieving mac iface: "+err.Error())
-			return
-		}
-	}
-	err = wireguard.SetPeers(iface, &cfg.Node, peerUpdate.Peers)
-	if err != nil {
-		logger.Log(0, "error syncing wg after peer update: "+err.Error())
-		return
-	}
-	logger.Log(0, "network:", cfg.Node.Network, "received peer update for node "+cfg.Node.Name+" "+cfg.Node.Network)
-	if cfg.Node.DNSOn == "yes" {
-		if err := setHostDNS(peerUpdate.DNS, cfg.Node.Interface, ncutils.IsWindows()); err != nil {
-			logger.Log(0, "network:", cfg.Node.Network, "error updating /etc/hosts "+err.Error())
-			return
-		}
-	} else {
-		if err := removeHostDNS(cfg.Node.Interface, ncutils.IsWindows()); err != nil {
-			logger.Log(0, "network:", cfg.Node.Network, "error removing profile from /etc/hosts "+err.Error())
-			return
-		}
-	}
-	_ = UpdateLocalListenPort(&cfg)
-}
-
-func setHostDNS(dns, iface string, windows bool) error {
-	etchosts := "/etc/hosts"
-	temp := os.TempDir()
-	lockfile := temp + "/netclient-lock"
-	if windows {
-		etchosts = "c:\\windows\\system32\\drivers\\etc\\hosts"
-		lockfile = temp + "\\netclient-lock"
-	}
-	if _, err := os.Stat(lockfile); !errors.Is(err, os.ErrNotExist) {
-		return errors.New("/etc/hosts file is locked .... aborting")
-	}
-	lock, err := os.Create(lockfile)
-	if err != nil {
-		return fmt.Errorf("could not create lock file %w", err)
-	}
-	lock.Close()
-	defer os.Remove(lockfile)
-	dnsdata := strings.NewReader(dns)
-	profile, err := parser.ParseProfile(dnsdata)
-	if err != nil {
-		return err
-	}
-	hosts, err := file.NewFile(etchosts)
-	if err != nil {
-		return err
-	}
-	profile.Name = strings.ToLower(iface)
-	profile.Status = types.Enabled
-	if err := hosts.ReplaceProfile(profile); err != nil {
-		return err
-	}
-	if err := hosts.Flush(); err != nil {
-		return err
-	}
-	return nil
-}
-
-func removeHostDNS(iface string, windows bool) error {
-	etchosts := "/etc/hosts"
-	temp := os.TempDir()
-	lockfile := temp + "/netclient-lock"
-	if windows {
-		etchosts = "c:\\windows\\system32\\drivers\\etc\\hosts"
-		lockfile = temp + "\\netclient-lock"
-	}
-	if _, err := os.Stat(lockfile); !errors.Is(err, os.ErrNotExist) {
-		return errors.New("/etc/hosts file is locked .... aborting")
-	}
-	lock, err := os.Create(lockfile)
-	if err != nil {
-		return fmt.Errorf("could not create lock file %w", err)
-	}
-	lock.Close()
-	defer os.Remove(lockfile)
-	hosts, err := file.NewFile(etchosts)
-	if err != nil {
-		return err
-	}
-	if err := hosts.RemoveProfile(strings.ToLower(iface)); err != nil {
-		if err == types.ErrUnknownProfile {
-			return nil
-		}
-		return err
-	}
-	if err := hosts.Flush(); err != nil {
-		return err
-	}
-	return nil
-}

+ 0 - 306
netclient/functions/mqpublish.go

@@ -1,306 +0,0 @@
-package functions
-
-import (
-	"context"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"io"
-	"net"
-	"net/http"
-	"strconv"
-	"sync"
-	"time"
-
-	"github.com/cloverstd/tcping/ping"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/logic/metrics"
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/auth"
-	"github.com/gravitl/netmaker/netclient/config"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-)
-
-var metricsCache = new(sync.Map)
-
-// Checkin  -- go routine that checks for public or local ip changes, publishes changes
-//
-//	if there are no updates, simply "pings" the server as a checkin
-func Checkin(ctx context.Context, wg *sync.WaitGroup) {
-	logger.Log(2, "starting checkin goroutine")
-	defer wg.Done()
-	ticker := time.NewTicker(time.Minute * ncutils.CheckInInterval)
-	defer ticker.Stop()
-	for {
-		select {
-		case <-ctx.Done():
-			logger.Log(0, "checkin routine closed")
-			return
-		case <-ticker.C:
-			if mqclient != nil && mqclient.IsConnected() {
-				checkin()
-			} else {
-				logger.Log(0, "MQ client is not connected, skipping checkin...")
-			}
-
-		}
-	}
-}
-
-func checkin() {
-	networks, _ := ncutils.GetSystemNetworks()
-	logger.Log(3, "checkin with server(s) for all networks")
-	for _, network := range networks {
-		var nodeCfg config.ClientConfig
-		nodeCfg.Network = network
-		nodeCfg.ReadConfig()
-		// check for nftables present if on Linux
-		if ncutils.IsLinux() {
-			if ncutils.IsNFTablesPresent() {
-				nodeCfg.Node.FirewallInUse = models.FIREWALL_NFTABLES
-			} else {
-				nodeCfg.Node.FirewallInUse = models.FIREWALL_IPTABLES
-			}
-		} else {
-			// defaults to iptables for now, may need another default for non-Linux OSes
-			nodeCfg.Node.FirewallInUse = models.FIREWALL_IPTABLES
-		}
-		if nodeCfg.Node.Connected == "yes" {
-			if nodeCfg.Node.IsStatic != "yes" {
-				extIP, err := ncutils.GetPublicIP(nodeCfg.Server.API)
-				if err != nil {
-					logger.Log(1, "error encountered checking public ip addresses: ", err.Error())
-				}
-				if nodeCfg.Node.Endpoint != extIP && extIP != "" {
-					logger.Log(1, "network:", nodeCfg.Node.Network, "endpoint has changed from ", nodeCfg.Node.Endpoint, " to ", extIP)
-					nodeCfg.Node.Endpoint = extIP
-					if err := PublishNodeUpdate(&nodeCfg); err != nil {
-						logger.Log(0, "network:", nodeCfg.Node.Network, "could not publish endpoint change")
-					}
-				}
-				intIP, err := getPrivateAddr()
-				if err != nil {
-					logger.Log(1, "network:", nodeCfg.Node.Network, "error encountered checking private ip addresses: ", err.Error())
-				}
-				if nodeCfg.Node.LocalAddress != intIP && intIP != "" {
-					logger.Log(1, "network:", nodeCfg.Node.Network, "local Address has changed from ", nodeCfg.Node.LocalAddress, " to ", intIP)
-					nodeCfg.Node.LocalAddress = intIP
-					if err := PublishNodeUpdate(&nodeCfg); err != nil {
-						logger.Log(0, "Network: ", nodeCfg.Node.Network, " could not publish local address change")
-					}
-				}
-				_ = UpdateLocalListenPort(&nodeCfg)
-
-			} else if nodeCfg.Node.IsLocal == "yes" && nodeCfg.Node.LocalRange != "" {
-				localIP, err := ncutils.GetLocalIP(nodeCfg.Node.LocalRange)
-				if err != nil {
-					logger.Log(1, "network:", nodeCfg.Node.Network, "error encountered checking local ip addresses: ", err.Error())
-				}
-				if nodeCfg.Node.Endpoint != localIP && localIP != "" {
-					logger.Log(1, "network:", nodeCfg.Node.Network, "endpoint has changed from "+nodeCfg.Node.Endpoint+" to ", localIP)
-					nodeCfg.Node.Endpoint = localIP
-					if err := PublishNodeUpdate(&nodeCfg); err != nil {
-						logger.Log(0, "network:", nodeCfg.Node.Network, "could not publish localip change")
-					}
-				}
-			}
-		}
-		// check version
-		if nodeCfg.Node.Version != ncutils.Version {
-			nodeCfg.Node.Version = ncutils.Version
-			config.Write(&nodeCfg, nodeCfg.Network)
-		}
-		Hello(&nodeCfg)
-		if nodeCfg.Server.Is_EE && nodeCfg.Node.Connected == "yes" {
-			logger.Log(0, "collecting metrics for node", nodeCfg.Node.Name)
-			publishMetrics(&nodeCfg)
-		}
-	}
-}
-
-// PublishNodeUpdates -- saves node and pushes changes to broker
-func PublishNodeUpdate(nodeCfg *config.ClientConfig) error {
-	if err := config.Write(nodeCfg, nodeCfg.Network); err != nil {
-		return err
-	}
-	data, err := json.Marshal(nodeCfg.Node)
-	if err != nil {
-		return err
-	}
-	if err = publish(nodeCfg, fmt.Sprintf("update/%s", nodeCfg.Node.ID), data, 1); err != nil {
-		return err
-	}
-
-	logger.Log(0, "network:", nodeCfg.Node.Network, "sent a node update to server for node", nodeCfg.Node.Name, ", ", nodeCfg.Node.ID)
-	return nil
-}
-
-// Hello -- ping the broker to let server know node it's alive and well
-func Hello(nodeCfg *config.ClientConfig) {
-	var checkin models.NodeCheckin
-	checkin.Version = ncutils.Version
-	checkin.Connected = nodeCfg.Node.Connected
-	ip, err := getInterfaces()
-	if err != nil {
-		logger.Log(0, "failed to retrieve local interfaces", err.Error())
-	} else {
-		nodeCfg.Node.Interfaces = *ip
-		config.Write(nodeCfg, nodeCfg.Network)
-	}
-	checkin.Ifaces = nodeCfg.Node.Interfaces
-	data, err := json.Marshal(checkin)
-	if err != nil {
-		logger.Log(0, "unable to marshal checkin data", err.Error())
-		return
-	}
-	if err := publish(nodeCfg, fmt.Sprintf("ping/%s", nodeCfg.Node.ID), data, 0); err != nil {
-		logger.Log(0, fmt.Sprintf("Network: %s error publishing ping, %v", nodeCfg.Node.Network, err))
-		logger.Log(0, "running pull on "+nodeCfg.Node.Network+" to reconnect")
-		_, err := Pull(nodeCfg.Node.Network, true)
-		if err != nil {
-			logger.Log(0, "could not run pull on "+nodeCfg.Node.Network+", error: "+err.Error())
-		}
-	} else {
-		logger.Log(3, "checkin for", nodeCfg.Network, "complete")
-	}
-}
-
-// publishMetrics - publishes the metrics of a given nodecfg
-func publishMetrics(nodeCfg *config.ClientConfig) {
-	token, err := Authenticate(nodeCfg)
-	if err != nil {
-		logger.Log(1, "failed to authenticate when publishing metrics", err.Error())
-		return
-	}
-	url := fmt.Sprintf("https://%s/api/nodes/%s/%s", nodeCfg.Server.API, nodeCfg.Network, nodeCfg.Node.ID)
-	response, err := API("", http.MethodGet, url, token)
-	if err != nil {
-		logger.Log(1, "failed to read from server during metrics publish", err.Error())
-		return
-	}
-	if response.StatusCode != http.StatusOK {
-		bytes, err := io.ReadAll(response.Body)
-		if err != nil {
-			fmt.Println(err)
-		}
-		logger.Log(0, fmt.Sprintf("%s %s", string(bytes), err.Error()))
-		return
-	}
-	defer response.Body.Close()
-	var nodeGET models.NodeGet
-	if err := json.NewDecoder(response.Body).Decode(&nodeGET); err != nil {
-		logger.Log(0, "failed to decode node when running metrics update", err.Error())
-		return
-	}
-
-	collected, err := metrics.Collect(nodeCfg.Node.Interface, nodeGET.PeerIDs)
-	if err != nil {
-		logger.Log(0, "failed metric collection for node", nodeCfg.Node.Name, err.Error())
-		return
-	}
-	collected.Network = nodeCfg.Node.Network
-	collected.NodeName = nodeCfg.Node.Name
-	collected.NodeID = nodeCfg.Node.ID
-	collected.IsServer = "no"
-	data, err := json.Marshal(collected)
-	if err != nil {
-		logger.Log(0, "something went wrong when marshalling metrics data for node", nodeCfg.Node.Name, err.Error())
-	}
-
-	if err = publish(nodeCfg, fmt.Sprintf("metrics/%s", nodeCfg.Node.ID), data, 1); err != nil {
-		logger.Log(0, "error occurred during publishing of metrics on node", nodeCfg.Node.Name, err.Error())
-		logger.Log(0, "aggregating metrics locally until broker connection re-established")
-		val, ok := metricsCache.Load(nodeCfg.Node.ID)
-		if !ok {
-			metricsCache.Store(nodeCfg.Node.ID, data)
-		} else {
-			var oldMetrics models.Metrics
-			err = json.Unmarshal(val.([]byte), &oldMetrics)
-			if err == nil {
-				for k := range oldMetrics.Connectivity {
-					currentMetric := collected.Connectivity[k]
-					if currentMetric.Latency == 0 {
-						currentMetric.Latency = oldMetrics.Connectivity[k].Latency
-					}
-					currentMetric.Uptime += oldMetrics.Connectivity[k].Uptime
-					currentMetric.TotalTime += oldMetrics.Connectivity[k].TotalTime
-					collected.Connectivity[k] = currentMetric
-				}
-				newData, err := json.Marshal(collected)
-				if err == nil {
-					metricsCache.Store(nodeCfg.Node.ID, newData)
-				}
-			}
-		}
-	} else {
-		metricsCache.Delete(nodeCfg.Node.ID)
-		logger.Log(0, "published metrics for node", nodeCfg.Node.Name)
-	}
-}
-
-// node cfg is required  in order to fetch the traffic keys of that node for encryption
-func publish(nodeCfg *config.ClientConfig, dest string, msg []byte, qos byte) error {
-	// setup the keys
-	trafficPrivKey, err := auth.RetrieveTrafficKey(nodeCfg.Node.Network)
-	if err != nil {
-		return err
-	}
-	serverPubKey, err := ncutils.ConvertBytesToKey(nodeCfg.Node.TrafficKeys.Server)
-	if err != nil {
-		return err
-	}
-
-	encrypted, err := ncutils.Chunk(msg, serverPubKey, trafficPrivKey)
-	if err != nil {
-		return err
-	}
-	if mqclient == nil {
-		return errors.New("unable to publish ... no mqclient")
-	}
-	if token := mqclient.Publish(dest, qos, false, encrypted); !token.WaitTimeout(30*time.Second) || token.Error() != nil {
-		logger.Log(0, "could not connect to broker at "+nodeCfg.Server.Server+":"+nodeCfg.Server.MQPort)
-		var err error
-		if token.Error() == nil {
-			err = errors.New("connection timeout")
-		} else {
-			err = token.Error()
-		}
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func checkBroker(broker string, port string) error {
-	if broker == "" {
-		return errors.New("error: broker address is blank")
-	}
-	if port == "" {
-		return errors.New("error: broker port is blank")
-	}
-	_, err := net.LookupIP(broker)
-	if err != nil {
-		return errors.New("nslookup failed for broker ... check dns records")
-	}
-	pinger := ping.NewTCPing()
-	intPort, err := strconv.Atoi(port)
-	if err != nil {
-		logger.Log(1, "error converting port to int: "+err.Error())
-	}
-	pinger.SetTarget(&ping.Target{
-		Protocol: ping.TCP,
-		Host:     broker,
-		Port:     intPort,
-		Counter:  3,
-		Interval: 1 * time.Second,
-		Timeout:  2 * time.Second,
-	})
-	pingerDone := pinger.Start()
-	<-pingerDone
-	if pinger.Result().SuccessCounter == 0 {
-		return errors.New("unable to connect to broker port ... check netmaker server and firewalls")
-	}
-	return nil
-}

+ 0 - 97
netclient/functions/pull.go

@@ -1,97 +0,0 @@
-package functions
-
-import (
-	"encoding/json"
-	"errors"
-	"fmt"
-	"io"
-	"net/http"
-	"os"
-	"runtime"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/config"
-	"github.com/gravitl/netmaker/netclient/local"
-	"github.com/gravitl/netmaker/netclient/ncutils"
-	"github.com/gravitl/netmaker/netclient/wireguard"
-	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
-	//homedir "github.com/mitchellh/go-homedir"
-)
-
-// Pull - pulls the latest config from the server, if manual it will overwrite
-func Pull(network string, iface bool) (*models.Node, error) {
-	cfg, err := config.ReadConfig(network)
-	if err != nil {
-		return nil, err
-	}
-	if cfg.Node.IPForwarding == "yes" && !ncutils.IsWindows() {
-		if err = local.SetIPForwarding(); err != nil {
-			return nil, err
-		}
-	}
-	token, err := Authenticate(cfg)
-	if err != nil {
-		return nil, err
-	}
-	url := "https://" + cfg.Server.API + "/api/nodes/" + cfg.Network + "/" + cfg.Node.ID
-	response, err := API("", http.MethodGet, url, token)
-	if err != nil {
-		return nil, err
-	}
-	if response.StatusCode != http.StatusOK {
-		bytes, err := io.ReadAll(response.Body)
-		if err != nil {
-			fmt.Println(err)
-		}
-		return nil, (fmt.Errorf("%s %w", string(bytes), err))
-	}
-	defer response.Body.Close()
-	var nodeGET models.NodeGet
-	if err := json.NewDecoder(response.Body).Decode(&nodeGET); err != nil {
-		return nil, fmt.Errorf("error decoding node %w", err)
-	}
-	resNode := nodeGET.Node
-	// ensure that the OS never changes
-	resNode.OS = runtime.GOOS
-	if nodeGET.Peers == nil {
-		nodeGET.Peers = []wgtypes.PeerConfig{}
-	}
-	if nodeGET.ServerConfig.API != "" && nodeGET.ServerConfig.MQPort != "" {
-		if err = config.ModServerConfig(&nodeGET.ServerConfig, resNode.Network); err != nil {
-			logger.Log(0, "unable to update server config: "+err.Error())
-		}
-	}
-	if nodeGET.Node.ListenPort != cfg.Node.LocalListenPort {
-		if err := wireguard.RemoveConf(resNode.Interface, false); err != nil {
-			logger.Log(0, "error remove interface", resNode.Interface, err.Error())
-		}
-		err = ncutils.ModPort(&resNode)
-		if err != nil {
-			return nil, err
-		}
-		informPortChange(&resNode)
-	}
-	if err = config.ModNodeConfig(&resNode); err != nil {
-		return nil, err
-	}
-	if iface {
-		if err = wireguard.SetWGConfig(network, false, nodeGET.Peers[:]); err != nil {
-			return nil, err
-		}
-	} else {
-		if err = wireguard.SetWGConfig(network, true, nodeGET.Peers[:]); err != nil {
-			if errors.Is(err, os.ErrNotExist) && !ncutils.IsFreeBSD() {
-				return Pull(network, true)
-			} else {
-				return nil, err
-			}
-		}
-	}
-	var bkupErr = config.SaveBackup(network)
-	if bkupErr != nil {
-		logger.Log(0, "unable to update backup file for", network)
-	}
-
-	return &resNode, err
-}

+ 0 - 13
netclient/functions/upgrades/types.go

@@ -1,13 +0,0 @@
-package upgrades
-
-import "github.com/gravitl/netmaker/netclient/config"
-
-// UpgradeFunction - logic for upgrade
-type UpgradeFunction func(*config.ClientConfig)
-
-// UpgradeInfo - struct for holding upgrade info
-type UpgradeInfo struct {
-	RequiredVersions []string
-	NewVersion       string
-	OP               UpgradeFunction
-}

+ 0 - 25
netclient/functions/upgrades/upgrades.go

@@ -1,25 +0,0 @@
-package upgrades
-
-// InitializeUpgrades - initializes written upgrades
-func InitializeUpgrades() {
-	addUpgrades([]UpgradeInfo{
-		upgrade0145,
-		upgrade0146,
-		upgrade0160,
-		upgrade0161,
-		upgrade0162,
-	})
-}
-
-// Upgrades - holds all upgrade funcs
-var Upgrades = []UpgradeInfo{}
-
-// addUpgrades - Adds upgrades to make to client
-func addUpgrades(upgrades []UpgradeInfo) {
-	Upgrades = append(Upgrades, upgrades...)
-}
-
-// ReleaseUpgrades - releases upgrade funcs from memory
-func ReleaseUpgrades() {
-	Upgrades = nil
-}

+ 0 - 24
netclient/functions/upgrades/v0-14-5.go

@@ -1,24 +0,0 @@
-package upgrades
-
-import (
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/netclient/config"
-)
-
-var upgrade0145 = UpgradeInfo{
-	RequiredVersions: []string{
-		"v0.14.0",
-		"v0.14.1",
-		"v0.14.2",
-		"v0.14.3",
-		"v0.14.4",
-	},
-	NewVersion: "v0.14.5",
-	OP:         update0145,
-}
-
-func update0145(cfg *config.ClientConfig) {
-	// do stuff for 14.X -> 14.5
-	// No-op
-	logger.Log(0, "updating schema for 0.14.5")
-}

+ 0 - 25
netclient/functions/upgrades/v0-14-6.go

@@ -1,25 +0,0 @@
-package upgrades
-
-import (
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/netclient/config"
-)
-
-var upgrade0146 = UpgradeInfo{
-	RequiredVersions: []string{
-		"v0.14.0",
-		"v0.14.1",
-		"v0.14.2",
-		"v0.14.3",
-		"v0.14.4",
-		"v0.14.5",
-	},
-	NewVersion: "v0.14.6",
-	OP:         update0146,
-}
-
-func update0146(cfg *config.ClientConfig) {
-	// do stuff for 14.X -> 14.5
-	// No-op
-	logger.Log(0, "updating schema for 0.14.6")
-}

+ 0 - 23
netclient/functions/upgrades/v0-16-0.go

@@ -1,23 +0,0 @@
-package upgrades
-
-import (
-	"github.com/gravitl/netmaker/netclient/config"
-)
-
-var upgrade0160 = UpgradeInfo{
-	RequiredVersions: []string{
-		"v0.14.6",
-		"v0.15.0",
-		"v0.15.1",
-		"v0.15.2",
-	},
-	NewVersion: "v0.16.0",
-	OP:         update0160,
-}
-
-func update0160(cfg *config.ClientConfig) {
-	// set connect default if not present 15.X -> 16.0
-	if cfg.Node.Connected == "" {
-		cfg.Node.SetDefaultConnected()
-	}
-}

+ 0 - 24
netclient/functions/upgrades/v0-16-1.go

@@ -1,24 +0,0 @@
-package upgrades
-
-import (
-	"github.com/gravitl/netmaker/netclient/config"
-)
-
-var upgrade0161 = UpgradeInfo{
-	RequiredVersions: []string{
-		"v0.14.6",
-		"v0.15.0",
-		"v0.15.1",
-		"v0.15.2",
-		"v0.16.1",
-	},
-	NewVersion: "v0.16.1",
-	OP:         update0161,
-}
-
-func update0161(cfg *config.ClientConfig) {
-	// set connect default if not present 15.X -> 16.0
-	if cfg.Node.Connected == "" {
-		cfg.Node.SetDefaultConnected()
-	}
-}

+ 0 - 22
netclient/functions/upgrades/v0-16-2.go

@@ -1,22 +0,0 @@
-package upgrades
-
-import (
-	"github.com/gravitl/netmaker/netclient/config"
-)
-
-var upgrade0162 = UpgradeInfo{
-	RequiredVersions: []string{
-		"v0.14.6",
-		"v0.15.0",
-		"v0.15.1",
-		"v0.15.2",
-		"v0.16.1",
-	},
-	NewVersion: "v0.16.2",
-	OP:         update0162,
-}
-
-func update0162(cfg *config.ClientConfig) {
-	// set connect default if not present 15.X -> 16.0
-	update0161(cfg)
-}

+ 0 - 33
netclient/gui/components/buttons.go

@@ -1,33 +0,0 @@
-package components
-
-import (
-	"image/color"
-
-	"fyne.io/fyne/v2"
-	"fyne.io/fyne/v2/canvas"
-	"fyne.io/fyne/v2/container"
-	"fyne.io/fyne/v2/layout"
-	"fyne.io/fyne/v2/widget"
-)
-
-// ColoredButton - renders a colored button with text
-func ColoredButton(text string, tapped func(), color color.Color) *fyne.Container {
-	btn := widget.NewButton(text, tapped)
-	bgColor := canvas.NewRectangle(color)
-	return container.New(
-		layout.NewMaxLayout(),
-		bgColor,
-		btn,
-	)
-}
-
-// ColoredIconButton - renders a colored button with an icon
-func ColoredIconButton(text string, icon fyne.Resource, tapped func(), color color.Color) *fyne.Container {
-	btn := widget.NewButtonWithIcon(text, icon, tapped)
-	bgColor := canvas.NewRectangle(color)
-	return container.New(
-		layout.NewMaxLayout(),
-		btn,
-		bgColor,
-	)
-}

+ 0 - 22
netclient/gui/components/colors.go

@@ -1,22 +0,0 @@
-package components
-
-import "image/color"
-
-var (
-	// Red_color - preferred red color
-	Red_color = color.NRGBA{R: 233, G: 10, B: 17, A: 155}
-	// Gravitl_color - gravitl primary sea green color
-	Gravitl_color = color.NRGBA{R: 14, G: 173, B: 105, A: 155}
-	// Blue_color - preferred blue color
-	Blue_color = color.NRGBA{R: 17, G: 157, B: 164, A: 155}
-	// Danger_color - preferred danger color
-	Danger_color = color.NRGBA{R: 223, G: 71, B: 89, A: 155}
-	// Purple_color - preferred purple color
-	Purple_color = color.NRGBA{R: 115, G: 80, B: 159, A: 155}
-	// Orange_color - preferred orange color
-	Orange_color = color.NRGBA{R: 253, G: 76, B: 65, A: 155}
-	// Grey_color - preferred grey color
-	Grey_color = color.NRGBA{R: 27, G: 27, B: 27, A: 155}
-	// Gold_color - preferred gold color
-	Gold_color = color.NRGBA{R: 218, G: 165, B: 32, A: 155}
-)

+ 0 - 23
netclient/gui/components/text.go

@@ -1,23 +0,0 @@
-package components
-
-import (
-	"image/color"
-
-	"fyne.io/fyne/v2"
-	"fyne.io/fyne/v2/canvas"
-	"fyne.io/fyne/v2/container"
-	"fyne.io/fyne/v2/layout"
-	"fyne.io/fyne/v2/widget"
-)
-
-// ColoredText - renders a colored label
-func ColoredText(text string, color color.Color) *fyne.Container {
-	btn := widget.NewLabel(text)
-	btn.Wrapping = fyne.TextWrapWord
-	bgColor := canvas.NewRectangle(color)
-	return container.New(
-		layout.NewMaxLayout(),
-		bgColor,
-		btn,
-	)
-}

+ 0 - 39
netclient/gui/components/toolbar.go

@@ -1,39 +0,0 @@
-package components
-
-import (
-	"image/color"
-
-	"fyne.io/fyne/v2"
-	"fyne.io/fyne/v2/container"
-	"fyne.io/fyne/v2/widget"
-)
-
-// NewToolbarLabelButton - makes a toolbar button cell with label
-func NewToolbarLabelButton(label string, icon fyne.Resource, onclick func(), colour color.Color) widget.ToolbarItem {
-	l := ColoredIconButton(label, icon, onclick, colour)
-	l.MinSize()
-	return &toolbarLabelButton{l}
-}
-
-// NewToolbarLabel - makes a toolbar text cell
-func NewToolbarLabel(label string) widget.ToolbarItem {
-	l := widget.NewLabel(label)
-	l.MinSize()
-	return &toolbarLabel{l}
-}
-
-type toolbarLabelButton struct {
-	*fyne.Container
-}
-
-type toolbarLabel struct {
-	*widget.Label
-}
-
-func (t *toolbarLabelButton) ToolbarObject() fyne.CanvasObject {
-	return container.NewCenter(t.Container)
-}
-
-func (t *toolbarLabel) ToolbarObject() fyne.CanvasObject {
-	return container.NewCenter(t.Label)
-}

+ 0 - 21
netclient/gui/components/views/confirm.go

@@ -1,21 +0,0 @@
-package views
-
-import (
-	"fyne.io/fyne/v2"
-	"fyne.io/fyne/v2/container"
-	"fyne.io/fyne/v2/theme"
-	"fyne.io/fyne/v2/widget"
-	"github.com/gravitl/netmaker/netclient/gui/components"
-)
-
-// GetConfirmation - displays a confirmation message
-func GetConfirmation(msg string, onCancel, onConfirm func()) fyne.CanvasObject {
-	return container.NewGridWithColumns(1,
-		container.NewCenter(widget.NewLabel(msg)),
-		container.NewCenter(
-			container.NewHBox(
-				components.ColoredIconButton("Confirm", theme.ConfirmIcon(), onConfirm, components.Gravitl_color),
-				components.ColoredIconButton("Cancel", theme.CancelIcon(), onCancel, components.Danger_color),
-			)),
-	)
-}

+ 0 - 25
netclient/gui/components/views/content.go

@@ -1,25 +0,0 @@
-package views
-
-import (
-	"fyne.io/fyne/v2"
-)
-
-// CurrentContent - the content currently being displayed
-var CurrentContent *fyne.Container
-
-// RemoveContent - removes a rendered content
-func RemoveContent(name string) {
-	CurrentContent.Remove(GetView(name))
-}
-
-// AddContent - adds content to be rendered
-func AddContent(name string) {
-	CurrentContent.Add(GetView(name))
-}
-
-// RefreshComponent - refreshes the component to re-render
-func RefreshComponent(name string, c fyne.CanvasObject) {
-	RemoveContent(name)
-	SetView(name, c)
-	AddContent(name)
-}

Some files were not shown because too many files changed in this diff