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
           prerelease: true
           asset_name: netmaker
           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/base64"
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
+	"fmt"
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"time"
 	"time"

+ 6 - 1
compose/docker-compose.yml

@@ -19,7 +19,9 @@ services:
       - sqldata:/root/data
       - sqldata:/root/data
       - mosquitto_data:/etc/netmaker
       - mosquitto_data:/etc/netmaker
     environment:
     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_HOST: "SERVER_PUBLIC_IP"
       SERVER_API_CONN_STRING: "api.NETMAKER_BASE_DOMAIN:443"
       SERVER_API_CONN_STRING: "api.NETMAKER_BASE_DOMAIN:443"
       COREDNS_ADDR: "SERVER_PUBLIC_IP"
       COREDNS_ADDR: "SERVER_PUBLIC_IP"
@@ -40,8 +42,11 @@ services:
       MANAGE_IPTABLES: "on"
       MANAGE_IPTABLES: "on"
       PORT_FORWARD_SERVICES: "dns"
       PORT_FORWARD_SERVICES: "dns"
       MQ_ADMIN_PASSWORD: "REPLACE_MQ_ADMIN_PASSWORD"
       MQ_ADMIN_PASSWORD: "REPLACE_MQ_ADMIN_PASSWORD"
+      STUN_PORT: "3478"
+      PROXY: "on"
     ports:
     ports:
       - "51821-51830:51821-51830/udp"
       - "51821-51830:51821-51830/udp"
+      - "3478:3478/udp"
   netmaker-ui:
   netmaker-ui:
     container_name: netmaker-ui
     container_name: netmaker-ui
     image: gravitl/netmaker-ui:v0.17.1
     image: gravitl/netmaker-ui:v0.17.1

+ 4 - 0
config/config.go

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

+ 1 - 0
controllers/controller.go

@@ -28,6 +28,7 @@ var HttpHandlers = []interface{}{
 	extClientHandlers,
 	extClientHandlers,
 	ipHandlers,
 	ipHandlers,
 	loggerHandlers,
 	loggerHandlers,
+	hostHandlers,
 }
 }
 
 
 // HandleRESTRequests - handles the rest requests
 // 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.
 // Gets node DNS entries associated with a network.
 //
 //
-//		Schemes: https
+//			Schemes: https
 //
 //
-// 		Security:
-//   		oauth
+//			Security:
+//	  		oauth
 func getNodeDNS(w http.ResponseWriter, r *http.Request) {
 func getNodeDNS(w http.ResponseWriter, r *http.Request) {
 
 
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
@@ -55,14 +55,13 @@ func getNodeDNS(w http.ResponseWriter, r *http.Request) {
 //
 //
 // Gets all DNS entries.
 // 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) {
 func getAllDNS(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 	dns, err := logic.GetAllDNS()
 	dns, err := logic.GetAllDNS()
@@ -79,14 +78,13 @@ func getAllDNS(w http.ResponseWriter, r *http.Request) {
 //
 //
 // Gets custom DNS entries associated with a network.
 // 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) {
 func getCustomDNS(w http.ResponseWriter, r *http.Request) {
 
 
 	w.Header().Set("Content-Type", "application/json")
 	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.
 // 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) {
 func getDNS(w http.ResponseWriter, r *http.Request) {
 
 
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
@@ -139,14 +136,13 @@ func getDNS(w http.ResponseWriter, r *http.Request) {
 //
 //
 // Create a DNS entry.
 // 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) {
 func createDNS(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	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)
 	logger.Log(1, "new DNS record added:", entry.Name)
 	if servercfg.IsMessageQueueBackend() {
 	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"),
 	logger.Log(2, r.Header.Get("user"),
@@ -202,14 +190,14 @@ func createDNS(w http.ResponseWriter, r *http.Request) {
 //
 //
 // Delete a DNS entry.
 // 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) {
 func deleteDNS(w http.ResponseWriter, r *http.Request) {
 	// Set header
 	// Set header
 	w.Header().Set("Content-Type", "application/json")
 	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.
 // 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) {
 func pushDNS(w http.ResponseWriter, r *http.Request) {
 	// Set header
 	// Set header
 	w.Header().Set("Content-Type", "application/json")
 	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)
 		assert.Equal(t, "10.0.0.1", dns[0].Address)
 	})
 	})
 	t.Run("MultipleNodes", func(t *testing.T) {
 	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)
 		err := logic.CreateNode(createnode)
 		assert.Nil(t, err)
 		assert.Nil(t, err)
 		dns, err := logic.GetNodeDNS("skynet")
 		dns, err := logic.GetNodeDNS("skynet")

+ 17 - 18
controllers/docs.go

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

+ 23 - 8
controllers/ext_client.go

@@ -35,7 +35,7 @@ func checkIngressExists(nodeID string) bool {
 	if err != nil {
 	if err != nil {
 		return false
 		return false
 	}
 	}
-	return node.IsIngressGateway == "yes"
+	return node.IsIngressGateway
 }
 }
 
 
 // swagger:route GET /api/extclients/{network} ext_client getNetworkExtClients
 // 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"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		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)
 	network, err := logic.GetParentNetwork(client.Network)
 	if err != nil {
 	if err != nil {
@@ -207,7 +214,7 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
 	if network.DefaultKeepalive != 0 {
 	if network.DefaultKeepalive != 0 {
 		keepalive = "PersistentKeepalive = " + strconv.Itoa(int(network.DefaultKeepalive))
 		keepalive = "PersistentKeepalive = " + strconv.Itoa(int(network.DefaultKeepalive))
 	}
 	}
-	gwendpoint := gwnode.Endpoint + ":" + strconv.Itoa(int(gwnode.ListenPort))
+	gwendpoint := host.EndpointIP.String() + ":" + strconv.Itoa(host.ListenPort)
 	newAllowedIPs := network.AddressRange
 	newAllowedIPs := network.AddressRange
 	if newAllowedIPs != "" && network.AddressRange6 != "" {
 	if newAllowedIPs != "" && network.AddressRange6 != "" {
 		newAllowedIPs += ","
 		newAllowedIPs += ","
@@ -226,8 +233,8 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 
 
 	defaultMTU := 1420
 	defaultMTU := 1420
-	if gwnode.MTU != 0 {
-		defaultMTU = int(gwnode.MTU)
+	if host.MTU != 0 {
+		defaultMTU = host.MTU
 	}
 	}
 	config := fmt.Sprintf(`[Interface]
 	config := fmt.Sprintf(`[Interface]
 Address = %s
 Address = %s
@@ -245,7 +252,7 @@ Endpoint = %s
 		client.PrivateKey,
 		client.PrivateKey,
 		defaultMTU,
 		defaultMTU,
 		defaultDNS,
 		defaultDNS,
-		gwnode.PublicKey,
+		host.PublicKey,
 		newAllowedIPs,
 		newAllowedIPs,
 		gwendpoint,
 		gwendpoint,
 		keepalive)
 		keepalive)
@@ -331,7 +338,15 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		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
 	extclient.Enabled = true
 	parentNetwork, err := logic.GetNetwork(networkName)
 	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 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 ingressNode, err := logic.GetNodeByID(newclient.IngressGatewayID); err == nil {
 			if err = mq.PublishExtPeerUpdate(&ingressNode); 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)
 	err = mq.PublishExtPeerUpdate(&ingressnode)
 	if err != nil {
 	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"),
 	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
 		return
 	}
 	}
 	for _, node := range nodes {
 	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
 			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 {
 	if rangeupdate4 || rangeupdate6 || localrangeupdate || holepunchupdate {
 		nodes, err := logic.GetNetworkNodes(network.NetID)
 		nodes, err := logic.GetNetworkNodes(network.NetID)
 		if err != nil {
 		if err != nil {
@@ -319,19 +307,10 @@ func updateNetworkACL(w http.ResponseWriter, r *http.Request) {
 
 
 	// send peer updates
 	// send peer updates
 	if servercfg.IsMessageQueueBackend() {
 	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)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(newNetACL)
 	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))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, errtype))
 		return
 		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)
 	logger.Log(1, r.Header.Get("user"), "deleted network", network)
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode("success")
 	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"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 		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)
 	logger.Log(1, r.Header.Get("user"), "created network", network.NetID)
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(network)
 	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.Nil(t, err)
 		assert.Equal(t, network.AddressRange6, "fde6:be04:fa5e:d076::/64")
 		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)
 	nodeErr := logic.CreateNode(&node1)
 	t.Run("Test node on network IPv6", func(t *testing.T) {
 	t.Run("Test node on network IPv6", func(t *testing.T) {
 		assert.Nil(t, nodeErr)
 		assert.Nil(t, nodeErr)

+ 355 - 377
controllers/node.go

@@ -2,11 +2,14 @@ package controller
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"fmt"
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 
 
+	"github.com/google/uuid"
 	"github.com/gorilla/mux"
 	"github.com/gorilla/mux"
+	proxy_models "github.com/gravitl/netclient/nmproxy/models"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"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}", 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(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}", 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}", 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}/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)
 	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}/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}/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}/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/{network}", nodeauth(checkFreeTierLimits(node_l, http.HandlerFunc(createNode)))).Methods(http.MethodPost)
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).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.
 // 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) {
 func authenticate(response http.ResponseWriter, request *http.Request) {
 
 
 	var authRequest models.AuthParams
 	var authRequest models.AuthParams
@@ -92,51 +96,22 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
 			return
 			return
 		}
 		}
 	}
 	}
-
-	err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(authRequest.Password))
+	host, err := logic.GetHost(result.HostID.String())
 	if err != nil {
 	if err != nil {
 		errorResponse.Code = http.StatusBadRequest
 		errorResponse.Code = http.StatusBadRequest
 		errorResponse.Message = err.Error()
 		errorResponse.Message = err.Error()
 		logger.Log(0, request.Header.Get("user"),
 		logger.Log(0, request.Header.Get("user"),
-			"error validating user password: ", err.Error())
+			"error retrieving host: ", err.Error())
 		logic.ReturnErrorResponse(response, request, errorResponse)
 		logic.ReturnErrorResponse(response, request, errorResponse)
 		return
 		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
 		return
 	}
 	}
 
 
@@ -171,7 +146,6 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
 	response.WriteHeader(http.StatusOK)
 	response.WriteHeader(http.StatusOK)
 	response.Header().Set("Content-Type", "application/json")
 	response.Header().Set("Content-Type", "application/json")
 	response.Write(successJSONResponse)
 	response.Write(successJSONResponse)
-
 }
 }
 
 
 // auth middleware for api calls from nodes where node is has not yet joined the server (register, join)
 // 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.
 // 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) {
 func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
-
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
-
-	var nodes []models.Node
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
 	networkName := params["network"]
 	networkName := params["network"]
-
 	nodes, err := logic.GetNetworkNodes(networkName)
 	nodes, err := logic.GetNetworkNodes(networkName)
 	if err != nil {
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 		logger.Log(0, r.Header.Get("user"),
@@ -366,29 +336,25 @@ func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
 		return
 		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)
 	logger.Log(2, r.Header.Get("user"), "fetched nodes on network", networkName)
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(nodes)
+	json.NewEncoder(w).Encode(apiNodes)
 }
 }
 
 
 // swagger:route GET /api/nodes nodes getAllNodes
 // swagger:route GET /api/nodes nodes getAllNodes
 //
 //
 // Get all nodes across all networks.
 // 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
 // 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) {
 func getAllNodes(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
@@ -416,10 +382,11 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
 			return
 			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")
 	logger.Log(3, r.Header.Get("user"), "fetched all nodes they have access to")
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(nodes)
+	json.NewEncoder(w).Encode(apiNodes)
 }
 }
 
 
 func getUsersNodes(user models.User) ([]models.Node, error) {
 func getUsersNodes(user models.User) ([]models.Node, error) {
@@ -439,13 +406,13 @@ func getUsersNodes(user models.User) ([]models.Node, error) {
 //
 //
 // Get an individual node.
 // 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) {
 func getNode(w http.ResponseWriter, r *http.Request) {
 	// set header.
 	// set header.
 	w.Header().Set("Content-Type", "application/json")
 	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"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		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) {
 	if err != nil && !database.IsEmptyRecord(err) {
 		logger.Log(0, r.Header.Get("user"),
 		logger.Log(0, r.Header.Get("user"),
 			fmt.Sprintf("error fetching wg peers config for node [ %s ]: %v", nodeid, err))
 			fmt.Sprintf("error fetching wg peers config for node [ %s ]: %v", nodeid, err))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		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{
 	response := models.NodeGet{
-		Node:         node,
+		Node:         *legacy,
 		Peers:        peerUpdate.Peers,
 		Peers:        peerUpdate.Peers,
-		ServerConfig: servercfg.GetServerInfo(),
+		ServerConfig: server,
 		PeerIDs:      peerUpdate.PeerIDs,
 		PeerIDs:      peerUpdate.PeerIDs,
 	}
 	}
 
 
 	if servercfg.Is_EE && nodeRequest {
 	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.
 // 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) {
 func createNode(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 
 
@@ -529,54 +506,47 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 		return
 		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 {
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
 		logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 		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
 		return
 	}
 	}
-	node.NetworkSettings, err = logic.GetNetworkSettings(node.Network)
+
+	data.Node.Network = networkName
+
+	networkSettings, err := logic.GetNetworkSettings(networkName)
 	if err != nil {
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 		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"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
-	keyName, validKey := logic.IsKeyValid(networkName, node.AccessKey)
+	data.Node.NetworkSettings(networkSettings)
+	keyName, validKey := logic.IsKeyValid(networkName, data.Key)
 	if !validKey {
 	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))
 	user, err := pro.GetNetworkUser(networkName, promodels.NetworkUserID(keyName))
 	if err == nil {
 	if err == nil {
 		if user.ID != "" {
 		if user.ID != "" {
 			logger.Log(1, "associating new node with user", keyName)
 			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"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
-	if node.TrafficKeys.Mine == nil {
+	if data.Host.TrafficKeyPublic == nil {
 		logger.Log(0, "error: node traffic key is nil")
 		logger.Log(0, "error: node traffic key is nil")
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		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
 	// 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 {
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 		logger.Log(0, r.Header.Get("user"),
 			fmt.Sprintf("failed to create node on network [%s]: %s",
 			fmt.Sprintf("failed to create node on network [%s]: %s",
-				node.Network, err))
+				networkName, err))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
@@ -616,107 +625,42 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 	// if it fails remove the node and fail request
 	// if it fails remove the node and fail request
 	if user != nil {
 	if user != nil {
 		var updatedUserNode bool
 		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 {
 		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
 			updatedUserNode = true
 		}
 		}
 		if !updatedUserNode { // user was found but not updated, so delete node
 		if !updatedUserNode { // user was found but not updated, so delete node
 			logger.Log(0, "failed to add node to user", keyName)
 			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"))
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 			return
 			return
 		}
 		}
 	}
 	}
-
-	peerUpdate, err := logic.GetPeerUpdate(&node)
+	peerUpdate, err := logic.GetPeerUpdate(&data.Node, &data.Host)
 	if err != nil && !database.IsEmptyRecord(err) {
 	if err != nil && !database.IsEmptyRecord(err) {
 		logger.Log(0, r.Header.Get("user"),
 		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"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		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,
 		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)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(response)
 	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 ==
 // == EGRESS ==
@@ -725,13 +669,13 @@ func uncordonNode(w http.ResponseWriter, r *http.Request) {
 //
 //
 // Create an egress gateway.
 // 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) {
 func createEgressGateway(w http.ResponseWriter, r *http.Request) {
 	var gateway models.EgressGatewayRequest
 	var gateway models.EgressGatewayRequest
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
@@ -753,9 +697,10 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 
 
+	apiNode := node.ConvertToAPINode()
 	logger.Log(1, r.Header.Get("user"), "created egress gateway on node", gateway.NodeID, "on network", gateway.NetID)
 	logger.Log(1, r.Header.Get("user"), "created egress gateway on node", gateway.NodeID, "on network", gateway.NetID)
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
+	json.NewEncoder(w).Encode(apiNode)
 
 
 	runUpdates(&node, true)
 	runUpdates(&node, true)
 }
 }
@@ -764,13 +709,13 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
 //
 //
 // Delete an egress gateway.
 // 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) {
 func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
@@ -785,9 +730,10 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 
 
+	apiNode := node.ConvertToAPINode()
 	logger.Log(1, r.Header.Get("user"), "deleted egress gateway on node", nodeid, "on network", netid)
 	logger.Log(1, r.Header.Get("user"), "deleted egress gateway on node", nodeid, "on network", netid)
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
+	json.NewEncoder(w).Encode(apiNode)
 
 
 	runUpdates(&node, true)
 	runUpdates(&node, true)
 }
 }
@@ -798,13 +744,13 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
 //
 //
 // Create an ingress gateway.
 // 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) {
 func createIngressGateway(w http.ResponseWriter, r *http.Request) {
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
 	w.Header().Set("Content-Type", "application/json")
 	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 servercfg.Is_EE && failoverReqBody.Failover {
 		if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil {
 		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)
 	logger.Log(1, r.Header.Get("user"), "created ingress gateway on node", nodeid, "on network", netid)
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
+	json.NewEncoder(w).Encode(apiNode)
 
 
 	runUpdates(&node, true)
 	runUpdates(&node, true)
 }
 }
@@ -842,13 +789,13 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
 //
 //
 // Delete an ingress gateway.
 // 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) {
 func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
@@ -865,40 +812,41 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 
 
 	if servercfg.Is_EE && wasFailover {
 	if servercfg.Is_EE && wasFailover {
 		if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil {
 		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)
 	logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid)
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
+	json.NewEncoder(w).Encode(apiNode)
 
 
 	runUpdates(&node, true)
 	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")
 	w.Header().Set("Content-Type", "application/json")
 
 
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
 
 
-	var node models.Node
 	//start here
 	//start here
 	nodeid := params["nodeid"]
 	nodeid := params["nodeid"]
-	node, err := logic.GetNodeByID(nodeid)
+	currentNode, err := logic.GetNodeByID(nodeid)
 	if err != nil {
 	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"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
@@ -912,53 +860,129 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 	relayupdate := false
 	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
 			relayupdate = true
 		} else {
 		} else {
 			for i, addr := range newNode.RelayAddrs {
 			for i, addr := range newNode.RelayAddrs {
-				if addr != node.RelayAddrs[i] {
+				if addr != currentNode.RelayAddrs[i] {
 					relayupdate = true
 					relayupdate = true
 				}
 				}
 			}
 			}
 		}
 		}
 	}
 	}
 	relayedUpdate := false
 	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
 		relayedUpdate = true
 	}
 	}
 
 
 	if !servercfg.GetRce() {
 	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 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 {
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 		logger.Log(0, r.Header.Get("user"),
 			fmt.Sprintf("failed to update node info [ %s ] info: %v", nodeid, err))
 			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
 		return
 	}
 	}
 	if relayupdate {
 	if relayupdate {
-		updatenodes := logic.UpdateRelay(node.Network, node.RelayAddrs, newNode.RelayAddrs)
+		updatenodes := logic.UpdateRelay(currentNode.Network, currentNode.RelayAddrs, newNode.RelayAddrs)
 		if len(updatenodes) > 0 {
 		if len(updatenodes) > 0 {
 			for _, relayedNode := range updatenodes {
 			for _, relayedNode := range updatenodes {
 				runUpdates(&relayedNode, false)
 				runUpdates(&relayedNode, false)
@@ -974,30 +998,31 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		}
 		}
 	}
 	}
 	if relayedUpdate {
 	if relayedUpdate {
-		updateRelay(&node, &newNode)
+		updateRelay(&currentNode, newNode)
 	}
 	}
 	if servercfg.IsDNSMode() {
 	if servercfg.IsDNSMode() {
 		logic.SetDNS()
 		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)
 	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
 // swagger:route DELETE /api/nodes/{network}/{nodeid} nodes deleteNode
 //
 //
 // Delete an individual node.
 // 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) {
 func deleteNode(w http.ResponseWriter, r *http.Request) {
 	// Set header
 	// Set header
 	w.Header().Set("Content-Type", "application/json")
 	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 params = mux.Vars(r)
 	var nodeid = params["nodeid"]
 	var nodeid = params["nodeid"]
 	fromNode := r.Header.Get("requestfrom") == "node"
 	fromNode := r.Header.Get("requestfrom") == "node"
-	var node, err = logic.GetNodeByID(nodeid)
+	node, err := logic.GetNodeByID(nodeid)
 	if err != nil {
 	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"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 		return
 	}
 	}
@@ -1037,118 +1050,83 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
 			return
 			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
 		return
 	}
 	}
+	if host.ProxyEnabled {
+		mq.ProxyUpdate(&proxy_models.ProxyManagerPayload{
+			Action:  proxy_models.DeleteNetwork,
+			Network: node.Network,
+		}, &node)
+	}
 	if fromNode {
 	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.")
 	logic.ReturnSuccessResponse(w, r, nodeid+" deleted.")
 	logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
 	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) {
 func runUpdates(node *models.Node, ifaceDelta bool) {
 	go func() { // don't block http response
 	go func() { // don't block http response
 		// publish node update if not server
 		// publish node update if not server
 		if err := mq.NodeUpdate(node); err != nil {
 		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) {
 func updateRelay(oldnode, newnode *models.Node) {
 	relay := logic.FindRelay(oldnode)
 	relay := logic.FindRelay(oldnode)
 	newrelay := relay
 	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
 	//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 {
 		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[: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
 	//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 {
 		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[: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()
 	createNet()
 	t.Run("NoNodes", func(t *testing.T) {
 	t.Run("NoNodes", func(t *testing.T) {
 		node, err := logic.CreateEgressGateway(gateway)
 		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")
 		assert.EqualError(t, err, "could not find any records")
 	})
 	})
 	t.Run("Non-linux node", func(t *testing.T) {
 	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)
 		err := logic.CreateNode(&createnode)
 		assert.Nil(t, err)
 		assert.Nil(t, err)
 		gateway.NodeID = createnode.ID
 		gateway.NodeID = createnode.ID
 		node, err := logic.CreateEgressGateway(gateway)
 		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")
 		assert.EqualError(t, err, "windows is unsupported for egress gateways")
 	})
 	})
 	t.Run("Success-Nat-Enabled", func(t *testing.T) {
 	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) {
 	t.Run("BadNode", func(t *testing.T) {
 		node, err := logic.DeleteEgressGateway(gateway.NetID, "01:02:03")
 		node, err := logic.DeleteEgressGateway(gateway.NetID, "01:02:03")
 		assert.EqualError(t, err, "no result found")
 		assert.EqualError(t, err, "no result found")
-		assert.Equal(t, models.Node{}, node)
+		assert.Equal(t, models.LegacyNode{}, node)
 		deleteAllNodes()
 		deleteAllNodes()
 	})
 	})
 }
 }
@@ -136,7 +136,7 @@ func TestGetNetworkNodes(t *testing.T) {
 		createTestNode()
 		createTestNode()
 		node, err := logic.GetNetworkNodes("skynet")
 		node, err := logic.GetNetworkNodes("skynet")
 		assert.Nil(t, err)
 		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()
 	node := createTestNode()
 	t.Run("BadID", func(t *testing.T) {
 	t.Run("BadID", func(t *testing.T) {
 		resp, err := logic.UncordonNode("blahblah")
 		resp, err := logic.UncordonNode("blahblah")
-		assert.Equal(t, models.Node{}, resp)
+		assert.Equal(t, models.LegacyNode{}, resp)
 		assert.EqualError(t, err, "no result found")
 		assert.EqualError(t, err, "no result found")
 	})
 	})
 	t.Run("Success", func(t *testing.T) {
 	t.Run("Success", func(t *testing.T) {
@@ -181,8 +181,8 @@ func TestValidateEgressGateway(t *testing.T) {
 
 
 func TestNodeACLs(t *testing.T) {
 func TestNodeACLs(t *testing.T) {
 	deleteAllNodes()
 	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(&node1)
 	logic.CreateNode(&node2)
 	logic.CreateNode(&node2)
 	t.Run("acls not present", func(t *testing.T) {
 	t.Run("acls not present", func(t *testing.T) {
@@ -222,7 +222,7 @@ func TestNodeACLs(t *testing.T) {
 		currentACL.Save(acls.ContainerID(node1.Network))
 		currentACL.Save(acls.ContainerID(node1.Network))
 	})
 	})
 	t.Run("node acls correct after add new node not allowed", func(t *testing.T) {
 	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)
 		logic.CreateNode(&node3)
 		var currentACL, err = nodeacls.FetchAllACLs(nodeacls.NetworkID(node3.Network))
 		var currentACL, err = nodeacls.FetchAllACLs(nodeacls.NetworkID(node3.Network))
 		assert.Nil(t, err)
 		assert.Nil(t, err)
@@ -249,8 +249,8 @@ func deleteAllNodes() {
 	database.DeleteAllRecords(database.NODES_TABLE_NAME)
 	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)
 	logic.CreateNode(&createnode)
 	return &createnode
 	return &createnode
 }
 }

+ 19 - 14
controllers/relay.go

@@ -16,13 +16,13 @@ import (
 //
 //
 // Create a relay.
 // 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) {
 func createRelay(w http.ResponseWriter, r *http.Request) {
 	var relay models.RelayRequest
 	var relay models.RelayRequest
 	var params = mux.Vars(r)
 	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"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
+
 	logger.Log(1, r.Header.Get("user"), "created relay on node", relay.NodeID, "on network", relay.NetID)
 	logger.Log(1, r.Header.Get("user"), "created relay on node", relay.NodeID, "on network", relay.NetID)
 	for _, relayedNode := range updatenodes {
 	for _, relayedNode := range updatenodes {
+
 		err = mq.NodeUpdate(&relayedNode)
 		err = mq.NodeUpdate(&relayedNode)
 		if err != nil {
 		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)
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
+	json.NewEncoder(w).Encode(apiNode)
 	runUpdates(&node, true)
 	runUpdates(&node, true)
 }
 }
 
 
@@ -58,13 +62,13 @@ func createRelay(w http.ResponseWriter, r *http.Request) {
 //
 //
 // Remove a relay.
 // 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) {
 func deleteRelay(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
@@ -80,10 +84,11 @@ func deleteRelay(w http.ResponseWriter, r *http.Request) {
 	for _, relayedNode := range updatenodes {
 	for _, relayedNode := range updatenodes {
 		err = mq.NodeUpdate(&relayedNode)
 		err = mq.NodeUpdate(&relayedNode)
 		if err != nil {
 		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)
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
+	json.NewEncoder(w).Encode(apiNode)
 	runUpdates(&node, true)
 	runUpdates(&node, true)
 }
 }

+ 68 - 90
database/database.go

@@ -14,96 +14,73 @@ import (
 	"golang.org/x/crypto/nacl/box"
 	"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{} {
 func getCurrentDB() map[string]interface{} {
 	switch servercfg.GetDB() {
 	switch servercfg.GetDB() {
@@ -155,6 +132,7 @@ func createTables() {
 	createTable(NETWORK_USER_TABLE_NAME)
 	createTable(NETWORK_USER_TABLE_NAME)
 	createTable(USER_GROUPS_TABLE_NAME)
 	createTable(USER_GROUPS_TABLE_NAME)
 	createTable(CACHE_TABLE_NAME)
 	createTable(CACHE_TABLE_NAME)
+	createTable(HOSTS_TABLE_NAME)
 }
 }
 
 
 func createTable(tableName string) error {
 func createTable(tableName string) error {

+ 6 - 0
docker/Caddyfile

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

+ 5 - 0
docker/Caddyfile-EE

@@ -44,6 +44,11 @@ https://api.NETMAKER_BASE_DOMAIN {
         reverse_proxy http://netmaker:8081
         reverse_proxy http://netmaker:8081
 }
 }
 
 
+# STUN
+https://stun.NETMAKER_BASE_DOMAIN {
+	reverse_proxy netmaker:3478
+}
+
 # MQ
 # MQ
 wss://broker.NETMAKER_BASE_DOMAIN {
 wss://broker.NETMAKER_BASE_DOMAIN {
         reverse_proxy ws://mq:8883
         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 {
 	for i := range networkNodes {
 		id := networkNodes[i].ID
 		id := networkNodes[i].ID
-		metrics, err := logic.GetMetrics(id)
+		metrics, err := logic.GetMetrics(id.String())
 		if err != nil {
 		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
 			continue
 		}
 		}
-		networkMetrics.Nodes[id] = *metrics
+		networkMetrics.Nodes[id.String()] = *metrics
 	}
 	}
 
 
 	logger.Log(1, r.Header.Get("user"), "fetched metrics for network", network)
 	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 {
 	for i := range ingresses {
 		id := ingresses[i].ID
 		id := ingresses[i].ID
-		ingressMetrics, err := logic.GetMetrics(id)
+		ingressMetrics, err := logic.GetMetrics(id.String())
 		if err != nil {
 		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
 			continue
 		}
 		}
 		if ingressMetrics.Connectivity == nil {
 		if ingressMetrics.Connectivity == nil {
@@ -142,12 +142,12 @@ func getAllMetrics(w http.ResponseWriter, r *http.Request) {
 
 
 	for i := range allNodes {
 	for i := range allNodes {
 		id := allNodes[i].ID
 		id := allNodes[i].ID
-		metrics, err := logic.GetMetrics(id)
+		metrics, err := logic.GetMetrics(id.String())
 		if err != nil {
 		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
 			continue
 		}
 		}
-		networkMetrics.Nodes[id] = *metrics
+		networkMetrics.Nodes[id.String()] = *metrics
 	}
 	}
 
 
 	logger.Log(1, r.Header.Get("user"), "fetched metrics for all nodes on server")
 	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 access level is NODE_ACCESS, filter nodes
 						if netUser.AccessLevel == pro.NODE_ACCESS {
 						if netUser.AccessLevel == pro.NODE_ACCESS {
 							for i := range netNodes {
 							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])
 									newData.Nodes = append(newData.Nodes, netNodes[i])
 								}
 								}
 							}
 							}
 						} else { // net admin so, get all nodes and ext clients on network...
 						} else { // net admin so, get all nodes and ext clients on network...
 							newData.Nodes = netNodes
 							newData.Nodes = netNodes
 							for i := range netNodes {
 							for i := range netNodes {
-								if netNodes[i].IsIngressGateway == "yes" {
+								if netNodes[i].IsIngressGateway {
 									newData.Vpn = append(newData.Vpn, netNodes[i])
 									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...)
 										newData.Clients = append(newData.Clients, clients...)
 									}
 									}
 								}
 								}
@@ -134,7 +134,7 @@ func getNetworkUserData(w http.ResponseWriter, r *http.Request) {
 							}
 							}
 						}
 						}
 						for i := range netNodes {
 						for i := range netNodes {
-							if netNodes[i].IsIngressGateway == "yes" {
+							if netNodes[i].IsIngressGateway {
 								newData.Vpn = append(newData.Vpn, netNodes[i])
 								newData.Vpn = append(newData.Vpn, netNodes[i])
 							}
 							}
 						}
 						}

+ 14 - 13
ee/logic/failover.go

@@ -1,6 +1,7 @@
 package logic
 package logic
 
 
 import (
 import (
+	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
@@ -24,11 +25,11 @@ func ResetFailover(network string) error {
 	for _, node := range nodes {
 	for _, node := range nodes {
 		err = SetFailover(&node)
 		err = SetFailover(&node)
 		if err != nil {
 		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 {
 		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
 	return nil
@@ -43,7 +44,7 @@ func determineFailoverCandidate(nodeToBeRelayed *models.Node) *models.Node {
 		return nil
 		return nil
 	}
 	}
 
 
-	currentMetrics, err := logic.GetMetrics(nodeToBeRelayed.ID)
+	currentMetrics, err := logic.GetMetrics(nodeToBeRelayed.ID.String())
 	if err != nil || currentMetrics == nil || currentMetrics.Connectivity == nil {
 	if err != nil || currentMetrics == nil || currentMetrics.Connectivity == nil {
 		return nil
 		return nil
 	}
 	}
@@ -55,10 +56,10 @@ func determineFailoverCandidate(nodeToBeRelayed *models.Node) *models.Node {
 			continue
 			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]
 				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 {
 func setFailoverNode(failoverNode, node *models.Node) error {
 
 
 	node.FailoverNode = failoverNode.ID
 	node.FailoverNode = failoverNode.ID
-	nodeToUpdate, err := logic.GetNodeByID(node.ID)
+	nodeToUpdate, err := logic.GetNodeByID(node.ID.String())
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -95,25 +96,25 @@ func WipeFailover(nodeid string) error {
 
 
 // WipeAffectedFailoversOnly - wipes failovers for nodes that have given node (ID)
 // WipeAffectedFailoversOnly - wipes failovers for nodes that have given node (ID)
 // in their respective failover lists
 // in their respective failover lists
-func WipeAffectedFailoversOnly(nodeid, network string) error {
+func WipeAffectedFailoversOnly(nodeid uuid.UUID, network string) error {
 	currentNetworkNodes, err := logic.GetNetworkNodes(network)
 	currentNetworkNodes, err := logic.GetNetworkNodes(network)
 	if err != nil {
 	if err != nil {
 		return nil
 		return nil
 	}
 	}
-	WipeFailover(nodeid)
+	WipeFailover(nodeid.String())
 
 
 	for i := range currentNetworkNodes {
 	for i := range currentNetworkNodes {
 		currNodeID := currentNetworkNodes[i].ID
 		currNodeID := currentNetworkNodes[i].ID
 		if currNodeID == nodeid {
 		if currNodeID == nodeid {
 			continue
 			continue
 		}
 		}
-		currMetrics, err := logic.GetMetrics(currNodeID)
+		currMetrics, err := logic.GetMetrics(currNodeID.String())
 		if err != nil || currMetrics == nil {
 		if err != nil || currMetrics == nil {
 			continue
 			continue
 		}
 		}
 		if currMetrics.FailoverPeers != nil {
 		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 {
 	if err == nil {
 		limits.Users = len(users)
 		limits.Users = len(users)
 	}
 	}
-	limits.Servers = logic.GetServerCount()
 	return
 	return
 }
 }

+ 23 - 56
go.mod

@@ -15,98 +15,65 @@ require (
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 	github.com/stretchr/testify v1.8.1
 	github.com/stretchr/testify v1.8.1
 	github.com/txn2/txeh v1.3.0
 	github.com/txn2/txeh v1.3.0
-	github.com/urfave/cli/v2 v2.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/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
 	golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220324164955-056925b7df31
 	google.golang.org/protobuf v1.28.1 // indirect
 	google.golang.org/protobuf v1.28.1 // indirect
-	gopkg.in/ini.v1 v1.67.0
 	gopkg.in/yaml.v3 v3.0.1
 	gopkg.in/yaml.v3 v3.0.1
 )
 )
 
 
 require (
 require (
 	filippo.io/edwards25519 v1.0.0
 	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/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
 	github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0
 )
 )
 
 
 require (
 require (
-	github.com/agnivade/levenshtein v1.1.1
 	github.com/coreos/go-oidc/v3 v3.4.0
 	github.com/coreos/go-oidc/v3 v3.4.0
 	github.com/gorilla/websocket v1.5.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/guumaster/tablewriter v0.0.10
+	github.com/matryer/is v1.4.0
 	github.com/olekukonko/tablewriter v0.0.5
 	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 (
 require (
 	cloud.google.com/go/compute v1.7.0 // indirect
 	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/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/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/locales v0.14.0 // indirect
 	github.com/go-playground/universal-translator v0.18.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/golang/protobuf v1.5.2 // indirect
 	github.com/google/go-cmp v0.5.8 // 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/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/leodido/go-urn v1.2.1 // indirect
 	github.com/mattn/go-runewidth v0.0.10 // indirect
 	github.com/mattn/go-runewidth v0.0.10 // indirect
 	github.com/mdlayher/genetlink v1.2.0 // indirect
 	github.com/mdlayher/genetlink v1.2.0 // indirect
 	github.com/mdlayher/netlink v1.6.0 // indirect
 	github.com/mdlayher/netlink v1.6.0 // indirect
 	github.com/mdlayher/socket v0.1.1 // 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/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/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
 	golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
 	google.golang.org/appengine v1.6.7 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
 	gopkg.in/square/go-jose.v2 v2.6.0 // 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.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.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.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.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
 cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
 cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
 cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
 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.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
 cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
 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.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.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
 cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
 cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
 cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
 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/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.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/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/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.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
 cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
 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.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.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.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=
 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=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
 filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
 filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
 filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
-fyne.io/fyne/v2 v2.2.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 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/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/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/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/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/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 v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 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/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/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/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-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-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/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 h1:xz7elHb/LDwm/ERpwHd+5nb7wFHL32rsr6bBOgaeu6g=
 github.com/coreos/go-oidc/v3 v3.4.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw=
 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.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 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-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/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/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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
-github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
-github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
-github.com/docker/distribution v2.8.1+incompatible/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 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6rNA5PM12m4=
 github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
 github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -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.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/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/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.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
 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/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.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/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 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-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-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 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
 github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
 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=
 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/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 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
 github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
 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 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
 github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 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=
 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.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 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 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 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.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=
 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-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-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-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-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-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/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.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
 github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
 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/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 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
 github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
 github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
 github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
 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.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 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
 github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 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/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 h1:A0HD94yMdt4usgxBjoEceNeE0XMJ027euoHAzsPqBQs=
 github.com/guumaster/tablewriter v0.0.10/go.mod h1:p4FRFhyfo0UD9ZLmMRbbJooTUsxo6b80qZTERVDWrH8=
 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.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/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/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-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/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.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
 github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
 github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 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 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
 github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
 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.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/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/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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 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.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 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/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.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 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/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 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
 github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 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.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.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 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
 github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
 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 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
 github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
 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 h1:4yrIkRV5Wfk1WfpWTcoOlGmsWgQj3OtQN9ZsbrE+XtU=
 github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ=
 github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ=
 github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0=
 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/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 h1:q3uOGirUPfAV2MUoaC7BavjQ154J7+JOkTWyiV+intI=
 github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
 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 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
 github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
 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-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.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 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
 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.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/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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 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 h1:Y2hUrkfuM0on62KZOci/VLijlkdF/yeWU262BQgvcjE=
 github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU=
 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=
 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.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.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 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 h1:BSnJgAfHzEp7o8PYJ7YfwAVHhqu7BYUTggcn/LGlUWY=
 github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f/go.mod h1:UW/gxgQwSePTvL1KA8QEHsXeYHP4xkoXgbDdN781p34=
 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 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.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/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/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 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
 github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 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 h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
 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/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.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.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 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.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.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 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 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.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.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 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.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.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.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 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 h1:vnbv63htVMZCaQgLqVBxKvj2+HHHFUzNW7I183zjg3E=
 github.com/txn2/txeh v1.3.0/go.mod h1:O7M6gUTPeMF+vsa4c4Ipx3JDkOYrruB1Wry8QRsMcw8=
 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/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-github.com/urfave/cli/v2 v2.4.0/go.mod h1:NX9W0zmTvedE5oDoOMs2RTC8RvdK98NTYZE5LbaEYPg=
-github.com/urfave/cli/v2 v2.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/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 h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g=
 github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
 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=
 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.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/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.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.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 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.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
 go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 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-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-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-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-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-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-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-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-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-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-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-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-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-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-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
 golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 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/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-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-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-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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 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/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-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-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.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.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 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/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-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-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-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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 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-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-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-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-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-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-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-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-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-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-20211111083644-e5c967477495/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/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-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-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-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.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-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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/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-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-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-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-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-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/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-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 h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
 golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 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-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-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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 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-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-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-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-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-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-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/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-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-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-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-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-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-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-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-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-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-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-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210603125802-9665404d3644/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-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-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-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-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-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/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-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-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-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.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-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-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.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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5/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.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.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-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-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/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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/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-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-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-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-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-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/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-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-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-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-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-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/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-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-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-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-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-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/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-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-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-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.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 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.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.3/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.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.5/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-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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/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/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/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-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 h1:AgW3hljgTzuRbCB0j+q9tXT0uy6ij7vMjEzSCeMlQY0=
 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220324164955-056925b7df31/go.mod h1:8P32Ilp1kCpwB4ItaHyvSk4xAtnpQ+8gQVfg5WaO1TU=
 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=
 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.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
 google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
 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.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.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
 google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
 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=
 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-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-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-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-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-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-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/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=
 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 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-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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 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/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 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
 gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 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.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.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-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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 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-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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/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
 kind: Service
 metadata:
 metadata:
   labels:
   labels:
-  name: 'netmaker-wireguard'
+    name: 'netmaker-wireguard'
 spec:
 spec:
   externalTrafficPolicy: Local
   externalTrafficPolicy: Local
   type: NodePort
   type: NodePort

+ 15 - 4
logic/extpeers.go

@@ -34,7 +34,7 @@ func GetExtPeersList(node *models.Node) ([]models.ExtPeersResponse, error) {
 			continue
 			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)
 			peers = append(peers, peer)
 		}
 		}
 	}
 	}
@@ -57,7 +57,7 @@ func GetEgressRangesOnNetwork(client *models.ExtClient) ([]string, error) {
 		if currentNode.Network != client.Network {
 		if currentNode.Network != client.Network {
 			continue
 			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 {
 			if len(currentNode.EgressGatewayRanges) > 0 {
 				result = append(result, currentNode.EgressGatewayRanges...)
 				result = append(result, currentNode.EgressGatewayRanges...)
 			}
 			}
@@ -137,7 +137,13 @@ func CreateExtClient(extclient *models.ExtClient) error {
 			if err != nil {
 			if err != nil {
 				return err
 				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 {
 			if err != nil {
 				return err
 				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
 // CreateEgressGateway - creates an egress gateway
 func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, error) {
 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 {
 	for i, cidr := range gateway.Ranges {
 		normalized, err := NormalizeCIDR(cidr)
 		normalized, err := NormalizeCIDR(cidr)
 		if err != nil {
 		if err != nil {
@@ -23,14 +31,10 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
 		gateway.Ranges[i] = normalized
 		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")
 		return models.Node{}, errors.New("firewall is not supported for egress gateways")
 	}
 	}
 	if gateway.NatEnabled == "" {
 	if gateway.NatEnabled == "" {
@@ -40,20 +44,17 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
 	if err != nil {
 	if err != nil {
 		return models.Node{}, err
 		return models.Node{}, err
 	}
 	}
-	node.IsEgressGateway = "yes"
+	node.IsEgressGateway = true
 	node.EgressGatewayRanges = gateway.Ranges
 	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
 	node.EgressGatewayRequest = gateway // store entire request for use when preserving the egress gateway
 	postUpCmd := ""
 	postUpCmd := ""
 	postDownCmd := ""
 	postDownCmd := ""
 	ipv4, ipv6 := getNetworkProtocols(gateway.Ranges)
 	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:
 		case models.FIREWALL_NFTABLES:
 			// nftables only supported on Linux
 			// nftables only supported on Linux
 			// assumes chains eg FORWARD and postrouting already exist
 			// 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
 			// 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
 			// 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.
 			// 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
 		default: // iptables assumed
 			logger.Log(3, "creating egress gateway nftables is not present")
 			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
 		// spacing around ; is important for later parsing of postup/postdown in wireguard/common.go
 		postUpCmd = "kldload ipfw ipfw_nat ; "
 		postUpCmd = "kldload ipfw ipfw_nat ; "
 		postUpCmd += "ipfw disable one_pass ; "
 		postUpCmd += "ipfw disable one_pass ; "
@@ -108,7 +109,7 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
 	if err != nil {
 	if err != nil {
 		return node, err
 		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 models.Node{}, err
 	}
 	}
 	return node, nil
 	return node, nil
@@ -131,13 +132,16 @@ func ValidateEgressGateway(gateway models.EgressGatewayRequest) error {
 
 
 // DeleteEgressGateway - deletes egress from node
 // DeleteEgressGateway - deletes egress from node
 func DeleteEgressGateway(network, nodeid string) (models.Node, error) {
 func DeleteEgressGateway(network, nodeid string) (models.Node, error) {
-
 	node, err := GetNodeByID(nodeid)
 	node, err := GetNodeByID(nodeid)
 	if err != nil {
 	if err != nil {
 		return models.Node{}, err
 		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.EgressGatewayRanges = []string{}
 	node.EgressGatewayRequest = models.EgressGatewayRequest{} // remove preserved request as the egress gateway is gone
 	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)
 	// 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.IngressGatewayRange)
 	cidrs = append(cidrs, node.IngressGatewayRange6)
 	cidrs = append(cidrs, node.IngressGatewayRange6)
 	ipv4, ipv6 := getNetworkProtocols(cidrs)
 	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
 		// 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:
 			case models.FIREWALL_NFTABLES:
 				// nftables only supported on Linux
 				// nftables only supported on Linux
 				// assumes chains eg FORWARD and postrouting already exist
 				// assumes chains eg FORWARD and postrouting already exist
 				logger.Log(3, "deleting egress gateway nftables is present")
 				logger.Log(3, "deleting egress gateway nftables is present")
-				node.PostUp, node.PostDown = firewallNFTCommandsCreateIngress(node.Interface)
+				node.PostUp, node.PostDown = firewallNFTCommandsCreateIngress(iface)
 			default:
 			default:
 				logger.Log(3, "deleting egress gateway nftables is not present")
 				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
 		// 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 {
 	if err != nil {
 		return models.Node{}, err
 		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 models.Node{}, err
 	}
 	}
 	return node, nil
 	return node, nil
@@ -181,15 +186,14 @@ func CreateIngressGateway(netid string, nodeid string, failover bool) (models.No
 
 
 	var postUpCmd, postDownCmd string
 	var postUpCmd, postDownCmd string
 	node, err := GetNodeByID(nodeid)
 	node, err := GetNodeByID(nodeid)
-
 	if err != nil {
 	if err != nil {
 		return models.Node{}, err
 		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")
 		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 {
 	if err != nil {
 		return models.Node{}, err
 		return models.Node{}, err
 	}
 	}
-	node.IsIngressGateway = "yes"
+	node.IsIngressGateway = true
 	cidrs := []string{}
 	cidrs := []string{}
 	cidrs = append(cidrs, network.AddressRange)
 	cidrs = append(cidrs, network.AddressRange)
 	cidrs = append(cidrs, network.AddressRange6)
 	cidrs = append(cidrs, network.AddressRange6)
 	node.IngressGatewayRange = network.AddressRange
 	node.IngressGatewayRange = network.AddressRange
 	node.IngressGatewayRange6 = network.AddressRange6
 	node.IngressGatewayRange6 = network.AddressRange6
 	ipv4, ipv6 := getNetworkProtocols(cidrs)
 	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:
 	case models.FIREWALL_NFTABLES:
 		// nftables only supported on Linux
 		// nftables only supported on Linux
 		// assumes chains eg FORWARD and postrouting already exist
 		// assumes chains eg FORWARD and postrouting already exist
 		logger.Log(3, "creating ingress gateway nftables is present")
 		logger.Log(3, "creating ingress gateway nftables is present")
-		postUpCmd, postDownCmd = firewallNFTCommandsCreateIngress(node.Interface)
+		postUpCmd, postDownCmd = firewallNFTCommandsCreateIngress(iface)
 	default:
 	default:
 		logger.Log(3, "creating ingress gateway using nftables is not present")
 		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 != "" {
 	if node.PostUp != "" {
@@ -233,15 +234,14 @@ func CreateIngressGateway(netid string, nodeid string, failover bool) (models.No
 	node.SetLastModified()
 	node.SetLastModified()
 	node.PostUp = postUpCmd
 	node.PostUp = postUpCmd
 	node.PostDown = postDownCmd
 	node.PostDown = postDownCmd
-	node.UDPHolePunch = "no"
 	if failover && servercfg.Is_EE {
 	if failover && servercfg.Is_EE {
-		node.Failover = "yes"
+		node.Failover = true
 	}
 	}
 	data, err := json.Marshal(&node)
 	data, err := json.Marshal(&node)
 	if err != nil {
 	if err != nil {
 		return models.Node{}, err
 		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 {
 	if err != nil {
 		return models.Node{}, err
 		return models.Node{}, err
 	}
 	}
@@ -251,34 +251,34 @@ func CreateIngressGateway(netid string, nodeid string, failover bool) (models.No
 
 
 // DeleteIngressGateway - deletes an ingress gateway
 // DeleteIngressGateway - deletes an ingress gateway
 func DeleteIngressGateway(networkName string, nodeid string) (models.Node, bool, error) {
 func DeleteIngressGateway(networkName string, nodeid string) (models.Node, bool, error) {
-
 	node, err := GetNodeByID(nodeid)
 	node, err := GetNodeByID(nodeid)
 	if err != nil {
 	if err != nil {
 		return models.Node{}, false, err
 		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 {
 	if err != nil {
 		return models.Node{}, false, err
 		return models.Node{}, false, err
 	}
 	}
 	// delete ext clients belonging to ingress gateway
 	// 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
 		return models.Node{}, false, err
 	}
 	}
 	logger.Log(3, "deleting ingress gateway")
 	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.IngressGatewayRange = ""
-	node.Failover = "no"
+	node.Failover = false
 
 
 	// default to removing postup and postdown
 	// default to removing postup and postdown
 	node.PostUp = ""
 	node.PostUp = ""
 	node.PostDown = ""
 	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 != "" {
 	if node.EgressGatewayRequest.NodeID != "" {
 		_, err := CreateEgressGateway(node.EgressGatewayRequest)
 		_, err := CreateEgressGateway(node.EgressGatewayRequest)
 		if err != nil {
 		if err != nil {
@@ -291,7 +291,7 @@ func DeleteIngressGateway(networkName string, nodeid string) (models.Node, bool,
 	if err != nil {
 	if err != nil {
 		return models.Node{}, false, err
 		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 {
 	if err != nil {
 		return models.Node{}, wasFailover, err
 		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.
 // 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
 	// spacing around ; is important for later parsing of postup/postdown in wireguard/common.go
 	postUp := ""
 	postUp := ""
 	postDown := ""
 	postDown := ""
@@ -351,7 +351,7 @@ func firewallNFTCommandsCreateEgress(networkInterface string, gatewayInterface s
 
 
 		postDown += "nft flush table filter ; "
 		postDown += "nft flush table filter ; "
 
 
-		if egressNatEnabled == "yes" {
+		if egressNatEnabled {
 			postUp += "nft add table nat ; "
 			postUp += "nft add table nat ; "
 			postUp += "nft add chain nat postrouting ; "
 			postUp += "nft add chain nat postrouting ; "
 			postUp += "nft add rule ip nat postrouting oifname " + gatewayInterface + " counter masquerade ; "
 			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 ; "
 		postDown += "nft flush table ip6 filter ; "
 
 
-		if egressNatEnabled == "yes" {
+		if egressNatEnabled {
 			postUp += "nft add table ip6 nat ; "
 			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 prerouting { type nat hook prerouting priority 0 ;}' ; "
 			postUp += "nft 'add chain ip6 nat postrouting { type nat hook postrouting 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.
 // 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
 	// spacing around ; is important for later parsing of postup/postdown in wireguard/common.go
 	postUp := ""
 	postUp := ""
 	postDown := ""
 	postDown := ""
@@ -421,7 +421,7 @@ func firewallIPTablesCommandsCreateEgress(networkInterface string, gatewayInterf
 		postDown += "iptables -D FORWARD -i " + networkInterface + " -j ACCEPT ; "
 		postDown += "iptables -D FORWARD -i " + networkInterface + " -j ACCEPT ; "
 		postDown += "iptables -D FORWARD -o " + 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 ; "
 			postUp += "iptables -t nat -A POSTROUTING -o " + gatewayInterface + " -j MASQUERADE ; "
 			postDown += "iptables -t nat -D 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 -i " + networkInterface + " -j ACCEPT ; "
 		postDown += "ip6tables -D FORWARD -o " + 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 ; "
 			postUp += "ip6tables -t nat -A POSTROUTING -o " + gatewayInterface + " -j MASQUERADE ; "
 			postDown += "ip6tables -t nat -D 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/database"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/wireguard"
 )
 )
 
 
 // GetMetrics - gets the metrics
 // GetMetrics - gets the metrics
@@ -38,69 +37,3 @@ func UpdateMetrics(nodeid string, metrics *models.Metrics) error {
 func DeleteMetrics(nodeid string) error {
 func DeleteMetrics(nodeid string) error {
 	return database.DeleteRecord(database.METRICS_TABLE_NAME, nodeid)
 	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
 package metrics
 
 
 import (
 import (
-	"runtime"
 	"time"
 	"time"
 
 
 	"github.com/go-ping/ping"
 	"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/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/wireguard"
 	"golang.zx2c4.com/wireguard/wgctrl"
 	"golang.zx2c4.com/wireguard/wgctrl"
 )
 )
 
 
 // Collect - collects metrics
 // 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
 	var metrics models.Metrics
 	metrics.Connectivity = make(map[string]models.Metric)
 	metrics.Connectivity = make(map[string]models.Metric)
 	var wgclient, err = wgctrl.New()
 	var wgclient, err = wgctrl.New()
@@ -22,19 +22,13 @@ func Collect(iface string, peerMap models.PeerMap) (*models.Metrics, error) {
 		return &metrics, err
 		return &metrics, err
 	}
 	}
 	defer wgclient.Close()
 	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)
 	device, err := wgclient.Device(iface)
 	if err != nil {
 	if err != nil {
 		fillUnconnectedData(&metrics, peerMap)
 		fillUnconnectedData(&metrics, peerMap)
 		return &metrics, err
 		return &metrics, err
 	}
 	}
+	metrics.ProxyMetrics = make(map[string]proxy_models.Metric)
+
 	// TODO handle freebsd??
 	// TODO handle freebsd??
 	for i := range device.Peers {
 	for i := range device.Peers {
 		currPeer := device.Peers[i]
 		currPeer := device.Peers[i]
@@ -88,6 +82,8 @@ func Collect(iface string, peerMap models.PeerMap) (*models.Metrics, error) {
 
 
 		newMetric.TotalTime = 1
 		newMetric.TotalTime = 1
 		metrics.Connectivity[id] = newMetric
 		metrics.Connectivity[id] = newMetric
+		metrics.ProxyMetrics[id] = proxy_metrics.GetMetric(network, currPeer.PublicKey.String())
+		proxy_metrics.ResetMetricsForPeer(network, currPeer.PublicKey.String())
 	}
 	}
 
 
 	fillUnconnectedData(&metrics, peerMap)
 	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
 // GetExchangedBytesForNode - get exchanged bytes for current node peers
 func GetExchangedBytesForNode(node *models.Node, metrics *models.Metrics) error {
 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 {
 	if err != nil {
 		logger.Log(0, "Failed to get peers: ", err.Error())
 		logger.Log(0, "Failed to get peers: ", err.Error())
 		return err
 		return err
@@ -107,7 +106,7 @@ func GetExchangedBytesForNode(node *models.Node, metrics *models.Metrics) error
 		return err
 		return err
 	}
 	}
 	defer wgclient.Close()
 	defer wgclient.Close()
-	device, err := wgclient.Device(node.Interface)
+	device, err := wgclient.Device(models.WIREGUARD_INTERFACE)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 37 - 134
logic/networks.go

@@ -5,7 +5,6 @@ import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"net"
 	"net"
-	"os/exec"
 	"strings"
 	"strings"
 
 
 	"github.com/c-robinson/iplib"
 	"github.com/c-robinson/iplib"
@@ -15,7 +14,6 @@ import (
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
 	"github.com/gravitl/netmaker/logic/pro"
 	"github.com/gravitl/netmaker/logic/pro"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/validation"
 	"github.com/gravitl/netmaker/validation"
 )
 )
 
 
@@ -51,18 +49,6 @@ func DeleteNetwork(network string) error {
 	nodeCount, err := GetNetworkNonServerNodeCount(network)
 	nodeCount, err := GetNetworkNonServerNodeCount(network)
 	if nodeCount == 0 || database.IsEmptyRecord(err) {
 	if nodeCount == 0 || database.IsEmptyRecord(err) {
 		// delete server nodes first then db records
 		// 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 {
 		if err = pro.RemoveAllNetworkUsers(network); err != nil {
 			logger.Log(0, "failed to remove network users on network delete for network", network, err.Error())
 			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 {
 		if err = json.Unmarshal([]byte(value), &node); err != nil {
 			return count, err
 			return count, err
 		} else {
 		} else {
-			if node.Network == networkName && node.IsServer != "yes" {
+			if node.Network == networkName {
 				count++
 				count++
 			}
 			}
 		}
 		}
@@ -178,22 +164,22 @@ func GetNetworkSettings(networkname string) (models.Network, error) {
 }
 }
 
 
 // UniqueAddress - see if address is unique
 // 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
 	var network models.Network
 	network, err := GetParentNetwork(networkName)
 	network, err := GetParentNetwork(networkName)
 	if err != nil {
 	if err != nil {
 		logger.Log(0, "UniqueAddressServer encountered  an error")
 		logger.Log(0, "UniqueAddressServer encountered  an error")
-		return "666", err
+		return add, err
 	}
 	}
 
 
 	if network.IsIPv4 == "no" {
 	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
 	//ensure AddressRange is valid
 	if _, _, err := net.ParseCIDR(network.AddressRange); err != nil {
 	if _, _, err := net.ParseCIDR(network.AddressRange); err != nil {
 		logger.Log(0, "UniqueAddress encountered  an error")
 		logger.Log(0, "UniqueAddress encountered  an error")
-		return "666", err
+		return add, err
 	}
 	}
 	net4 := iplib.Net4FromStr(network.AddressRange)
 	net4 := iplib.Net4FromStr(network.AddressRange)
 	newAddrs := net4.FirstAddress()
 	newAddrs := net4.FirstAddress()
@@ -205,7 +191,7 @@ func UniqueAddress(networkName string, reverse bool) (string, error) {
 	for {
 	for {
 		if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, false) &&
 		if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, false) &&
 			IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, false) {
 			IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, false) {
-			return newAddrs.String(), nil
+			return newAddrs, nil
 		}
 		}
 		if reverse {
 		if reverse {
 			newAddrs, err = net4.PreviousIP(newAddrs)
 			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
 // IsIPUnique - checks if an IP is unique
@@ -236,11 +222,11 @@ func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool {
 			continue
 			continue
 		}
 		}
 		if isIpv6 {
 		if isIpv6 {
-			if node.Address6 == ip && node.Network == network {
+			if node.Address6.IP.String() == ip && node.Network == network {
 				return false
 				return false
 			}
 			}
 		} else {
 		} else {
-			if node.Address == ip && node.Network == network {
+			if node.Address.IP.String() == ip && node.Network == network {
 				return false
 				return false
 			}
 			}
 		}
 		}
@@ -250,21 +236,21 @@ func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool {
 }
 }
 
 
 // UniqueAddress6 - see if ipv6 address is unique
 // 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
 	var network models.Network
 	network, err := GetParentNetwork(networkName)
 	network, err := GetParentNetwork(networkName)
 	if err != nil {
 	if err != nil {
 		fmt.Println("Network Not Found")
 		fmt.Println("Network Not Found")
-		return "", err
+		return add, err
 	}
 	}
 	if network.IsIPv6 == "no" {
 	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
 	//ensure AddressRange is valid
 	if _, _, err := net.ParseCIDR(network.AddressRange6); err != nil {
 	if _, _, err := net.ParseCIDR(network.AddressRange6); err != nil {
-		return "666", err
+		return add, err
 	}
 	}
 	net6 := iplib.Net6FromStr(network.AddressRange6)
 	net6 := iplib.Net6FromStr(network.AddressRange6)
 
 
@@ -273,13 +259,13 @@ func UniqueAddress6(networkName string, reverse bool) (string, error) {
 		newAddrs, err = net6.PreviousIP(net6.LastAddress())
 		newAddrs, err = net6.PreviousIP(net6.LastAddress())
 	}
 	}
 	if err != nil {
 	if err != nil {
-		return "", err
+		return add, err
 	}
 	}
 
 
 	for {
 	for {
 		if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, true) &&
 		if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, true) &&
 			IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, true) {
 			IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, true) {
-			return newAddrs.String(), nil
+			return newAddrs, nil
 		}
 		}
 		if reverse {
 		if reverse {
 			newAddrs, err = net6.PreviousIP(newAddrs)
 			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
 // GetLocalIP - gets the local ip
 func GetLocalIP(node models.Node) string {
 func GetLocalIP(node models.Node) string {
-
 	var local string
 	var local string
-
 	ifaces, err := net.Interfaces()
 	ifaces, err := net.Interfaces()
 	if err != nil {
 	if err != nil {
 		return local
 		return local
 	}
 	}
-	_, localrange, err := net.ParseCIDR(node.LocalRange)
+	host, err := GetHost(node.HostID.String())
 	if err != nil {
 	if err != nil {
 		return local
 		return local
 	}
 	}
-
+	localrange := host.LocalRange
 	found := false
 	found := false
 	for _, i := range ifaces {
 	for _, i := range ifaces {
 		if i.Flags&net.FlagUp == 0 {
 		if i.Flags&net.FlagUp == 0 {
@@ -327,7 +311,7 @@ func GetLocalIP(node models.Node) string {
 				if !found {
 				if !found {
 					ip = v.IP
 					ip = v.IP
 					local = ip.String()
 					local = ip.String()
-					if node.IsLocal == "yes" {
+					if node.IsLocal {
 						found = localrange.Contains(ip)
 						found = localrange.Contains(ip)
 					} else {
 					} else {
 						found = true
 						found = true
@@ -337,7 +321,7 @@ func GetLocalIP(node models.Node) string {
 				if !found {
 				if !found {
 					ip = v.IP
 					ip = v.IP
 					local = ip.String()
 					local = ip.String()
-					if node.IsLocal == "yes" {
+					if node.IsLocal {
 						found = localrange.Contains(ip)
 						found = localrange.Contains(ip)
 
 
 					} else {
 					} else {
@@ -369,53 +353,27 @@ func UpdateNetworkLocalAddresses(networkName string) error {
 			return err
 			return err
 		}
 		}
 		if node.Network == networkName {
 		if node.Network == networkName {
-			var ipaddr string
+			var ipaddr net.IP
 			var iperr error
 			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 {
 			if iperr != nil {
 				fmt.Println("error in node  address assignment!")
 				fmt.Println("error in node  address assignment!")
 				return iperr
 				return iperr
 			}
 			}
 
 
-			node.Address = ipaddr
+			node.Address.IP = ipaddr
 			newNodeData, err := json.Marshal(&node)
 			newNodeData, err := json.Marshal(&node)
 			if err != nil {
 			if err != nil {
 				logger.Log(1, "error in node  address assignment!")
 				logger.Log(1, "error in node  address assignment!")
 				return err
 				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
 	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
 // RemoveNetworkNodeIPv6Addresses - removes network node IPv6 addresses
 func RemoveNetworkNodeIPv6Addresses(networkName string) error {
 func RemoveNetworkNodeIPv6Addresses(networkName string) error {
 
 
@@ -433,12 +391,12 @@ func RemoveNetworkNodeIPv6Addresses(networkName string) error {
 			return err
 			return err
 		}
 		}
 		if node.Network == networkName {
 		if node.Network == networkName {
-			node.Address6 = ""
+			node.Address6.IP = nil
 			data, err := json.Marshal(&node)
 			data, err := json.Marshal(&node)
 			if err != nil {
 			if err != nil {
 				return err
 				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
 			return err
 		}
 		}
 		if node.Network == networkName {
 		if node.Network == networkName {
-			var ipaddr string
+			var ipaddr net.IP
 			var iperr error
 			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 {
 			if iperr != nil {
 				logger.Log(1, "error in node ipv4 address assignment!")
 				logger.Log(1, "error in node ipv4 address assignment!")
 				return iperr
 				return iperr
 			}
 			}
 
 
-			node.Address = ipaddr
+			node.Address.IP = ipaddr
 			data, err := json.Marshal(&node)
 			data, err := json.Marshal(&node)
 			if err != nil {
 			if err != nil {
 				return err
 				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
 			return err
 		}
 		}
 		if node.Network == networkName {
 		if node.Network == networkName {
-			var ipaddr string
+			var ipaddr net.IP
 			var iperr error
 			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 {
 			if iperr != nil {
 				logger.Log(1, "error in node ipv6 address assignment!")
 				logger.Log(1, "error in node ipv6 address assignment!")
 				return iperr
 				return iperr
 			}
 			}
 
 
-			node.Address6 = ipaddr
+			node.Address6.IP = ipaddr
 			data, err := json.Marshal(&node)
 			data, err := json.Marshal(&node)
 			if err != nil {
 			if err != nil {
 				return err
 				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 {
 			if err != nil {
 				return err
 				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
 	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
 package logic
 
 
 import (
 import (
+	"context"
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	"sort"
+	"net"
 	"time"
 	"time"
 
 
 	validator "github.com/go-playground/validator/v10"
 	validator "github.com/go-playground/validator/v10"
@@ -19,11 +20,16 @@ import (
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/validation"
 	"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
 // GetNetworkNodes - gets the nodes of a network
 func GetNetworkNodes(network string) ([]models.Node, error) {
 func GetNetworkNodes(network string) ([]models.Node, error) {
@@ -40,92 +46,11 @@ func GetNetworkNodes(network string) ([]models.Node, error) {
 	return nodes, nil
 	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
 // UpdateNode - takes a node and updates another node with it's values
 func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
 func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
-	if newNode.Address != currentNode.Address {
+	if newNode.Address.String() != currentNode.Address.String() {
 		if network, err := GetParentNetwork(newNode.Network); err == nil {
 		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)
 				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
 	nodeACLDelta := currentNode.DefaultACL != newNode.DefaultACL
 	newNode.Fill(currentNode)
 	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
 	// check for un-settable server values
 	if err := ValidateNode(newNode, true); err != nil {
 	if err := ValidateNode(newNode, true); err != nil {
 		return err
 		return err
@@ -144,7 +66,7 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
 	if newNode.ID == currentNode.ID {
 	if newNode.ID == currentNode.ID {
 		if nodeACLDelta {
 		if nodeACLDelta {
 			if err := updateProNodeACLS(newNode); err != nil {
 			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
 				return err
 			}
 			}
 		}
 		}
@@ -153,36 +75,48 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
 		if data, err := json.Marshal(newNode); err != nil {
 		if data, err := json.Marshal(newNode); err != nil {
 			return err
 			return err
 		} else {
 		} 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
 			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 {
 	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
 			return err
 		}
 		}
 	}
 	}
-
 	if servercfg.IsDNSMode() {
 	if servercfg.IsDNSMode() {
 		SetDNS()
 		SetDNS()
 	}
 	}
 	if node.OwnerID != "" {
 	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 {
 		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 {
 	if err != nil {
 		// ignoring for now, could hit a nil pointer if delete called twice
 		// 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
 	// 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
 	return nil
 }
 }
 
 
 // IsNodeIDUnique - checks if node id is unique
 // IsNodeIDUnique - checks if node id is unique
 func IsNodeIDUnique(node *models.Node) (bool, error) {
 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
 	return database.IsEmptyRecord(err), err
 }
 }
 
 
@@ -238,18 +165,10 @@ func ValidateNode(node *models.Node, isUpdate bool) error {
 		_, err := GetNetworkByNode(node)
 		_, err := GetNetworkByNode(node)
 		return err == nil
 		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)
 	err := v.Struct(node)
-
 	return err
 	return err
 }
 }
 
 
@@ -260,118 +179,13 @@ func IsFailoverPresent(network string) bool {
 		return false
 		return false
 	}
 	}
 	for i := range netNodes {
 	for i := range netNodes {
-		if netNodes[i].Failover == "yes" {
+		if netNodes[i].Failover {
 			return true
 			return true
 		}
 		}
 	}
 	}
 	return false
 	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
 // GetAllNodes - returns all nodes in the DB
 func GetAllNodes() ([]models.Node, error) {
 func GetAllNodes() ([]models.Node, error) {
 	var nodes []models.Node
 	var nodes []models.Node
@@ -396,24 +210,6 @@ func GetAllNodes() ([]models.Node, error) {
 	return nodes, nil
 	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
 // GetNetworkByNode - gets the network model from a node
 func GetNetworkByNode(node *models.Node) (models.Network, error) {
 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
 // SetNodeDefaults - sets the defaults of a node to avoid empty fields
 func SetNodeDefaults(node *models.Node) {
 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)
 	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 {
 	if node.PersistentKeepalive == 0 {
-		node.PersistentKeepalive = parentNetwork.DefaultKeepalive
+		node.PersistentKeepalive = time.Second * time.Duration(parentNetwork.DefaultKeepalive)
 	}
 	}
 	if node.PostUp == "" {
 	if node.PostUp == "" {
 		postup := parentNetwork.DefaultPostUp
 		postup := parentNetwork.DefaultPostUp
@@ -460,44 +253,41 @@ func SetNodeDefaults(node *models.Node) {
 		postdown := parentNetwork.DefaultPostDown
 		postdown := parentNetwork.DefaultPostDown
 		node.PostDown = postdown
 		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 ==
 	// == Parent Network settings ==
 
 
-	if node.MTU == 0 {
-		node.MTU = parentNetwork.DefaultMTU
-	}
 	// == node defaults if not set by parent ==
 	// == 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
 // GetRecordKey - get record key
@@ -541,7 +331,7 @@ func GetNodesByAddress(network string, addresses []string) ([]models.Node, error
 		return []models.Node{}, err
 		return []models.Node{}, err
 	}
 	}
 	for _, node := range allnodes {
 	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)
 			nodes = append(nodes, node)
 		}
 		}
 	}
 	}
@@ -589,7 +379,7 @@ func GetNodeRelay(network string, relayedNodeAddr string) (models.Node, error) {
 			logger.Log(2, err.Error())
 			logger.Log(2, err.Error())
 			continue
 			continue
 		}
 		}
-		if relay.IsRelay == "yes" {
+		if relay.IsRelay {
 			for _, addr := range relay.RelayAddrs {
 			for _, addr := range relay.RelayAddrs {
 				if addr == relayedNodeAddr {
 				if addr == relayedNodeAddr {
 					return relay, nil
 					return relay, nil
@@ -631,81 +421,9 @@ func GetDeletedNodeByID(uuid string) (models.Node, error) {
 	return node, nil
 	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
 // FindRelay - returns the node that is the relay for a relayed node
 func FindRelay(node *models.Node) *models.Node {
 func FindRelay(node *models.Node) *models.Node {
-	if node.IsRelayed == "no" {
+	if !node.IsRelayed {
 		return nil
 		return nil
 	}
 	}
 	peers, err := GetNetworkNodes(node.Network)
 	peers, err := GetNetworkNodes(node.Network)
@@ -713,11 +431,11 @@ func FindRelay(node *models.Node) *models.Node {
 		return nil
 		return nil
 	}
 	}
 	for _, peer := range peers {
 	for _, peer := range peers {
-		if peer.IsRelay == "no" {
+		if !peer.IsRelay {
 			continue
 			continue
 		}
 		}
 		for _, ip := range peer.RelayAddrs {
 		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
 				return &peer
 			}
 			}
 		}
 		}
@@ -731,10 +449,10 @@ func findNode(ip string) (*models.Node, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 	for _, node := range nodes {
 	for _, node := range nodes {
-		if node.Address == ip {
+		if node.Address.IP.String() == ip {
 			return &node, nil
 			return &node, nil
 		}
 		}
-		if node.Address6 == ip {
+		if node.Address6.IP.String() == ip {
 			return &node, nil
 			return &node, nil
 		}
 		}
 	}
 	}
@@ -749,13 +467,23 @@ func GetNetworkIngresses(network string) ([]models.Node, error) {
 		return []models.Node{}, err
 		return []models.Node{}, err
 	}
 	}
 	for i := range netNodes {
 	for i := range netNodes {
-		if netNodes[i].IsIngressGateway == "yes" {
+		if netNodes[i].IsIngressGateway {
 			ingresses = append(ingresses, netNodes[i])
 			ingresses = append(ingresses, netNodes[i])
 		}
 		}
 	}
 	}
 	return ingresses, nil
 	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 ==
 // == PRO ==
 
 
 func updateProNodeACLS(node *models.Node) error {
 func updateProNodeACLS(node *models.Node) error {
@@ -770,4 +498,134 @@ func updateProNodeACLS(node *models.Node) error {
 	return nil
 	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 ==
 // == END PRO ==

+ 496 - 161
logic/peers.go

@@ -3,6 +3,7 @@ package logic
 import (
 import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"log"
 	"net"
 	"net"
 	"sort"
 	"sort"
 	"strconv"
 	"strconv"
@@ -10,6 +11,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/c-robinson/iplib"
 	"github.com/c-robinson/iplib"
+	proxy_models "github.com/gravitl/netclient/nmproxy/models"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
@@ -20,23 +22,285 @@ import (
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 	"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
 // 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 peerUpdate models.PeerUpdate
 	var peers []wgtypes.PeerConfig
 	var peers []wgtypes.PeerConfig
 	var serverNodeAddresses = []models.ServerAddr{}
 	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 peerMap = make(models.PeerMap)
-
 	var metrics *models.Metrics
 	var metrics *models.Metrics
 	if servercfg.Is_EE {
 	if servercfg.Is_EE {
-		metrics, _ = GetMetrics(node.ID)
+		metrics, _ = GetMetrics(node.ID.String())
 	}
 	}
 	if metrics == nil {
 	if metrics == nil {
 		metrics = &models.Metrics{}
 		metrics = &models.Metrics{}
@@ -55,8 +319,11 @@ func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
 	if err != nil {
 	if err != nil {
 		return models.PeerUpdate{}, err
 		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)
 		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
 	// #2 Set local address: set_local - could be a LOT BETTER and fix some bugs with additional logic
 	// #3 Set allowedips: set_allowedips
 	// #3 Set allowedips: set_allowedips
 	for _, peer := range currentPeers {
 	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 {
 		if peer.ID == node.ID {
 			//skip yourself
 			//skip yourself
 			continue
 			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
 			//skip unconnected nodes
 			continue
 			continue
 		}
 		}
 
 
 		// if the node is not a server, set the endpoint
 		// 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
 				//skip -- will be added to relay
 				continue
 				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
 				// dont set peer endpoint if it's relayed by node
 				setEndpoint = false
 				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
 			//skip if not permitted by acl
 			continue
 			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
 			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 {
 		if err != nil {
+			logger.Log(0, "error retrieving host for node", node.ID.String(), err.Error())
 			return models.PeerUpdate{}, err
 			return models.PeerUpdate{}, err
 		}
 		}
-		if node.Endpoint == peer.Endpoint {
+		if host.EndpointIP.String() == peerHost.EndpointIP.String() {
 			//peer is on same network
 			//peer is on same network
 			// set_local
 			// 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 {
 		if setEndpoint {
 
 
 			var setUDPPort = false
 			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, ":")
 				endpointarr := strings.Split(endpointstring, ":")
 				if len(endpointarr) == 2 {
 				if len(endpointarr) == 2 {
 					port, err := strconv.Atoi(endpointarr[1])
 					port, err := strconv.Atoi(endpointarr[1])
 					if err == nil {
 					if err == nil {
 						setUDPPort = true
 						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
 			// 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
 			// or, if port is for some reason zero use the LocalListenPort
 			// but only do this if LocalListenPort is not zero
 			// 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)
 			address, err = net.ResolveUDPAddr("udp", endpoint)
 			if err != nil {
 			if err != nil {
 				return models.PeerUpdate{}, err
 				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
 		var keepalive time.Duration
 		if node.PersistentKeepalive != 0 {
 		if node.PersistentKeepalive != 0 {
+
 			// set_keepalive
 			// set_keepalive
-			keepalive, _ = time.ParseDuration(strconv.FormatInt(int64(node.PersistentKeepalive), 10) + "s")
+			keepalive = node.PersistentKeepalive
 		}
 		}
 		var peerData = wgtypes.PeerConfig{
 		var peerData = wgtypes.PeerConfig{
-			PublicKey:                   pubkey,
+			PublicKey:                   peerHost.PublicKey,
 			Endpoint:                    address,
 			Endpoint:                    address,
 			ReplaceAllowedIPs:           true,
 			ReplaceAllowedIPs:           true,
 			AllowedIPs:                  allowedips,
 			AllowedIPs:                  allowedips,
@@ -166,19 +441,16 @@ func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
 		}
 		}
 
 
 		peers = append(peers, peerData)
 		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(),
 			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 {
 		if err == nil {
 			peers = append(peers, extPeers...)
 			peers = append(peers, extPeers...)
 			for i := range idsAndAddr {
 			for i := range idsAndAddr {
@@ -201,10 +473,14 @@ func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
 	return peerUpdate, nil
 	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 peers []wgtypes.PeerConfig
 	var idsAndAddr []models.IDandAddr
 	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 {
 	if err != nil {
 		return peers, idsAndAddr, err
 		return peers, idsAndAddr, err
 	}
 	}
@@ -215,13 +491,13 @@ func getExtPeers(node *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, e
 			continue
 			continue
 		}
 		}
 
 
-		if node.PublicKey == extPeer.PublicKey {
+		if host.PublicKey.String() == extPeer.PublicKey {
 			continue
 			continue
 		}
 		}
 
 
 		var allowedips []net.IPNet
 		var allowedips []net.IPNet
 		var peer wgtypes.PeerConfig
 		var peer wgtypes.PeerConfig
-		if extPeer.Address != "" {
+		if forIngressNode && extPeer.Address != "" {
 			var peeraddr = net.IPNet{
 			var peeraddr = net.IPNet{
 				IP:   net.ParseIP(extPeer.Address),
 				IP:   net.ParseIP(extPeer.Address),
 				Mask: net.CIDRMask(32, 32),
 				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{
 			var addr6 = net.IPNet{
 				IP:   net.ParseIP(extPeer.Address6),
 				IP:   net.ParseIP(extPeer.Address6),
 				Mask: net.CIDRMask(128, 128),
 				Mask: net.CIDRMask(128, 128),
@@ -240,12 +516,31 @@ func getExtPeers(node *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, e
 				allowedips = append(allowedips, addr6)
 				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
 		primaryAddr := extPeer.Address
 		if primaryAddr == "" {
 		if primaryAddr == "" {
 			primaryAddr = extPeer.Address6
 			primaryAddr = extPeer.Address6
 		}
 		}
-
 		peer = wgtypes.PeerConfig{
 		peer = wgtypes.PeerConfig{
 			PublicKey:         pubkey,
 			PublicKey:         pubkey,
 			ReplaceAllowedIPs: true,
 			ReplaceAllowedIPs: true,
@@ -257,41 +552,113 @@ func getExtPeers(node *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, e
 			Address: primaryAddr,
 			Address: primaryAddr,
 		})
 		})
 	}
 	}
-	sort.SliceStable(peers[:], func(i, j int) bool {
-		return peers[i].PublicKey.String() < peers[j].PublicKey.String()
-	})
 	return peers, idsAndAddr, nil
 	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
 // 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
 	var allowedips []net.IPNet
 	allowedips = getNodeAllowedIPs(peer, node)
 	allowedips = getNodeAllowedIPs(peer, node)
 
 
 	// handle ingress gateway peers
 	// handle ingress gateway peers
-	if peer.IsIngressGateway == "yes" {
-		extPeers, _, err := getExtPeers(peer)
+	if peer.IsIngressGateway {
+		extPeers, _, err := getExtPeers(peer, false)
 		if err != nil {
 		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 {
 		for _, extPeer := range extPeers {
 			allowedips = append(allowedips, extPeer.AllowedIPs...)
 			allowedips = append(allowedips, extPeer.AllowedIPs...)
 		}
 		}
 		// if node is a failover node, add allowed ips from nodes it is handling
 		// 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
 			// 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 {
 			for k := range metrics.FailoverPeers {
 				// if FailoverNode is me for this node, add allowedips
 				// 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
 					// get original node so we can traverse the allowed ips
 					nodeToFailover, err := GetNodeByID(k)
 					nodeToFailover, err := GetNodeByID(k)
 					if err == nil {
 					if err == nil {
-						failoverNodeMetrics, err := GetMetrics(nodeToFailover.ID)
+						failoverNodeMetrics, err := GetMetrics(nodeToFailover.ID.String())
 						if err == nil && failoverNodeMetrics != nil {
 						if err == nil && failoverNodeMetrics != nil {
 							if len(failoverNodeMetrics.NodeName) > 0 {
 							if len(failoverNodeMetrics.NodeName) > 0 {
 								allowedips = append(allowedips, getNodeAllowedIPs(&nodeToFailover, peer)...)
 								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
 	// handle relay gateway peers
-	if peer.IsRelay == "yes" {
+	if fetchRelayedIps && peer.IsRelay {
 		for _, ip := range peer.RelayAddrs {
 		for _, ip := range peer.RelayAddrs {
 			//find node ID of relayed peer
 			//find node ID of relayed peer
 			relayedPeer, err := findNode(ip)
 			relayedPeer, err := findNode(ip)
@@ -316,7 +683,7 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet
 				continue
 				continue
 			}
 			}
 			//check if acl permits comms
 			//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
 				continue
 			}
 			}
 			if iplib.Version(net.ParseIP(ip)) == 4 {
 			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())
 				logger.Log(1, "unable to find node for relayed address", ip, err.Error())
 				continue
 				continue
 			}
 			}
-			if relayedNode.IsEgressGateway == "yes" {
+			if relayedNode.IsEgressGateway {
 				extAllowedIPs := getEgressIPs(node, relayedNode)
 				extAllowedIPs := getEgressIPs(node, relayedNode)
 				allowedips = append(allowedips, extAllowedIPs...)
 				allowedips = append(allowedips, extAllowedIPs...)
 			}
 			}
-			if relayedNode.IsIngressGateway == "yes" {
-				extPeers, _, err := getExtPeers(relayedNode)
+			if relayedNode.IsIngressGateway {
+				extPeers, _, err := getExtPeers(relayedNode, false)
 				if err == nil {
 				if err == nil {
 					for _, extPeer := range extPeers {
 					for _, extPeer := range extPeers {
 						allowedips = append(allowedips, extPeer.AllowedIPs...)
 						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 {
 func getPeerDNS(network string) string {
 	var dns string
 	var dns string
 	if nodes, err := GetNetworkNodes(network); err == nil {
 	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
 	//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)
 		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)
 		allowedips = append(allowedips, relayIP6)
 	}
 	}
 	//get PeerUpdate for relayed node
 	//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 {
 	if err != nil {
 		return models.PeerUpdate{}, err
 		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())
 			logger.Log(0, "failed to find node for ip", allowedips[i].IP.String())
 			continue
 			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:]...)
 			allowedips = append(allowedips[:i], allowedips[i+1:]...)
 		}
 		}
 	}
 	}
 	//delete self from allowed ips
 	//delete self from allowed ips
 	for i := len(allowedips) - 1; i >= 0; i-- {
 	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:]...)
 			allowedips = append(allowedips[:i], allowedips[i+1:]...)
 		}
 		}
 	}
 	}
 	//delete egressrange from allowedip if we are egress gateway
 	//delete egressrange from allowedip if we are egress gateway
-	if node.IsEgressGateway == "yes" {
+	if node.IsEgressGateway {
 		for i := len(allowedips) - 1; i >= 0; i-- {
 		for i := len(allowedips) - 1; i >= 0; i-- {
 			if StringSliceContains(node.EgressGatewayRanges, allowedips[i].String()) {
 			if StringSliceContains(node.EgressGatewayRanges, allowedips[i].String()) {
 				allowedips = append(allowedips[:i], allowedips[i+1:]...)
 				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
 	//delete extclients from allowedip if we are ingress gateway
-	if node.IsIngressGateway == "yes" {
+	if node.IsIngressGateway {
 		for i := len(allowedips) - 1; i >= 0; i-- {
 		for i := len(allowedips) - 1; i >= 0; i-- {
 			if strings.Contains(node.IngressGatewayRange, allowedips[i].IP.String()) {
 			if strings.Contains(node.IngressGatewayRange, allowedips[i].IP.String()) {
 				allowedips = append(allowedips[:i], allowedips[i+1:]...)
 				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
 	//add egress range if relay is egress
-	if relay.IsEgressGateway == "yes" {
+	if relay.IsEgressGateway {
 		var ip *net.IPNet
 		var ip *net.IPNet
 		for _, cidr := range relay.EgressGatewayRanges {
 		for _, cidr := range relay.EgressGatewayRanges {
 			_, ip, err = net.ParseCIDR(cidr)
 			_, ip, err = net.ParseCIDR(cidr)
@@ -460,31 +829,27 @@ func GetPeerUpdateForRelayedNode(node *models.Node, udppeers map[string]string)
 		}
 		}
 		allowedips = append(allowedips, *ip)
 		allowedips = append(allowedips, *ip)
 	}
 	}
-
-	pubkey, err := wgtypes.ParseKey(relay.PublicKey)
-	if err != nil {
-		return models.PeerUpdate{}, err
-	}
 	var setUDPPort = false
 	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, ":")
 		endpointarr := strings.Split(endpointstring, ":")
 		if len(endpointarr) == 2 {
 		if len(endpointarr) == 2 {
 			port, err := strconv.Atoi(endpointarr[1])
 			port, err := strconv.Atoi(endpointarr[1])
 			if err == nil {
 			if err == nil {
 				setUDPPort = true
 				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
 	// 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
 	// or, if port is for some reason zero use the LocalListenPort
 	// but only do this if LocalListenPort is not zero
 	// 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)
 	address, err := net.ResolveUDPAddr("udp", endpoint)
 	if err != nil {
 	if err != nil {
 		return models.PeerUpdate{}, err
 		return models.PeerUpdate{}, err
@@ -492,26 +857,23 @@ func GetPeerUpdateForRelayedNode(node *models.Node, udppeers map[string]string)
 	var keepalive time.Duration
 	var keepalive time.Duration
 	if node.PersistentKeepalive != 0 {
 	if node.PersistentKeepalive != 0 {
 		// set_keepalive
 		// set_keepalive
-		keepalive, _ = time.ParseDuration(strconv.FormatInt(int64(node.PersistentKeepalive), 10) + "s")
+		keepalive = node.PersistentKeepalive
 	}
 	}
 	var peerData = wgtypes.PeerConfig{
 	var peerData = wgtypes.PeerConfig{
-		PublicKey:                   pubkey,
+		PublicKey:                   relayHost.PublicKey,
 		Endpoint:                    address,
 		Endpoint:                    address,
 		ReplaceAllowedIPs:           true,
 		ReplaceAllowedIPs:           true,
 		AllowedIPs:                  allowedips,
 		AllowedIPs:                  allowedips,
 		PersistentKeepaliveInterval: &keepalive,
 		PersistentKeepaliveInterval: &keepalive,
 	}
 	}
 	peers = append(peers, peerData)
 	peers = append(peers, peerData)
-	if relay.IsServer == "yes" {
-		serverNodeAddresses = append(serverNodeAddresses, models.ServerAddr{IsLeader: IsLeader(relay), Address: relay.Address})
-	}
 	//if ingress add extclients
 	//if ingress add extclients
-	if node.IsIngressGateway == "yes" {
-		extPeers, _, err := getExtPeers(node)
+	if node.IsIngressGateway {
+		extPeers, _, err := getExtPeers(node, true)
 		if err == nil {
 		if err == nil {
 			peers = append(peers, extPeers...)
 			peers = append(peers, extPeers...)
 		} else {
 		} 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
 	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 {
 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
 	//check for internet gateway
 	internetGateway := false
 	internetGateway := false
 	if slices.Contains(peer.EgressGatewayRanges, "0.0.0.0/0") || slices.Contains(peer.EgressGatewayRanges, "::/0") {
 	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)
 			logger.Log(1, "could not parse gateway IP range. Not adding ", iprange)
 			continue // if can't parse CIDR
 			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
 		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
 			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
 		// 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
 			continue // skip adding egress range if overlaps with node's local ip
 		}
 		}
 		if err != nil {
 		if err != nil {
@@ -559,60 +930,24 @@ func getEgressIPs(node, peer *models.Node) []net.IPNet {
 
 
 func getNodeAllowedIPs(peer, node *models.Node) []net.IPNet {
 func getNodeAllowedIPs(peer, node *models.Node) []net.IPNet {
 	var allowedips = []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),
 			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),
 			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
 	// handle egress gateway peers
-	if peer.IsEgressGateway == "yes" {
+	if peer.IsEgressGateway {
 		//hasGateway = true
 		//hasGateway = true
 		egressIPs := getEgressIPs(node, peer)
 		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...)
 		allowedips = append(allowedips, egressIPs...)
 	}
 	}
 	return allowedips
 	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 {
 	for i := range nodes {
-		if nodes[i].ID == nodeID {
+		if nodes[i].ID.String() == nodeID {
 			for j := range netUser.Nodes {
 			for j := range netUser.Nodes {
 				if netUser.Nodes[j] == nodeID {
 				if netUser.Nodes[j] == nodeID {
 					return true
 					return true

+ 2 - 2
logic/pro/networkuser_test.go

@@ -18,8 +18,8 @@ func TestNetworkUserLogic(t *testing.T) {
 		NetID:        "skynet",
 		NetID:        "skynet",
 		AddressRange: "192.168.0.0/24",
 		AddressRange: "192.168.0.0/24",
 	}
 	}
-	nodes := []models.Node{
-		models.Node{ID: "coolnode"},
+	nodes := []models.LegacyNode{
+		models.LegacyNode{ID: "coolnode"},
 	}
 	}
 
 
 	clients := []models.ExtClient{
 	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
 // AdjustNodeAcls - adjusts ACLs based on a node's default value
 func AdjustNodeAcls(node *models.Node, networkNodes []models.Node) error {
 func AdjustNodeAcls(node *models.Node, networkNodes []models.Node) error {
 	networkID := nodeacls.NetworkID(node.Network)
 	networkID := nodeacls.NetworkID(node.Network)
-	nodeID := nodeacls.NodeID(node.ID)
+	nodeID := nodeacls.NodeID(node.ID.String())
 	currentACLs, err := nodeacls.FetchAllACLs(networkID)
 	currentACLs, err := nodeacls.FetchAllACLs(networkID)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	for i := range networkNodes {
 	for i := range networkNodes {
-		currentNodeID := nodeacls.NodeID(networkNodes[i].ID)
+		currentNodeID := nodeacls.NodeID(networkNodes[i].ID.String())
 		if currentNodeID == nodeID {
 		if currentNodeID == nodeID {
 			continue
 			continue
 		}
 		}

+ 35 - 18
logic/relay.go

@@ -19,14 +19,18 @@ func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error)
 	if err != nil {
 	if err != nil {
 		return returnnodes, models.Node{}, err
 		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")
 		return returnnodes, models.Node{}, fmt.Errorf("only linux machines can be relay nodes")
 	}
 	}
 	err = ValidateRelay(relay)
 	err = ValidateRelay(relay)
 	if err != nil {
 	if err != nil {
 		return returnnodes, models.Node{}, err
 		return returnnodes, models.Node{}, err
 	}
 	}
-	node.IsRelay = "yes"
+	node.IsRelay = true
 	node.RelayAddrs = relay.RelayAddrs
 	node.RelayAddrs = relay.RelayAddrs
 
 
 	node.SetLastModified()
 	node.SetLastModified()
@@ -34,7 +38,7 @@ func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error)
 	if err != nil {
 	if err != nil {
 		return returnnodes, node, err
 		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
 		return returnnodes, models.Node{}, err
 	}
 	}
 	returnnodes, err = SetRelayedNodes(true, node.Network, node.RelayAddrs)
 	returnnodes, err = SetRelayedNodes(true, node.Network, node.RelayAddrs)
@@ -52,21 +56,34 @@ func SetRelayedNodes(setRelayed bool, networkName string, addrs []string) ([]mod
 		return returnnodes, err
 		return returnnodes, err
 	}
 	}
 	for _, node := range networkNodes {
 	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
 		return returnnodes, node, err
 	}
 	}
 
 
-	node.IsRelay = "no"
+	node.IsRelay = false
 	node.RelayAddrs = []string{}
 	node.RelayAddrs = []string{}
 	node.SetLastModified()
 	node.SetLastModified()
 
 

+ 0 - 305
logic/server.go

@@ -1,21 +1,9 @@
 package logic
 package logic
 
 
 import (
 import (
-	"encoding/json"
-	"errors"
-	"fmt"
-	"net"
-	"os"
-	"runtime"
 	"strings"
 	"strings"
 
 
-	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
 	"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
 // 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
 // KUBERNETES_SERVER_MTU - ideal mtu for kubernetes deployments right now
 const KUBERNETES_SERVER_MTU = 1024
 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
 // EnterpriseCheck - Runs enterprise functions if presented
 func EnterpriseCheck() {
 func EnterpriseCheck() {
 	for _, check := range EnterpriseCheckFuncs {
 	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 ==
 // == Private ==
 
 
 func isDeleteError(err error) bool {
 func isDeleteError(err error) bool {
 	return err != nil && strings.Contains(err.Error(), models.NODE_DELETE)
 	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.Users = getDBLength(database.USERS_TABLE_NAME)
 	data.Networks = getDBLength(database.NETWORKS_TABLE_NAME)
 	data.Networks = getDBLength(database.NETWORKS_TABLE_NAME)
 	data.Version = servercfg.GetVersion()
 	data.Version = servercfg.GetVersion()
-	data.Servers = GetServerCount()
+	//data.Servers = GetServerCount()
 	nodes, err := GetAllNodes()
 	nodes, err := GetAllNodes()
 	if err == nil {
 	if err == nil {
 		data.Nodes = len(nodes)
 		data.Nodes = len(nodes)
@@ -114,7 +114,11 @@ func setTelemetryTimestamp(telRecord *models.Telemetry) error {
 func getClientCount(nodes []models.Node) clientCount {
 func getClientCount(nodes []models.Node) clientCount {
 	var count clientCount
 	var count clientCount
 	for _, node := range nodes {
 	for _, node := range nodes {
-		switch node.OS {
+		host, err := GetHost(node.HostID.String())
+		if err != nil {
+			continue
+		}
+		switch host.OS {
 		case "darwin":
 		case "darwin":
 			count.MacOS += 1
 			count.MacOS += 1
 		case "windows":
 		case "windows":
@@ -124,9 +128,6 @@ func getClientCount(nodes []models.Node) clientCount {
 		case "freebsd":
 		case "freebsd":
 			count.FreeBSD += 1
 			count.FreeBSD += 1
 		}
 		}
-		if !(node.IsServer == "yes") {
-			count.NonServer += 1
-		}
 	}
 	}
 	return count
 	return count
 }
 }

+ 6 - 26
logic/util.go

@@ -16,7 +16,6 @@ import (
 	"github.com/c-robinson/iplib"
 	"github.com/c-robinson/iplib"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/netclient/ncutils"
 )
 )
 
 
@@ -146,31 +145,6 @@ func StringSliceContains(slice []string, item string) bool {
 
 
 // == private ==
 // == 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
 // NormalCIDR - returns the first address of CIDR
 func NormalizeCIDR(address string) (string, error) {
 func NormalizeCIDR(address string) (string, error) {
 	ip, IPNet, err := net.ParseCIDR(address)
 	ip, IPNet, err := net.ParseCIDR(address)
@@ -226,3 +200,9 @@ func CheckIfFileExists(filePath string) bool {
 	}
 	}
 	return true
 	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
 package logic
 
 
 import (
 import (
-	"os"
-	"os/exec"
-	"strings"
-
-	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
 	"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
 // IfaceDelta - checks if the new node causes an interface change
 func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool {
 func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool {
 	// single comparison statements
 	// 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.IsEgressGateway != currentNode.IsEgressGateway ||
 		newNode.IsIngressGateway != currentNode.IsIngressGateway ||
 		newNode.IsIngressGateway != currentNode.IsIngressGateway ||
 		newNode.IsRelay != currentNode.IsRelay ||
 		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.PersistentKeepalive != currentNode.PersistentKeepalive ||
 		newNode.DNSOn != currentNode.DNSOn ||
 		newNode.DNSOn != currentNode.DNSOn ||
-		newNode.Connected != currentNode.Connected ||
-		len(newNode.AllowedIPs) != len(currentNode.AllowedIPs) {
+		newNode.Connected != currentNode.Connected {
 		return true
 		return true
 	}
 	}
-
 	// multi-comparison statements
 	// multi-comparison statements
-	if newNode.IsEgressGateway == "yes" {
+	if newNode.IsEgressGateway {
 		if len(currentNode.EgressGatewayRanges) != len(newNode.EgressGatewayRanges) {
 		if len(currentNode.EgressGatewayRanges) != len(newNode.EgressGatewayRanges) {
 			return true
 			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) {
 		if len(currentNode.RelayAddrs) != len(newNode.RelayAddrs) {
 			return true
 			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
 	return false
 }
 }
 
 
 // == Private Functions ==
 // == 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 (
 import (
 	"context"
 	"context"
+	"net"
 	"time"
 	"time"
 
 
+	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 )
 )
@@ -16,22 +18,26 @@ const (
 )
 )
 
 
 var (
 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
 // CheckZombies - checks if new node has same macaddress as existing node
 // if so, existing node is added to zombie node quarantine list
 // 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)
 	nodes, err := GetNetworkNodes(newnode.Network)
 	if err != nil {
 	if err != nil {
 		logger.Log(1, "Failed to retrieve network nodes", newnode.Network, err.Error())
 		logger.Log(1, "Failed to retrieve network nodes", newnode.Network, err.Error())
 		return
 		return
 	}
 	}
 	for _, node := range nodes {
 	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
 			newZombie <- node.ID
 		}
 		}
 	}
 	}
@@ -46,14 +52,14 @@ func ManageZombies(ctx context.Context) {
 		case <-ctx.Done():
 		case <-ctx.Done():
 			return
 			return
 		case id := <-newZombie:
 		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)
 			zombies = append(zombies, id)
 		case id := <-removeZombie:
 		case id := <-removeZombie:
 			found := false
 			found := false
 			if len(zombies) > 0 {
 			if len(zombies) > 0 {
 				for i := len(zombies) - 1; i >= 0; i-- {
 				for i := len(zombies) - 1; i >= 0; i-- {
 					if zombies[i] == id {
 					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:]...)
 						zombies = append(zombies[:i], zombies[i+1:]...)
 						found = true
 						found = true
 					}
 					}
@@ -66,19 +72,19 @@ func ManageZombies(ctx context.Context) {
 			logger.Log(3, "checking for zombie nodes")
 			logger.Log(3, "checking for zombie nodes")
 			if len(zombies) > 0 {
 			if len(zombies) > 0 {
 				for i := len(zombies) - 1; i >= 0; i-- {
 				for i := len(zombies) - 1; i >= 0; i-- {
-					node, err := GetNodeByID(zombies[i])
+					node, err := GetNodeByID(zombies[i].String())
 					if err != nil {
 					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:]...)
 						zombies = append(zombies[:i], zombies[i+1:]...)
 						continue
 						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
 							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:]...)
 						zombies = append(zombies[:i], zombies[i+1:]...)
 					}
 					}
 				}
 				}
@@ -104,13 +110,13 @@ func InitializeZombies() {
 			if node.ID == othernode.ID {
 			if node.ID == othernode.ID {
 				continue
 				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)
 					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 {
 				} else {
 					zombies = append(zombies, node.ID)
 					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/netclient/ncutils"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/serverctl"
 	"github.com/gravitl/netmaker/serverctl"
+	stunserver "github.com/gravitl/netmaker/stun-server"
 )
 )
 
 
 var version = "dev"
 var version = "dev"
@@ -70,9 +71,6 @@ func initialize() { // Client Mode Prereq Check
 		logger.FatalLog("Error connecting to database: ", err.Error())
 		logger.FatalLog("Error connecting to database: ", err.Error())
 	}
 	}
 	logger.Log(0, "database successfully connected")
 	logger.Log(0, "database successfully connected")
-	if err = logic.AddServerIDIfNotPresent(); err != nil {
-		logger.Log(1, "failed to save server ID")
-	}
 
 
 	logic.SetJWTSecret()
 	logic.SetJWTSecret()
 
 
@@ -111,9 +109,6 @@ func initialize() { // Client Mode Prereq Check
 		if uid != 0 {
 		if uid != 0 {
 			logger.FatalLog("To run in client mode requires root privileges. Either disable client mode or run with sudo.")
 			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
 	// initialize iptables to ensure gateways work correctly and mq is forwarded if containerized
 	if servercfg.ManageIPTables() != "off" {
 	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'.")
 		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()
 	waitnetwork.Wait()
 }
 }
 
 
@@ -184,6 +198,7 @@ func runMessageQueue(wg *sync.WaitGroup) {
 	ctx, cancel := context.WithCancel(context.Background())
 	ctx, cancel := context.WithCancel(context.Background())
 	go mq.Keepalive(ctx)
 	go mq.Keepalive(ctx)
 	go logic.ManageZombies(ctx)
 	go logic.ManageZombies(ctx)
+	go logic.PurgePendingNodes(ctx)
 	quit := make(chan os.Signal, 1)
 	quit := make(chan os.Signal, 1)
 	signal.Notify(quit, syscall.SIGTERM, os.Interrupt)
 	signal.Notify(quit, syscall.SIGTERM, os.Interrupt)
 	<-quit
 	<-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"`
 	LastModified           int64  `json:"lastmodified" bson:"lastmodified"`
 	Enabled                bool   `json:"enabled" bson:"enabled"`
 	Enabled                bool   `json:"enabled" bson:"enabled"`
 	OwnerID                string `json:"ownerid" bson:"ownerid"`
 	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
 package models
 
 
-import "time"
+import (
+	"time"
+
+	proxy_models "github.com/gravitl/netclient/nmproxy/models"
+)
 
 
 // Metrics - metrics struct
 // Metrics - metrics struct
 type 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
 // Metric - holds a metric for data between nodes

+ 11 - 7
models/mqtt.go

@@ -1,15 +1,19 @@
 package models
 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
 // PeerUpdate - struct
 type 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
 // KeyUpdate - key update struct

+ 252 - 138
models/node.go

@@ -7,7 +7,8 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
-	"golang.org/x/crypto/bcrypt"
+	"github.com/google/uuid"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 )
 )
 
 
 const (
 const (
@@ -42,19 +43,68 @@ var seededRand *rand.Rand = rand.New(
 // NodeCheckin - struct for node checkins with server
 // NodeCheckin - struct for node checkins with server
 type NodeCheckin struct {
 type NodeCheckin struct {
 	Version   string
 	Version   string
-	Connected string
+	Connected bool
 	Ifaces    []Iface
 	Ifaces    []Iface
 }
 }
 
 
 // Iface struct for local interfaces of a node
 // Iface struct for local interfaces of a node
 type Iface struct {
 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"`
 	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"`
 	Address                 string               `json:"address" bson:"address" yaml:"address" validate:"omitempty,ipv4"`
 	Address6                string               `json:"address6" bson:"address6" yaml:"address6" validate:"omitempty,ipv6"`
 	Address6                string               `json:"address6" bson:"address6" yaml:"address6" validate:"omitempty,ipv6"`
 	LocalAddress            string               `json:"localaddress" bson:"localaddress" yaml:"localaddress" validate:"omitempty"`
 	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:"-"`
 	NetworkSettings         Network              `json:"networksettings" bson:"networksettings" yaml:"networksettings" validate:"-"`
 	ListenPort              int32                `json:"listenport" bson:"listenport" yaml:"listenport" validate:"omitempty,numeric,min=1024,max=65535"`
 	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"`
 	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"`
 	PublicKey               string               `json:"publickey" bson:"publickey" yaml:"publickey" validate:"required,base64"`
 	Endpoint                string               `json:"endpoint" bson:"endpoint" yaml:"endpoint" validate:"required,ip"`
 	Endpoint                string               `json:"endpoint" bson:"endpoint" yaml:"endpoint" validate:"required,ip"`
 	PostUp                  string               `json:"postup" bson:"postup" yaml:"postup"`
 	PostUp                  string               `json:"postup" bson:"postup" yaml:"postup"`
@@ -110,6 +161,8 @@ type Node struct {
 	FirewallInUse   string      `json:"firewallinuse" bson:"firewallinuse" yaml:"firewallinuse"`
 	FirewallInUse   string      `json:"firewallinuse" bson:"firewallinuse" yaml:"firewallinuse"`
 	InternetGateway string      `json:"internetgateway" bson:"internetgateway" yaml:"internetgateway"`
 	InternetGateway string      `json:"internetgateway" bson:"internetgateway" yaml:"internetgateway"`
 	Connected       string      `json:"connected" bson:"connected" yaml:"connected" validate:"checkyesorno"`
 	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 ==
 	// == PRO ==
 	DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
 	DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
 	OwnerID    string `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"`
 	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
 // NodesArray.Len - gets length of node array
 func (a NodesArray) Len() int { return len(a) }
 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
 // NodesArray.Swap - swaps two nodes in array
 func (a NodesArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
 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
 // Node.PrimaryAddress - return ipv4 address if present, else return ipv6
 func (node *Node) PrimaryAddress() string {
 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
 // Node.SetDefaultConnected
-func (node *Node) SetDefaultConnected() {
+func (node *LegacyNode) SetDefaultConnected() {
 	if node.Connected == "" {
 	if node.Connected == "" {
 		node.Connected = "yes"
 		node.Connected = "yes"
 	}
 	}
@@ -153,84 +208,77 @@ func (node *Node) SetDefaultConnected() {
 }
 }
 
 
 // Node.SetDefaultACL
 // Node.SetDefaultACL
-func (node *Node) SetDefaultACL() {
+func (node *LegacyNode) SetDefaultACL() {
 	if node.DefaultACL == "" {
 	if node.DefaultACL == "" {
 		node.DefaultACL = "yes"
 		node.DefaultACL = "yes"
 	}
 	}
 }
 }
 
 
 // Node.SetDefaultMTU - sets default MTU of a node
 // Node.SetDefaultMTU - sets default MTU of a node
-func (node *Node) SetDefaultMTU() {
+func (node *LegacyNode) SetDefaultMTU() {
 	if node.MTU == 0 {
 	if node.MTU == 0 {
 		node.MTU = 1280
 		node.MTU = 1280
 	}
 	}
 }
 }
 
 
 // Node.SetDefaultNFTablesPresent - sets default for nftables check
 // Node.SetDefaultNFTablesPresent - sets default for nftables check
-func (node *Node) SetDefaultNFTablesPresent() {
+func (node *LegacyNode) SetDefaultNFTablesPresent() {
 	if node.FirewallInUse == "" {
 	if node.FirewallInUse == "" {
 		node.FirewallInUse = FIREWALL_IPTABLES // default to iptables
 		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
 // Node.SetDefaultIsRelayed - set default is relayed
-func (node *Node) SetDefaultIsRelayed() {
+func (node *LegacyNode) SetDefaultIsRelayed() {
 	if node.IsRelayed == "" {
 	if node.IsRelayed == "" {
 		node.IsRelayed = "no"
 		node.IsRelayed = "no"
 	}
 	}
 }
 }
 
 
 // Node.SetDefaultIsRelayed - set default is relayed
 // Node.SetDefaultIsRelayed - set default is relayed
-func (node *Node) SetDefaultIsHub() {
+func (node *LegacyNode) SetDefaultIsHub() {
 	if node.IsHub == "" {
 	if node.IsHub == "" {
 		node.IsHub = "no"
 		node.IsHub = "no"
 	}
 	}
 }
 }
 
 
 // Node.SetDefaultIsRelay - set default isrelay
 // Node.SetDefaultIsRelay - set default isrelay
-func (node *Node) SetDefaultIsRelay() {
+func (node *LegacyNode) SetDefaultIsRelay() {
 	if node.IsRelay == "" {
 	if node.IsRelay == "" {
 		node.IsRelay = "no"
 		node.IsRelay = "no"
 	}
 	}
 }
 }
 
 
 // Node.SetDefaultIsDocker - set default isdocker
 // Node.SetDefaultIsDocker - set default isdocker
-func (node *Node) SetDefaultIsDocker() {
+func (node *LegacyNode) SetDefaultIsDocker() {
 	if node.IsDocker == "" {
 	if node.IsDocker == "" {
 		node.IsDocker = "no"
 		node.IsDocker = "no"
 	}
 	}
 }
 }
 
 
 // Node.SetDefaultIsK8S - set default isk8s
 // Node.SetDefaultIsK8S - set default isk8s
-func (node *Node) SetDefaultIsK8S() {
+func (node *LegacyNode) SetDefaultIsK8S() {
 	if node.IsK8S == "" {
 	if node.IsK8S == "" {
 		node.IsK8S = "no"
 		node.IsK8S = "no"
 	}
 	}
 }
 }
 
 
 // Node.SetDefaultEgressGateway - sets default egress gateway status
 // Node.SetDefaultEgressGateway - sets default egress gateway status
-func (node *Node) SetDefaultEgressGateway() {
+func (node *LegacyNode) SetDefaultEgressGateway() {
 	if node.IsEgressGateway == "" {
 	if node.IsEgressGateway == "" {
 		node.IsEgressGateway = "no"
 		node.IsEgressGateway = "no"
 	}
 	}
 }
 }
 
 
 // Node.SetDefaultIngressGateway - sets default ingress gateway status
 // Node.SetDefaultIngressGateway - sets default ingress gateway status
-func (node *Node) SetDefaultIngressGateway() {
+func (node *LegacyNode) SetDefaultIngressGateway() {
 	if node.IsIngressGateway == "" {
 	if node.IsIngressGateway == "" {
 		node.IsIngressGateway = "no"
 		node.IsIngressGateway = "no"
 	}
 	}
 }
 }
 
 
 // Node.SetDefaultAction - sets default action status
 // Node.SetDefaultAction - sets default action status
-func (node *Node) SetDefaultAction() {
+func (node *LegacyNode) SetDefaultAction() {
 	if node.Action == "" {
 	if node.Action == "" {
 		node.Action = NODE_NOOP
 		node.Action = NODE_NOOP
 	}
 	}
@@ -244,35 +292,35 @@ func (node *Node) SetDefaultAction() {
 //}
 //}
 
 
 // Node.SetIPForwardingDefault - set ip forwarding default
 // Node.SetIPForwardingDefault - set ip forwarding default
-func (node *Node) SetIPForwardingDefault() {
+func (node *LegacyNode) SetIPForwardingDefault() {
 	if node.IPForwarding == "" {
 	if node.IPForwarding == "" {
 		node.IPForwarding = "yes"
 		node.IPForwarding = "yes"
 	}
 	}
 }
 }
 
 
 // Node.SetIsLocalDefault - set is local default
 // Node.SetIsLocalDefault - set is local default
-func (node *Node) SetIsLocalDefault() {
+func (node *LegacyNode) SetIsLocalDefault() {
 	if node.IsLocal == "" {
 	if node.IsLocal == "" {
 		node.IsLocal = "no"
 		node.IsLocal = "no"
 	}
 	}
 }
 }
 
 
 // Node.SetDNSOnDefault - sets dns on default
 // Node.SetDNSOnDefault - sets dns on default
-func (node *Node) SetDNSOnDefault() {
+func (node *LegacyNode) SetDNSOnDefault() {
 	if node.DNSOn == "" {
 	if node.DNSOn == "" {
 		node.DNSOn = "yes"
 		node.DNSOn = "yes"
 	}
 	}
 }
 }
 
 
 // Node.SetIsServerDefault - sets node isserver default
 // Node.SetIsServerDefault - sets node isserver default
-func (node *Node) SetIsServerDefault() {
+func (node *LegacyNode) SetIsServerDefault() {
 	if node.IsServer != "yes" {
 	if node.IsServer != "yes" {
 		node.IsServer = "no"
 		node.IsServer = "no"
 	}
 	}
 }
 }
 
 
 // Node.SetIsStaticDefault - set is static default
 // Node.SetIsStaticDefault - set is static default
-func (node *Node) SetIsStaticDefault() {
+func (node *LegacyNode) SetIsStaticDefault() {
 	if node.IsServer == "yes" {
 	if node.IsServer == "yes" {
 		node.IsStatic = "yes"
 		node.IsStatic = "yes"
 	} else if node.IsStatic != "yes" {
 	} else if node.IsStatic != "yes" {
@@ -282,33 +330,33 @@ func (node *Node) SetIsStaticDefault() {
 
 
 // Node.SetLastModified - set last modified initial time
 // Node.SetLastModified - set last modified initial time
 func (node *Node) SetLastModified() {
 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() {
 func (node *Node) SetLastCheckIn() {
-	node.LastCheckIn = time.Now().Unix()
+	node.LastCheckIn = time.Now()
 }
 }
 
 
 // Node.SetLastPeerUpdate - sets last peer update time
 // Node.SetLastPeerUpdate - sets last peer update time
 func (node *Node) SetLastPeerUpdate() {
 func (node *Node) SetLastPeerUpdate() {
-	node.LastPeerUpdate = time.Now().Unix()
+	node.LastPeerUpdate = time.Now()
 }
 }
 
 
 // Node.SetExpirationDateTime - sets node expiry time
 // Node.SetExpirationDateTime - sets node expiry time
 func (node *Node) SetExpirationDateTime() {
 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
 // Node.SetDefaultName - sets a random name to node
-func (node *Node) SetDefaultName() {
+func (node *LegacyNode) SetDefaultName() {
 	if node.Name == "" {
 	if node.Name == "" {
 		node.Name = GenerateNodeName()
 		node.Name = GenerateNodeName()
 	}
 	}
 }
 }
 
 
 // Node.SetDefaultFailover - sets default value of failover status to no if not set
 // Node.SetDefaultFailover - sets default value of failover status to no if not set
-func (node *Node) SetDefaultFailover() {
+func (node *LegacyNode) SetDefaultFailover() {
 	if node.Failover == "" {
 	if node.Failover == "" {
 		node.Failover = "no"
 		node.Failover = "no"
 	}
 	}
@@ -318,84 +366,43 @@ func (node *Node) SetDefaultFailover() {
 func (newNode *Node) Fill(currentNode *Node) { // TODO add new field for nftables present
 func (newNode *Node) Fill(currentNode *Node) { // TODO add new field for nftables present
 	newNode.ID = currentNode.ID
 	newNode.ID = currentNode.ID
 
 
-	if newNode.Address == "" {
+	// Revisit the logic for boolean values
+	// TODO ---- !!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	// TODO ---- !!!!!!!!!!!!!!!!!!!!!!!!!!
+	if newNode.Address.String() == "" {
 		newNode.Address = currentNode.Address
 		newNode.Address = currentNode.Address
 	}
 	}
-	if newNode.Address6 == "" {
+	if newNode.Address6.String() == "" {
 		newNode.Address6 = currentNode.Address6
 		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 == "" {
 	if newNode.PostUp == "" {
 		newNode.PostUp = currentNode.PostUp
 		newNode.PostUp = currentNode.PostUp
 	}
 	}
 	if newNode.PostDown == "" {
 	if newNode.PostDown == "" {
 		newNode.PostDown = currentNode.PostDown
 		newNode.PostDown = currentNode.PostDown
 	}
 	}
-	if newNode.AllowedIPs == nil {
-		newNode.AllowedIPs = currentNode.AllowedIPs
-	}
 	if newNode.PersistentKeepalive < 0 {
 	if newNode.PersistentKeepalive < 0 {
 		newNode.PersistentKeepalive = currentNode.PersistentKeepalive
 		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
 		newNode.LastModified = currentNode.LastModified
 	}
 	}
-	if newNode.ExpirationDateTime == 0 {
+	if newNode.ExpirationDateTime.IsZero() {
 		newNode.ExpirationDateTime = currentNode.ExpirationDateTime
 		newNode.ExpirationDateTime = currentNode.ExpirationDateTime
 	}
 	}
-	if newNode.LastPeerUpdate == 0 {
+	if newNode.LastPeerUpdate.IsZero() {
 		newNode.LastPeerUpdate = currentNode.LastPeerUpdate
 		newNode.LastPeerUpdate = currentNode.LastPeerUpdate
 	}
 	}
-	if newNode.LastCheckIn == 0 {
+	if newNode.LastCheckIn.IsZero() {
 		newNode.LastCheckIn = currentNode.LastCheckIn
 		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 == "" {
 	if newNode.Network == "" {
 		newNode.Network = currentNode.Network
 		newNode.Network = currentNode.Network
 	}
 	}
-	if newNode.IsPending == "" {
-		newNode.IsPending = currentNode.IsPending
-	}
-	if newNode.IsEgressGateway == "" {
+	if newNode.IsEgressGateway != currentNode.IsEgressGateway {
 		newNode.IsEgressGateway = currentNode.IsEgressGateway
 		newNode.IsEgressGateway = currentNode.IsEgressGateway
 	}
 	}
-	if newNode.IsIngressGateway == "" {
+	if newNode.IsIngressGateway != currentNode.IsIngressGateway {
 		newNode.IsIngressGateway = currentNode.IsIngressGateway
 		newNode.IsIngressGateway = currentNode.IsIngressGateway
 	}
 	}
 	if newNode.EgressGatewayRanges == nil {
 	if newNode.EgressGatewayRanges == nil {
@@ -407,73 +414,36 @@ func (newNode *Node) Fill(currentNode *Node) { // TODO add new field for nftable
 	if newNode.IngressGatewayRange6 == "" {
 	if newNode.IngressGatewayRange6 == "" {
 		newNode.IngressGatewayRange6 = currentNode.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
 		newNode.DNSOn = currentNode.DNSOn
 	}
 	}
-	if newNode.IsLocal == "" {
+	if newNode.IsLocal != currentNode.IsLocal {
 		newNode.IsLocal = currentNode.IsLocal
 		newNode.IsLocal = currentNode.IsLocal
 	}
 	}
-	if newNode.IPForwarding == "" {
-		newNode.IPForwarding = currentNode.IPForwarding
-	}
 	if newNode.Action == "" {
 	if newNode.Action == "" {
 		newNode.Action = currentNode.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 {
 	if newNode.RelayAddrs == nil {
 		newNode.RelayAddrs = currentNode.RelayAddrs
 		newNode.RelayAddrs = currentNode.RelayAddrs
 	}
 	}
-	if newNode.IsRelay == "" {
+	if newNode.IsRelay != currentNode.IsRelay {
 		newNode.IsRelay = currentNode.IsRelay
 		newNode.IsRelay = currentNode.IsRelay
 	}
 	}
-	if newNode.IsRelayed == "" {
+	if newNode.IsRelayed == currentNode.IsRelayed {
 		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 == "" {
 	if newNode.Server == "" {
 		newNode.Server = currentNode.Server
 		newNode.Server = currentNode.Server
 	}
 	}
-	if newNode.Connected == "" {
+	if newNode.Connected != currentNode.Connected {
 		newNode.Connected = currentNode.Connected
 		newNode.Connected = currentNode.Connected
 	}
 	}
 	if newNode.DefaultACL == "" {
 	if newNode.DefaultACL == "" {
 		newNode.DefaultACL = currentNode.DefaultACL
 		newNode.DefaultACL = currentNode.DefaultACL
 	}
 	}
-
-	if newNode.Failover == "" {
+	if newNode.Failover != currentNode.Failover {
 		newNode.Failover = currentNode.Failover
 		newNode.Failover = currentNode.Failover
 	}
 	}
-
-	newNode.TrafficKeys = currentNode.TrafficKeys
 }
 }
 
 
 // StringWithCharset - returns random string inside defined charset
 // 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
 // Node.NameInNodeCharset - returns if name is in charset below or not
-func (node *Node) NameInNodeCharSet() bool {
+func (node *LegacyNode) NameInNodeCharSet() bool {
 
 
 	charset := "abcdefghijklmnopqrstuvwxyz1234567890-"
 	charset := "abcdefghijklmnopqrstuvwxyz1234567890-"
 
 
@@ -516,3 +486,147 @@ func (node *Node) DoesACLAllow() bool {
 func (node *Node) DoesACLDeny() bool {
 func (node *Node) DoesACLDeny() bool {
 	return node.DefaultACL == "no"
 	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
 // ServerUpdateData - contains data to configure server
 // and if it should set peers
 // and if it should set peers
 type ServerUpdateData struct {
 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
 // 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
 // NodeGet - struct for a single node get response
 type NodeGet struct {
 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"`
 	Peers        []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"`
 	ServerConfig ServerConfig         `json:"serverconfig" bson:"serverconfig" yaml:"serverconfig"`
 	ServerConfig ServerConfig         `json:"serverconfig" bson:"serverconfig" yaml:"serverconfig"`
 	PeerIDs      PeerMap              `json:"peerids,omitempty" bson:"peerids,omitempty" yaml:"peerids,omitempty"`
 	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
 // ServerConfig - struct for dealing with the server information for a netclient
 type ServerConfig struct {
 type ServerConfig struct {
 	CoreDNSAddr string `yaml:"corednsaddr"`
 	CoreDNSAddr string `yaml:"corednsaddr"`
@@ -218,7 +227,11 @@ type ServerConfig struct {
 	Version     string `yaml:"version"`
 	Version     string `yaml:"version"`
 	MQPort      string `yaml:"mqport"`
 	MQPort      string `yaml:"mqport"`
 	Server      string `yaml:"server"`
 	Server      string `yaml:"server"`
+	Broker      string `yaml:"broker"`
 	Is_EE       bool   `yaml:"isee"`
 	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
 // User.NameInCharset - returns if name is in charset below or not
@@ -236,3 +249,10 @@ func (user *User) NameInCharSet() bool {
 type ServerIDs struct {
 type ServerIDs struct {
 	ServerIDs []string `json:"server_ids"`
 	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)
 	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)
 	d, err := json.Marshal(payload)
 	if err != nil {
 	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"
 	exporterRole = "exporter"
 	// constant for node role
 	// constant for node role
 	NodeRole = "node"
 	NodeRole = "node"
+	// HostRole constant for host role
+	HostRole = "host"
 
 
 	// const for dynamic security file
 	// const for dynamic security file
 	dynamicSecurityFile = "dynamic-security.json"
 	dynamicSecurityFile = "dynamic-security.json"
@@ -64,7 +66,7 @@ var (
 				Acls:     fetchServerAcls(),
 				Acls:     fetchServerAcls(),
 			},
 			},
 			{
 			{
-				Rolename: NodeRole,
+				Rolename: HostRole,
 				Acls:     fetchNodeAcls(),
 				Acls:     fetchNodeAcls(),
 			},
 			},
 			exporterMQRole,
 			exporterMQRole,
@@ -182,6 +184,12 @@ func FetchNetworkAcls(network string) []Acl {
 			Priority: -1,
 			Priority: -1,
 			Allow:    true,
 			Allow:    true,
 		},
 		},
+		{
+			AclType:  "publishClientReceive",
+			Topic:    fmt.Sprintf("proxy/%s/#", network),
+			Priority: -1,
+			Allow:    true,
+		},
 		{
 		{
 			AclType:  "subscribePattern",
 			AclType:  "subscribePattern",
 			Topic:    "#",
 			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
 // serverAcls - fetches server role related acls
 func fetchServerAcls() []Acl {
 func fetchServerAcls() []Acl {
 	return []Acl{
 	return []Acl{
@@ -206,6 +246,12 @@ func fetchServerAcls() []Acl {
 			Priority: -1,
 			Priority: -1,
 			Allow:    true,
 			Allow:    true,
 		},
 		},
+		{
+			AclType:  "publishClientSend",
+			Topic:    "proxy/#",
+			Priority: -1,
+			Allow:    true,
+		},
 		{
 		{
 			AclType:  "publishClientSend",
 			AclType:  "publishClientSend",
 			Topic:    "update/#",
 			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())
 		decrypted, decryptErr := decryptMsg(&node, msg.Payload())
 		if decryptErr != nil {
 		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
 			return
 		}
 		}
 		var checkin models.NodeCheckin
 		var checkin models.NodeCheckin
@@ -49,16 +49,24 @@ func Ping(client mqtt.Client, msg mqtt.Message) {
 			logger.Log(1, "error unmarshaling payload ", err.Error())
 			logger.Log(1, "error unmarshaling payload ", err.Error())
 			return
 			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.SetLastCheckIn()
-		node.Version = checkin.Version
+		host.Version = checkin.Version
 		node.Connected = checkin.Connected
 		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 {
 		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
 			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.
 		// --TODO --set client version once feature is implemented.
 		//node.SetClientVersion(msg.Payload())
 		//node.SetClientVersion(msg.Payload())
 	}()
 	}()
@@ -89,8 +97,8 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
 		}
 		}
 		ifaceDelta := logic.IfaceDelta(&currentNode, &newNode)
 		ifaceDelta := logic.IfaceDelta(&currentNode, &newNode)
 		if servercfg.Is_EE && ifaceDelta {
 		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()
 		newNode.SetLastCheckIn()
@@ -99,11 +107,13 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
 			return
 			return
 		}
 		}
 		if ifaceDelta { // reduce number of unneeded updates, by only sending on iface changes
 		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)
 			shouldUpdate := updateNodeMetrics(&currentNode, &newMetrics)
 
 
 			if err = logic.UpdateMetrics(id, &newMetrics); err != nil {
 			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
 				return
 			}
 			}
 			if servercfg.IsMetricsExporter() {
 			if servercfg.IsMetricsExporter() {
 				if err := pushMetricsToExporter(newMetrics); err != nil {
 				if err := pushMetricsToExporter(newMetrics); err != nil {
 					logger.Log(2, fmt.Sprintf("failed to push node: [%s] metrics to exporter, err: %v",
 					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 {
 			if newMetrics.Connectivity != nil {
 				err := logic.EnterpriseFailoverFunc(&currentNode)
 				err := logic.EnterpriseFailoverFunc(&currentNode)
 				if err != nil {
 				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 {
 			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 {
 				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] {
 		switch decrypted[0] {
 		case ncutils.ACK:
 		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:
 		case ncutils.DONE:
 			updateNodePeers(&currentNode)
 			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) {
 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
 		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 {
 func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) bool {
 	if newMetrics.FailoverPeers == nil {
 	if newMetrics.FailoverPeers == nil {
 		newMetrics.FailoverPeers = make(map[string]string)
 		newMetrics.FailoverPeers = make(map[string]string)
 	}
 	}
-	oldMetrics, err := logic.GetMetrics(currentNode.ID)
+	oldMetrics, err := logic.GetMetrics(currentNode.ID.String())
 	if err != nil {
 	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
 		return false
 	}
 	}
 	if oldMetrics.FailoverPeers == nil {
 	if oldMetrics.FailoverPeers == nil {
@@ -233,8 +225,8 @@ func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) boo
 	}
 	}
 
 
 	var attachedClients []models.ExtClient
 	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 {
 		if err == nil {
 			attachedClients = clients
 			attachedClients = clients
 		}
 		}
@@ -272,6 +264,13 @@ func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) boo
 		currMetric.ActualUptime = time.Duration(totalUpMinutes) * time.Minute
 		currMetric.ActualUptime = time.Duration(totalUpMinutes) * time.Minute
 		delete(oldMetrics.Connectivity, k) // remove from old data
 		delete(oldMetrics.Connectivity, k) // remove from old data
 		newMetrics.Connectivity[k] = currMetric
 		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
 	// add nodes that need failover
@@ -281,12 +280,12 @@ func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) boo
 		return false
 		return false
 	}
 	}
 	for _, node := range nodes {
 	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 &&
 			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
 	shouldUpdate := len(oldMetrics.FailoverPeers) == 0 && len(newMetrics.FailoverPeers) > 0

+ 135 - 101
mq/publishers.go

@@ -6,47 +6,68 @@ import (
 	"fmt"
 	"fmt"
 	"time"
 	"time"
 
 
+	proxy_models "github.com/gravitl/netclient/nmproxy/models"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/logic/metrics"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/serverctl"
 	"github.com/gravitl/netmaker/serverctl"
 )
 )
 
 
 // PublishPeerUpdate --- deterines and publishes a peer update to all the peers of a node
 // 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() {
 	if !servercfg.IsMessageQueueBackend() {
 		return nil
 		return nil
 	}
 	}
-	networkNodes, err := logic.GetNetworkNodes(newNode.Network)
+	networkNodes, err := logic.GetNetworkNodes(network)
 	if err != nil {
 	if err != nil {
 		logger.Log(1, "err getting Network Nodes", err.Error())
 		logger.Log(1, "err getting Network Nodes", err.Error())
 		return err
 		return err
 	}
 	}
 	for _, node := range networkNodes {
 	for _, node := range networkNodes {
-
-		if node.IsServer == "yes" {
-			continue
-		}
-		if !publishToSelf && newNode.ID == node.ID {
-			//skip self
-			continue
-		}
 		err = PublishSinglePeerUpdate(&node)
 		err = PublishSinglePeerUpdate(&node)
 		if err != nil {
 		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
 	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
 // PublishSinglePeerUpdate --- determines and publishes a peer update to one node
 func PublishSinglePeerUpdate(node *models.Node) error {
 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 {
 	if err != nil {
 		return err
 		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)
 	data, err := json.Marshal(&peerUpdate)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -56,19 +77,14 @@ func PublishSinglePeerUpdate(node *models.Node) error {
 
 
 // PublishPeerUpdate --- publishes a peer update to all the peers of a node
 // PublishPeerUpdate --- publishes a peer update to all the peers of a node
 func PublishExtPeerUpdate(node *models.Node) error {
 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() {
 	if !servercfg.IsMessageQueueBackend() {
 		return nil
 		return nil
 	}
 	}
-	peerUpdate, err := logic.GetPeerUpdate(node)
+	peerUpdate, err := logic.GetPeerUpdate(node, host)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -76,23 +92,34 @@ func PublishExtPeerUpdate(node *models.Node) error {
 	if err != nil {
 	if err != nil {
 		return err
 		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 {
 	if err = publish(node, fmt.Sprintf("peers/%s/%s", node.Network, node.ID), data); err != nil {
 		return err
 		return err
 	}
 	}
-	go PublishPeerUpdate(node, false)
+	go PublishPeerUpdate(node.Network, false)
 	return nil
 	return nil
 }
 }
 
 
 // NodeUpdate -- publishes a node update
 // NodeUpdate -- publishes a node update
 func NodeUpdate(node *models.Node) error {
 func NodeUpdate(node *models.Node) error {
-	if !servercfg.IsMessageQueueBackend() || node.IsServer == "yes" {
+	host, err := logic.GetHost(node.HostID.String())
+	if err != nil {
 		return 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)
 	data, err := json.Marshal(node)
 	if err != nil {
 	if err != nil {
@@ -100,7 +127,37 @@ func NodeUpdate(node *models.Node) error {
 		return err
 		return err
 	}
 	}
 	if err = publish(node, fmt.Sprintf("update/%s/%s", node.Network, node.ID), data); err != nil {
 	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 err
 	}
 	}
 	return nil
 	return nil
@@ -131,39 +188,16 @@ func sendPeers() {
 			logger.Log(3, "error occurred on timer,", err.Error())
 			logger.Log(3, "error occurred on timer,", err.Error())
 		}
 		}
 
 
-		collectServerMetrics(networks[:])
+		//collectServerMetrics(networks[:])
 	}
 	}
 
 
 	for _, network := range 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 {
 	for i := range nodes {
 		nodes[i].Action = models.NODE_FORCE_UPDATE
 		nodes[i].Action = models.NODE_FORCE_UPDATE
 		if err = NodeUpdate(&nodes[i]); err != nil {
 		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
 	return nil
 }
 }
 
 
 // function to collect and store metrics for server nodes
 // 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 {
 func pushMetricsToExporter(metrics models.Metrics) error {
 	logger.Log(2, "----> Pushing metrics to exporter")
 	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
 	if len(msg) <= 24 { // make sure message is of appropriate length
 		return nil, fmt.Errorf("recieved invalid message from broker %v", msg)
 		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
 	trafficKey, trafficErr := logic.RetrievePrivateTrafficKey() // get server private key
 	if trafficErr != nil {
 	if trafficErr != nil {
@@ -24,12 +28,12 @@ func decryptMsg(node *models.Node, msg []byte) ([]byte, error) {
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	nodePubTKey, err := ncutils.ConvertBytesToKey(node.TrafficKeys.Mine)
+	nodePubTKey, err := ncutils.ConvertBytesToKey(host.TrafficKeyPublic)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		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)
 		return ncutils.BoxDecrypt(msg, nodePubTKey, serverPrivTKey)
 	}
 	}
 
 
@@ -48,12 +52,16 @@ func encryptMsg(node *models.Node, msg []byte) ([]byte, error) {
 		return nil, err
 		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 {
 	if err != nil {
 		return nil, err
 		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)
 		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