Browse Source

Merge pull request #682 from gravitl/bugfixes_v0.10.0_various2

Bugfixes v0.10.0 various2
Alex Feiszli 3 years ago
parent
commit
03693bd9d2
100 changed files with 2043 additions and 1871 deletions
  1. 11 0
      .github/workflows/buildandrelease.yml
  2. 2 2
      .github/workflows/publish-docker.yml
  3. 79 0
      .github/workflows/publish-netclient-docker.yml
  4. 17 0
      .github/workflows/test.yml
  5. 1 0
      .gitignore
  6. 71 0
      CONTRIBUTING.md
  7. 3 3
      README.md
  8. 23 6
      compose/docker-compose.contained.yml
  9. 12 11
      compose/docker-compose.hostnetwork.yml
  10. 64 0
      compose/docker-compose.nocaddy.yml
  11. 3 0
      compose/docker-compose.reference.yml
  12. 19 8
      compose/docker-compose.yml
  13. 7 0
      config/config.go
  14. 2 1
      config/environments/dev.yaml
  15. 14 16
      controllers/auth_grpc.go
  16. 2 1
      controllers/config/dnsconfig/netmaker.hosts
  17. 2 2
      controllers/controller.go
  18. 8 8
      controllers/ext_client.go
  19. 0 23
      controllers/logger.go
  20. 44 7
      controllers/network.go
  21. 93 43
      controllers/node.go
  22. 136 58
      controllers/node_grpc.go
  23. 23 27
      controllers/node_test.go
  24. 14 4
      controllers/relay.go
  25. 2 4
      controllers/server.go
  26. 34 0
      controllers/server_util.go
  27. 54 3
      database/database.go
  28. 1 1
      database/sqlite.go
  29. 0 14
      defaultvalues.sh
  30. 39 0
      docker/Dockerfile-netclient-multiarch
  31. 5 0
      docker/mosquitto.conf
  32. BIN
      docs/_build/doctrees/api.doctree
  33. BIN
      docs/_build/doctrees/architecture.doctree
  34. BIN
      docs/_build/doctrees/client-installation.doctree
  35. BIN
      docs/_build/doctrees/environment.pickle
  36. BIN
      docs/_build/doctrees/index.doctree
  37. BIN
      docs/_build/doctrees/quick-start-nginx.doctree
  38. BIN
      docs/_build/doctrees/quick-start.doctree
  39. BIN
      docs/_build/doctrees/server-installation.doctree
  40. BIN
      docs/_build/doctrees/support.doctree
  41. BIN
      docs/_build/doctrees/troubleshoot.doctree
  42. BIN
      docs/_build/html/_images/create-user.png
  43. BIN
      docs/_build/html/_images/default-net.png
  44. BIN
      docs/_build/html/_images/egress4.png
  45. BIN
      docs/_build/html/_images/install-server.gif
  46. BIN
      docs/_build/html/_images/netcreate.png
  47. BIN
      docs/_build/html/_images/nm-diagram.jpg
  48. BIN
      docs/_build/html/_images/visit-website.gif
  49. 59 47
      docs/_build/html/_sources/api.rst.txt
  50. 12 2
      docs/_build/html/_sources/architecture.rst.txt
  51. 42 0
      docs/_build/html/_sources/client-installation.rst.txt
  52. 3 3
      docs/_build/html/_sources/index.rst.txt
  53. 0 170
      docs/_build/html/_sources/quick-start-nginx.rst.txt
  54. 1 1
      docs/_build/html/_sources/quick-start.rst.txt
  55. 58 8
      docs/_build/html/_sources/server-installation.rst.txt
  56. 27 0
      docs/_build/html/_sources/support.rst.txt
  57. 24 0
      docs/_build/html/_sources/troubleshoot.rst.txt
  58. 1 1
      docs/_build/html/about.html
  59. 75 35
      docs/_build/html/api.html
  60. 14 3
      docs/_build/html/architecture.html
  61. 72 3
      docs/_build/html/client-installation.html
  62. 15 8
      docs/_build/html/conduct.html
  63. 1 1
      docs/_build/html/egress-gateway.html
  64. 1 1
      docs/_build/html/external-clients.html
  65. 1 1
      docs/_build/html/genindex.html
  66. 18 11
      docs/_build/html/getting-started.html
  67. 6 4
      docs/_build/html/index.html
  68. 1 1
      docs/_build/html/install.html
  69. 15 8
      docs/_build/html/license.html
  70. 1 1
      docs/_build/html/oauth.html
  71. 0 1112
      docs/_build/html/quick-start-nginx.html
  72. 16 9
      docs/_build/html/quick-start.html
  73. 1 1
      docs/_build/html/relay-server.html
  74. 1 1
      docs/_build/html/search.html
  75. 0 0
      docs/_build/html/searchindex.js
  76. 59 12
      docs/_build/html/server-installation.html
  77. 35 1
      docs/_build/html/support.html
  78. 39 9
      docs/_build/html/troubleshoot.html
  79. 1 1
      docs/_build/html/ui-reference.html
  80. 1 1
      docs/_build/html/upgrades.html
  81. 1 1
      docs/_build/html/usage.html
  82. 59 47
      docs/api.rst
  83. 12 2
      docs/architecture.rst
  84. 42 0
      docs/client-installation.rst
  85. 3 3
      docs/index.rst
  86. 1 1
      docs/quick-start.rst
  87. 58 8
      docs/server-installation.rst
  88. 27 0
      docs/support.rst
  89. 24 0
      docs/troubleshoot.rst
  90. 0 25
      functions/helpers.go
  91. 6 2
      functions/local.go
  92. 12 1
      go.mod
  93. 24 2
      go.sum
  94. 10 0
      logger/logger.go
  95. 4 2
      logic/dns.go
  96. 2 2
      logic/extpeers.go
  97. 20 32
      logic/gateway.go
  98. 7 6
      logic/jwts.go
  99. 82 14
      logic/networks.go
  100. 264 25
      logic/nodes.go

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

@@ -33,6 +33,7 @@ jobs:
 
       - name: Build
         run: |
+          env GOOS=linux GOARCH=amd64 go build -o build/netmaker main.go
           cd netclient
           env GOOS=linux GOARCH=amd64 go build -o build/netclient main.go
           env GOOS=linux GOARCH=arm GOARM=5 go build -o build/netclient-arm5/netclient main.go
@@ -47,6 +48,16 @@ jobs:
           env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm64 go build -o build/netclient-freebsd-arm64/netclient main.go
           env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o build/netclient-darwin/netclient main.go
           
+      - name: Upload netmaker x86 to Release
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: build/netmaker
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netmaker
+
       - name: Upload x86 to Release
         uses: svenstaro/upload-release-action@v2
         with:

+ 2 - 2
.github/workflows/publish-docker.yml

@@ -18,10 +18,10 @@ jobs:
         run: |
             if [[ -n "${{ github.event.inputs.tag }}" ]]; then
               TAG=${{ github.event.inputs.tag }}
-            elif [[ "${{ github.base_ref }}" == 'master' ]]; then
+            elif [[ "${{ github.ref_name }}" == 'master' ]]; then
               TAG="latest"
             else
-              TAG="${{ github.base_ref }}"
+              TAG="${{ github.ref_name }}"
             fi
             echo "TAG=${TAG}" >> $GITHUB_ENV
       - 

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

@@ -0,0 +1,79 @@
+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@v2
+      - 
+        name: Set up QEMU
+        uses: docker/setup-qemu-action@v1
+      - 
+        name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v1
+      - 
+        name: Login to DockerHub
+        uses: docker/login-action@v1
+        with:
+          username: ${{ secrets.DOCKERHUB_USERNAME }}
+          password: ${{ secrets.DOCKERHUB_TOKEN }}
+      - 
+        name: Build x86 and export to Docker
+        uses: docker/build-push-action@v2
+        with:
+          context: .
+          load: true
+          platforms: linux/amd64
+          file: docker/Dockerfile-netclient-multiarch
+          tags: ${{ env.TAG }}
+      -
+        name: Test x86
+        run: |
+            docker run --rm ${{ env.TAG }}&
+            sleep 10
+            kill %1
+      -
+        name: Build arm and export to Docker
+        uses: docker/build-push-action@v2
+        with:
+          context: .
+          load: true
+          platforms: linux/arm64
+          file: docker/Dockerfile-netclient-multiarch
+          tags: ${{ env.TAG }}
+      -
+        name: Test arm
+        run: |
+            docker run --rm ${{ env.TAG }}&
+            sleep 10
+            kill %1
+      -
+        name: Build and push
+        uses: docker/build-push-action@v2
+        with:
+          context: .
+          platforms: linux/amd64, linux/arm64
+          push: true
+          tags: gravitl/netclient:${{ env.TAG }}

+ 17 - 0
.github/workflows/test.yml

@@ -4,6 +4,23 @@ on:
   push:
 
 jobs:
+  build:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+      - name: Setup Go
+        uses: actions/setup-go@v2
+        with:
+            go-version: 1.17
+      - name: Build
+        run: |
+         env GOOS=linux GOARCH=amd64 go build main.go
+         cd netclient
+         env GOOS=linux GOARCH=amd64 go build main.go
+         env GOOS=freebsd GOARCH=amd64 go build main.go
+         env GOOS=darwin GOARCH=amd64 go build main.go
+         env GOOS=windows GOARCH=amd64 go build main.go
   tests:
     env:
       DATABASE: sqlite

+ 1 - 0
.gitignore

@@ -17,3 +17,4 @@ config/dnsconfig/
 data/
 .vscode/
 .idea/
+

+ 71 - 0
CONTRIBUTING.md

@@ -0,0 +1,71 @@
+# How to contribute to Netmaker
+
+Hey! We're glad you're here. We need your help making Netmaker as great as it can be.
+
+If you haven't already, come chat with us in [Discord](https://discord.gg/zRb9Vfhk8A). We can help you find the right thing to work on.
+
+Before you start contributing, take a moment to check here if it makes sense.
+
+#### **Did you find a bug?**
+
+* Search on on GitHub under [Issues](https://github.com/gravitl/netmaker/issues) to make sure the bug was not already discovered.
+
+* If you don't find an open issue that addresses the problem, you can [open a new one](https://github.com/gravitl/netmaker/issues/new). 
+
+* If you're creating a new issue, include a **title and clear description**, as much relevant information as possible **including logs**, and an explanation/output demonstrating expected behavior vs. actual behavior. Make sure to specify the **version of netmaker/netclient.** If it's a server issue, describe the environment where the server is running. If it's a client issue, give us the operating system and any relevant environment factors (CGNAT, 4g router, etc).
+
+* Respond to team queries in a timely manner, since stale issues will be closed.
+
+#### **Did you write a patch that fixes a bug?**
+
+* Open a new GitHub pull request with the patch **against the 'develop' branch**.
+
+* The PR should clearly describe the problem and solution. Include an issue number if possible.
+
+* Make sure to add comments for any new functions
+
+#### **Did you fix whitespace, format code, or make a purely cosmetic patch?**
+
+Cosmetic changes that do not add substantial useability, stability, functionality, or testability to the code base will not be accepted. The calculation is simple. If it will take more time to merge and test than it took you to make and submit the code, it is likely not worthwhile (execptions exist of course for critical errors with easy fixes).
+
+#### **Do you want to add a new feature to Netmaker?**
+
+* Once again, join the [Discord](https://discord.gg/zRb9Vfhk8A)! Bring it up there and we can discuss. Even if you do not know what you want to build, but you want to build something, we can help you choose something from the roadmap.
+
+#### **Do you want to contribute to Netmaker documentation?**
+
+* Make sure your documentation compiles correctly
+
+* You will need [sphinx](https://www.sphinx-doc.org/en/master/usage/installation.html) and the [material theme](https://github.com/bashtage/sphinx-material/) to run the documentation locally.
+
+* Once the above plugins are installed, you can navigate to the **docs** directory and run **make html**
+
+* View the compiled files (start with index.html under _build) in your browser and make sure your changes look correct before submitting.
+
+
+## Submitting Changes
+
+* Please label your branch using our convention: **purpose_version_thing-you-did**. Purpose is either feature, bugfix, or hotfix.
+
+* Examples: feature_v0.9.5_widget, bugfix_v0.8.2_ipv6-changes
+
+* Please open a [Pull Request](https://github.com/gravitl/netmaker/compare/develop...master?expand=1) against the develop branch with your branch which clearly describes everything you've done and references any related GitHub issues. 
+
+* You will need to sign the CLA in order for us to accept your changes (a bot should appear asking you to sign)
+
+* Please respond to any feedback in a timely manner. Stale PR's will be closed periodically.
+
+## Coding conventions
+
+Take a look around the code to get a feel for how we're doing things.
+
+* Use private functions where possible
+* Use the custom loggers for log messages
+* Comment any new public functions
+
+
+
+
+## Thanks for taking the time to read this! You're awesome, and we look forward to working with you!
+  
+-The Netmaker Team

+ 3 - 3
README.md

@@ -43,7 +43,7 @@
 2. Open ports 443, 80, 53, and 51821-51830/udp on the VM firewall and in cloud security settings.
 3. Run the script **(see below for optional configurations)**:
 
-`sudo wget -qO - https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/nm-quick.sh | bash`
+`wget -qO - https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/nm-quick.sh | sudo bash`
 
 <img src="./docs/images/install-server.gif" width="50%" /><img src="./docs/images/visit-website.gif" width="50%" />
 
@@ -55,11 +55,11 @@ After installing Netmaker, check out the [Walkthrough](https://itnext.io/getting
 
 **Deploy a "Hub-And-Spoke VPN" on the server**  
 *This will configure a standard VPN (non-meshed) for private internet access, with 10 clients (-c).*  
-`sudo wget -qO - https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/nm-quick.sh | bash -s -- -v true -c 10`  
+`wget -qO - https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/nm-quick.sh | sudo bash -s -- -v true -c 10`  
 
 **Specify Domain and Email**  
 *Make sure your wildcard domain is pointing towards the server ip.*  
-`sudo wget -qO - https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/nm-quick.sh | bash -s -- -d mynetmaker.domain.com -e [email protected]`  
+`wget -qO - https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/nm-quick.sh | sudo bash -s -- -d mynetmaker.domain.com -e [email protected]`  
 
 **Script Options**  
 ```

+ 23 - 6
compose/docker-compose.contained.yml

@@ -3,15 +3,19 @@ version: "3.4"
 services:
   netmaker:
     container_name: netmaker
-    image: gravitl/netmaker:v0.9.4
+    image: gravitl/netmaker:v0.10.0
     volumes:
       - dnsconfig:/root/config/dnsconfig
       - /usr/bin/wg:/usr/bin/wg
       - sqldata:/root/data
     cap_add: 
       - NET_ADMIN
+      - NET_RAW
+      - SYS_MODULE
+    sysctls:
+      - net.ipv4.ip_forward=1
+      - net.ipv4.conf.all.src_valid_mark=1
     restart: always
-    privileged: true
     environment:
       SERVER_HOST: "SERVER_PUBLIC_IP"
       SERVER_API_CONN_STRING: "api.NETMAKER_BASE_DOMAIN:443"
@@ -25,11 +29,15 @@ services:
       GRPC_PORT: "50051"
       CLIENT_MODE: "on"
       MASTER_KEY: "REPLACE_MASTER_KEY"
-      SERVER_GRPC_WIREGUARD: "off"
       CORS_ALLOWED_ORIGIN: "*"
       DISPLAY_KEYS: "on"
       DATABASE: "sqlite"
       NODE_ID: "netmaker-server-1"
+      MQ_HOST: "mq"
+      HOST_NETWORK: "off"
+      MANAGE_IPTABLES: "on"
+      PORT_FORWARD_SERVICES: "mq,dns,ssh"
+      VERBOSITY: "1"
     ports:
       - "51821-51830:51821-51830/udp"
       - "8081:8081"
@@ -53,9 +61,6 @@ services:
     command: -conf /root/dnsconfig/Corefile
     container_name: coredns
     restart: always
-    ports:
-      - "COREDNS_IP:53:53/udp"
-      - "COREDNS_IP:53:53/tcp"
     volumes:
       - dnsconfig:/root/dnsconfig
   caddy:
@@ -68,9 +73,21 @@ services:
       # - $PWD/site:/srv # you could also serve a static site in site folder
       - caddy_data:/data
       - caddy_conf:/config
+  mq:
+    image: eclipse-mosquitto:2.0.14
+    container_name: mq
+    restart: unless-stopped
+    ports:
+      - "1883:1883"
+    volumes:
+      - /root/mosquitto.conf:/mosquitto/config/mosquitto.conf
+      - mosquitto_data:/mosquitto/data
+      - mosquitto_logs:/mosquitto/log
 volumes:
   caddy_data: {}
   caddy_conf: {}
   sqldata: {}
   dnsconfig: {}
+  mosquitto_data: {}
+  mosquitto_logs: {}
 

+ 12 - 11
compose/docker-compose.caddy.yml → compose/docker-compose.hostnetwork.yml

@@ -5,19 +5,16 @@ services:
     container_name: netmaker
     image: gravitl/netmaker:v0.9.4
     volumes:
-      - /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket
-      - /run/systemd/system:/run/systemd/system
-      - /etc/systemd/system:/etc/systemd/system
-      - /sys/fs/cgroup:/sys/fs/cgroup
-      - /usr/bin/wg:/usr/bin/wg
       - dnsconfig:/root/config/dnsconfig
+      - /usr/bin/wg:/usr/bin/wg
       - sqldata:/root/data
-    cap_add: 
+      - /run/xtables.lock:/run/xtables.lock
+    cap_add:
       - NET_ADMIN
-      - SYS_ADMIN
-    restart: always
+      - NET_RAW
+      - SYS_MODULE
     network_mode: host
-    privileged: true
+    restart: always
     environment:
       SERVER_HOST: "SERVER_PUBLIC_IP"
       SERVER_API_CONN_STRING: "api.NETMAKER_BASE_DOMAIN:443"
@@ -35,7 +32,11 @@ services:
       CORS_ALLOWED_ORIGIN: "*"
       DISPLAY_KEYS: "on"
       DATABASE: "sqlite"
+      HOST_NETWORK: "on"
       NODE_ID: "netmaker-server-1"
+      MANAGE_IPTABLES: "on"
+      PORT_FORWARD_SERVICES: ""
+      VERBOSITY: "1"
   netmaker-ui:
     container_name: netmaker-ui
     depends_on:
@@ -56,8 +57,8 @@ services:
     container_name: coredns
     restart: always
     ports:
-      - "COREDNS_IP:53:53/udp"
-      - "COREDNS_IP:53:53/tcp"
+      - "53053:53/udp"
+      - "53053:53/tcp"
     volumes:
       - dnsconfig:/root/dnsconfig
   caddy:

+ 64 - 0
compose/docker-compose.nocaddy.yml

@@ -0,0 +1,64 @@
+version: "3.4"
+
+services:
+  netmaker:
+    container_name: netmaker
+    image: gravitl/netmaker:v0.9.4
+    volumes:
+      - dnsconfig:/root/config/dnsconfig
+      - /usr/bin/wg:/usr/bin/wg
+      - sqldata:/root/data
+    cap_add: 
+      - NET_ADMIN
+    restart: always
+    privileged: true
+    environment:
+      SERVER_HOST: "SERVER_PUBLIC_IP"
+      SERVER_API_CONN_STRING: "api.NETMAKER_BASE_DOMAIN:443"
+      SERVER_GRPC_CONN_STRING: "grpc.NETMAKER_BASE_DOMAIN:443"
+      COREDNS_ADDR: "SERVER_PUBLIC_IP"
+      GRPC_SSL: "on"
+      DNS_MODE: "on"
+      SERVER_HTTP_HOST: "api.NETMAKER_BASE_DOMAIN"
+      SERVER_GRPC_HOST: "grpc.NETMAKER_BASE_DOMAIN"
+      API_PORT: "8081"
+      GRPC_PORT: "50051"
+      CLIENT_MODE: "on"
+      MASTER_KEY: "REPLACE_MASTER_KEY"
+      SERVER_GRPC_WIREGUARD: "off"
+      CORS_ALLOWED_ORIGIN: "*"
+      DISPLAY_KEYS: "on"
+      DATABASE: "sqlite"
+      NODE_ID: "netmaker-server-1"
+    ports:
+      - "51821-51830:51821-51830/udp"
+      - "8081:8081"
+      - "50051:50051"
+  netmaker-ui:
+    container_name: netmaker-ui
+    depends_on:
+      - netmaker
+    image: gravitl/netmaker-ui:v0.9.3
+    links:
+      - "netmaker:api"
+    ports:
+      - "8082:80"
+    environment:
+      BACKEND_URL: "https://api.NETMAKER_BASE_DOMAIN"
+    restart: always
+  coredns:
+    depends_on:
+      - netmaker 
+    image: coredns/coredns
+    command: -conf /root/dnsconfig/Corefile
+    container_name: coredns
+    restart: always
+    ports:
+      - "COREDNS_IP:53:53/udp"
+      - "COREDNS_IP:53:53/tcp"
+    volumes:
+      - dnsconfig:/root/dnsconfig
+volumes:
+  sqldata: {}
+  dnsconfig: {}
+

+ 3 - 0
compose/docker-compose.reference.yml

@@ -37,6 +37,9 @@ services:
       DISPLAY_KEYS: "on" # Show keys permanently in UI (until deleted) as opposed to 1-time display.
       SERVER_API_CONN_STRING: "" # Changes the api connection string. IP:PORT format. By default is empty and uses SERVER_HOST:API_PORT
       SERVER_GRPC_CONN_STRING: "" # Changes the grpc connection string. IP:PORT format. By default is empty and uses SERVER_HOST:GRPC_PORT
+      RCE: "off" # Enables setting PostUp and PostDown (arbitrary commands) on nodes from the server. Off by default.
+      NODE_ID: "" # Sets the name/id of the nodes that the server creates. Necessary for HA configurations to identify between servers (for instance, netmaker-1, netmaker-2, etc). For non-HA deployments, is not necessary.
+      TELEMETRY: "on" # Whether or not to send telemetry data to help improve Netmaker. Switch to "off" to opt out of sending telemetry.
   netmaker-ui: # The Netmaker UI Component
     container_name: netmaker-ui
     depends_on:

+ 19 - 8
compose/docker-compose.yml

@@ -5,18 +5,12 @@ services:
     container_name: netmaker
     image: gravitl/netmaker:v0.9.4
     volumes:
-      - /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket
-      - /run/systemd/system:/run/systemd/system
-      - /etc/systemd/system:/etc/systemd/system
-      - /sys/fs/cgroup:/sys/fs/cgroup
-      - /usr/bin/wg:/usr/bin/wg
       - dnsconfig:/root/config/dnsconfig
+      - /usr/bin/wg:/usr/bin/wg
       - sqldata:/root/data
     cap_add: 
       - NET_ADMIN
-      - SYS_ADMIN
     restart: always
-    network_mode: host
     privileged: true
     environment:
       SERVER_HOST: "SERVER_PUBLIC_IP"
@@ -33,9 +27,13 @@ services:
       MASTER_KEY: "REPLACE_MASTER_KEY"
       SERVER_GRPC_WIREGUARD: "off"
       CORS_ALLOWED_ORIGIN: "*"
-      DATABASE: "sqlite"
       DISPLAY_KEYS: "on"
+      DATABASE: "sqlite"
       NODE_ID: "netmaker-server-1"
+    ports:
+      - "51821-51830:51821-51830/udp"
+      - "8081:8081"
+      - "50051:50051"
   netmaker-ui:
     container_name: netmaker-ui
     depends_on:
@@ -60,6 +58,19 @@ services:
       - "COREDNS_IP:53:53/tcp"
     volumes:
       - dnsconfig:/root/dnsconfig
+  caddy:
+    image: caddy:latest
+    container_name: caddy
+    restart: unless-stopped
+    network_mode: host # Wants ports 80 and 443!
+    volumes:
+      - /root/Caddyfile:/etc/caddy/Caddyfile
+      # - $PWD/site:/srv # you could also serve a static site in site folder
+      - caddy_data:/data
+      - caddy_conf:/config
 volumes:
+  caddy_data: {}
+  caddy_conf: {}
   sqldata: {}
   dnsconfig: {}
+

+ 7 - 0
config/config.go

@@ -43,12 +43,14 @@ type ServerConfig struct {
 	GRPCHost              string `yaml:"grpchost"`
 	GRPCPort              string `yaml:"grpcport"`
 	GRPCSecure            string `yaml:"grpcsecure"`
+	MQHOST                string `yaml:"mqhost"`
 	MasterKey             string `yaml:"masterkey"`
 	DNSKey                string `yaml:"dnskey"`
 	AllowedOrigin         string `yaml:"allowedorigin"`
 	NodeID                string `yaml:"nodeid"`
 	RestBackend           string `yaml:"restbackend"`
 	AgentBackend          string `yaml:"agentbackend"`
+	MessageQueueBackend   string `yaml:"messagequeuebackend"`
 	ClientMode            string `yaml:"clientmode"`
 	DNSMode               string `yaml:"dnsmode"`
 	SplitDNS              string `yaml:"splitdns"`
@@ -70,6 +72,11 @@ type ServerConfig struct {
 	DisplayKeys           string `yaml:"displaykeys"`
 	AzureTenant           string `yaml:"azuretenant"`
 	RCE                   string `yaml:"rce"`
+	Debug                 bool   `yaml:"debug"`
+	Telemetry             string `yaml:"telemetry"`
+	ManageIPTables        string `yaml:"manageiptables"`
+	PortForwardServices   string `yaml:"portforwardservices"`
+	HostNetwork           string `yaml:"hostnetwork"`
 }
 
 // SQLConfig - Generic SQL Config

+ 2 - 1
config/environments/dev.yaml

@@ -11,4 +11,5 @@ server:
   dnsmode: "" # defaults to "on" or DNS_MODE (if set)
   sqlconn: "" # defaults to "http://" or SQL_CONN (if set)
   disableremoteipcheck: "" # defaults to "false" or DISABLE_REMOTE_IP_CHECK (if set)
-  version: "" # version of server
+  version: "" # version of server
+  rce: "" # defaults to "off"

+ 14 - 16
controllers/auth_grpc.go

@@ -72,7 +72,7 @@ func grpcAuthorize(ctx context.Context) error {
 
 	authToken := authHeader[0]
 
-	mac, network, err := logic.VerifyToken(authToken)
+	nodeID, _, network, err := logic.VerifyToken(authToken)
 	if err != nil {
 		return err
 	}
@@ -82,10 +82,10 @@ func grpcAuthorize(ctx context.Context) error {
 	if err != nil {
 		return status.Errorf(codes.Unauthenticated, "Unauthorized. Network does not exist: "+network)
 	}
-	emptynode := models.Node{}
-	node, err := logic.GetNodeByMacAddress(network, mac)
+	node, err := logic.GetNodeByID(nodeID)
 	if database.IsEmptyRecord(err) {
-		if node, err = logic.GetDeletedNodeByMacAddress(network, mac); err == nil {
+		// == DELETE replace logic after 2 major version updates ==
+		if node, err = logic.GetDeletedNodeByID(node.ID); err == nil {
 			if functions.RemoveDeletedNode(node.ID) {
 				return status.Errorf(codes.Unauthenticated, models.NODE_DELETE)
 			}
@@ -93,7 +93,7 @@ func grpcAuthorize(ctx context.Context) error {
 		}
 		return status.Errorf(codes.Unauthenticated, "Empty record")
 	}
-	if err != nil || node.MacAddress == emptynode.MacAddress {
+	if err != nil || node.ID == "" {
 		return status.Errorf(codes.Unauthenticated, "Node does not exist.")
 	}
 
@@ -106,29 +106,27 @@ func grpcAuthorize(ctx context.Context) error {
 // Login - node authenticates using its password and retrieves a JWT for authorization.
 func (s *NodeServiceServer) Login(ctx context.Context, req *nodepb.Object) (*nodepb.Object, error) {
 
-	//out := new(LoginResponse)
-	var reqNode models.Node
-	if err := json.Unmarshal([]byte(req.Data), &reqNode); err != nil {
+	var reqNode, err = getNodeFromRequestData(req.Data)
+	if err != nil {
 		return nil, err
 	}
 
-	macaddress := reqNode.MacAddress
+	nodeID := reqNode.ID
 	network := reqNode.Network
 	password := reqNode.Password
+	macaddress := reqNode.MacAddress
 
 	var result models.NodeAuth
-	var err error
-	// err := errors.New("generic server error")
 
-	if macaddress == "" {
+	if nodeID == "" {
 		//TODO: Set Error  response
-		err = errors.New("missing mac address")
+		err = errors.New("missing node ID")
 		return nil, err
 	} else if password == "" {
 		err = errors.New("missing password")
 		return nil, err
 	} else {
-		//Search DB for node with Mac Address. Ignore pending nodes (they should not be able to authenticate with API until approved).
+		//Search DB for node with ID. Ignore pending nodes (they should not be able to authenticate with API until approved).
 		collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
 		if err != nil {
 			return nil, err
@@ -137,7 +135,7 @@ func (s *NodeServiceServer) Login(ctx context.Context, req *nodepb.Object) (*nod
 			if err = json.Unmarshal([]byte(value), &result); err != nil {
 				continue // finish going through nodes
 			}
-			if result.MacAddress == macaddress && result.Network == network {
+			if result.ID == nodeID && result.Network == network {
 				break
 			}
 		}
@@ -150,7 +148,7 @@ func (s *NodeServiceServer) Login(ctx context.Context, req *nodepb.Object) (*nod
 			return nil, err
 		} else {
 			//Create a new JWT for the node
-			tokenString, err := logic.CreateJWT(macaddress, result.Network)
+			tokenString, err := logic.CreateJWT(result.ID, macaddress, result.Network)
 
 			if err != nil {
 				return nil, err

+ 2 - 1
controllers/config/dnsconfig/netmaker.hosts

@@ -1 +1,2 @@
-10.0.0.1         node-4bukt.skynet
+10.0.0.1         testnode.skynet
+10.0.0.2         myhost.skynet

+ 2 - 2
controllers/controller.go

@@ -15,6 +15,7 @@ import (
 	"github.com/gravitl/netmaker/servercfg"
 )
 
+// HttpHandlers - handler functions for REST interactions
 var HttpHandlers = []interface{}{
 	nodeHandlers,
 	userHandlers,
@@ -23,7 +24,6 @@ var HttpHandlers = []interface{}{
 	fileHandlers,
 	serverHandlers,
 	extClientHandlers,
-	loggerHandlers,
 }
 
 // HandleRESTRequests - handles the rest requests
@@ -64,7 +64,7 @@ func HandleRESTRequests(wg *sync.WaitGroup) {
 
 	// After receiving CTRL+C Properly stop the server
 	logger.Log(0, "Stopping the REST server...")
-	srv.Shutdown(context.TODO())
 	logger.Log(0, "REST Server closed.")
 	logger.DumpFile(fmt.Sprintf("data/netmaker.log.%s", time.Now().Format(logger.TimeFormatDay)))
+	srv.Shutdown(context.TODO())
 }

+ 8 - 8
controllers/ext_client.go

@@ -25,11 +25,11 @@ func extClientHandlers(r *mux.Router) {
 	r.HandleFunc("/api/extclients/{network}/{clientid}/{type}", securityCheck(false, http.HandlerFunc(getExtClientConf))).Methods("GET")
 	r.HandleFunc("/api/extclients/{network}/{clientid}", securityCheck(false, http.HandlerFunc(updateExtClient))).Methods("PUT")
 	r.HandleFunc("/api/extclients/{network}/{clientid}", securityCheck(false, http.HandlerFunc(deleteExtClient))).Methods("DELETE")
-	r.HandleFunc("/api/extclients/{network}/{macaddress}", securityCheck(false, http.HandlerFunc(createExtClient))).Methods("POST")
+	r.HandleFunc("/api/extclients/{network}/{nodeid}", securityCheck(false, http.HandlerFunc(createExtClient))).Methods("POST")
 }
 
-func checkIngressExists(network string, macaddress string) bool {
-	node, err := logic.GetNodeByMacAddress(network, macaddress)
+func checkIngressExists(nodeID string) bool {
+	node, err := logic.GetNodeByID(nodeID)
 	if err != nil {
 		return false
 	}
@@ -122,7 +122,7 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	gwnode, err := logic.GetNodeByMacAddress(client.Network, client.IngressGatewayID)
+	gwnode, err := logic.GetNodeByID(client.IngressGatewayID)
 	if err != nil {
 		logger.Log(1, fmt.Sprintf("%s %s %s", r.Header.Get("user"), "Could not retrieve Ingress Gateway Node", client.IngressGatewayID))
 		returnErrorResponse(w, r, formatError(err, "internal"))
@@ -211,8 +211,8 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 	var params = mux.Vars(r)
 
 	networkName := params["network"]
-	macaddress := params["macaddress"]
-	ingressExists := checkIngressExists(networkName, macaddress)
+	nodeid := params["nodeid"]
+	ingressExists := checkIngressExists(nodeid)
 	if !ingressExists {
 		returnErrorResponse(w, r, formatError(errors.New("ingress does not exist"), "internal"))
 		return
@@ -220,8 +220,8 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 
 	var extclient models.ExtClient
 	extclient.Network = networkName
-	extclient.IngressGatewayID = macaddress
-	node, err := logic.GetNodeByMacAddress(networkName, macaddress)
+	extclient.IngressGatewayID = nodeid
+	node, err := logic.GetNodeByID(nodeid)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return

+ 0 - 23
controllers/logger.go

@@ -1,23 +0,0 @@
-package controller
-
-import (
-	"fmt"
-	"net/http"
-	"time"
-
-	"github.com/gorilla/mux"
-	"github.com/gravitl/netmaker/logger"
-)
-
-func loggerHandlers(r *mux.Router) {
-	r.HandleFunc("/api/logs", securityCheck(true, http.HandlerFunc(getLogs))).Methods("GET")
-}
-
-func getLogs(w http.ResponseWriter, r *http.Request) {
-	var currentTime = time.Now().Format(logger.TimeFormatDay)
-	var currentFilePath = fmt.Sprintf("data/netmaker.log.%s", currentTime)
-	logger.DumpFile(currentFilePath)
-	logger.ResetLogs()
-	w.WriteHeader(http.StatusOK)
-	w.Write([]byte(logger.Retrieve(currentFilePath)))
-}

+ 44 - 7
controllers/network.go

@@ -3,6 +3,7 @@ package controller
 import (
 	"encoding/json"
 	"errors"
+	"fmt"
 	"net/http"
 	"strings"
 
@@ -11,8 +12,8 @@ import (
 	"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"
-	"github.com/gravitl/netmaker/serverctl"
 )
 
 const ALL_NETWORK_ACCESS = "THIS_USER_HAS_ALL"
@@ -99,6 +100,23 @@ func keyUpdate(w http.ResponseWriter, r *http.Request) {
 	logger.Log(2, r.Header.Get("user"), "updated key on network", netname)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(network)
+	nodes, err := logic.GetNetworkNodes(netname)
+	if err != nil {
+		logger.Log(2, "failed to retrieve network nodes for network", netname, err.Error())
+		return
+	}
+	for _, node := range nodes {
+		fmt.Println("updating node ", node.Name, " for a key update")
+		if err := mq.NodeUpdate(&node); err != nil {
+			logger.Log(2, "failed key update ", node.Name)
+		}
+	}
+	node, err := logic.GetNetworkServerLeader(netname)
+	if err != nil {
+		logger.Log(2, "failed to get server node")
+		return
+	}
+	runUpdates(&node, false)
 }
 
 // Update a network
@@ -124,7 +142,7 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
 		newNetwork.DefaultPostUp = network.DefaultPostUp
 	}
 
-	rangeupdate, localrangeupdate, err := logic.UpdateNetwork(&network, &newNetwork)
+	rangeupdate, localrangeupdate, holepunchupdate, err := logic.UpdateNetwork(&network, &newNetwork)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
@@ -149,6 +167,24 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 	}
+	if holepunchupdate {
+		err = logic.UpdateNetworkHolePunching(network.NetID, newNetwork.DefaultUDPHolePunch)
+		if err != nil {
+			returnErrorResponse(w, r, formatError(err, "internal"))
+			return
+		}
+	}
+	if rangeupdate || localrangeupdate || holepunchupdate {
+		nodes, err := logic.GetNetworkNodes(network.NetID)
+		if err != nil {
+			returnErrorResponse(w, r, formatError(err, "internal"))
+			return
+		}
+		for _, node := range nodes {
+			runUpdates(&node, true)
+		}
+	}
+
 	logger.Log(1, r.Header.Get("user"), "updated network", netname)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(newNetwork)
@@ -183,8 +219,8 @@ func updateNetworkNodeLimit(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(network)
 }
 
-//Delete a network
-//Will stop you if  there's any nodes associated
+// Delete a network
+// Will stop you if  there's any nodes associated
 func deleteNetwork(w http.ResponseWriter, r *http.Request) {
 	// Set header
 	w.Header().Set("Content-Type", "application/json")
@@ -226,9 +262,9 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if servercfg.IsClientMode() != "off" {
-		var success bool
-		success, err = serverctl.AddNetwork(&network)
-		if err != nil || !success {
+		var node models.Node
+		node, err = logic.ServerJoin(&network)
+		if err != nil {
 			logic.DeleteNetwork(network.NetID)
 			if err == nil {
 				err = errors.New("Failed to add server to network " + network.DisplayName)
@@ -236,6 +272,7 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
 			returnErrorResponse(w, r, formatError(err, "internal"))
 			return
 		}
+		getServerAddrs(&node)
 	}
 
 	logger.Log(1, r.Header.Get("user"), "created network", network.NetID)

+ 93 - 43
controllers/node.go

@@ -2,6 +2,7 @@ package controller
 
 import (
 	"encoding/json"
+	"fmt"
 	"net/http"
 	"strings"
 
@@ -11,6 +12,7 @@ import (
 	"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"
 	"golang.org/x/crypto/bcrypt"
 )
@@ -19,16 +21,16 @@ func nodeHandlers(r *mux.Router) {
 
 	r.HandleFunc("/api/nodes", authorize(false, "user", http.HandlerFunc(getAllNodes))).Methods("GET")
 	r.HandleFunc("/api/nodes/{network}", authorize(true, "network", http.HandlerFunc(getNetworkNodes))).Methods("GET")
-	r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(getNode))).Methods("GET")
-	r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(updateNode))).Methods("PUT")
-	r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(deleteNode))).Methods("DELETE")
-	r.HandleFunc("/api/nodes/{network}/{macaddress}/createrelay", authorize(true, "user", http.HandlerFunc(createRelay))).Methods("POST")
-	r.HandleFunc("/api/nodes/{network}/{macaddress}/deleterelay", authorize(true, "user", http.HandlerFunc(deleteRelay))).Methods("DELETE")
-	r.HandleFunc("/api/nodes/{network}/{macaddress}/creategateway", authorize(true, "user", http.HandlerFunc(createEgressGateway))).Methods("POST")
-	r.HandleFunc("/api/nodes/{network}/{macaddress}/deletegateway", authorize(true, "user", http.HandlerFunc(deleteEgressGateway))).Methods("DELETE")
-	r.HandleFunc("/api/nodes/{network}/{macaddress}/createingress", securityCheck(false, http.HandlerFunc(createIngressGateway))).Methods("POST")
-	r.HandleFunc("/api/nodes/{network}/{macaddress}/deleteingress", securityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods("DELETE")
-	r.HandleFunc("/api/nodes/{network}/{macaddress}/approve", authorize(true, "user", http.HandlerFunc(uncordonNode))).Methods("POST")
+	r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, "node", http.HandlerFunc(getNode))).Methods("GET")
+	r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, "node", http.HandlerFunc(updateNode))).Methods("PUT")
+	r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, "node", http.HandlerFunc(deleteNode))).Methods("DELETE")
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", authorize(true, "user", http.HandlerFunc(createRelay))).Methods("POST")
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", authorize(true, "user", http.HandlerFunc(deleteRelay))).Methods("DELETE")
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", authorize(true, "user", http.HandlerFunc(createEgressGateway))).Methods("POST")
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", authorize(true, "user", http.HandlerFunc(deleteEgressGateway))).Methods("DELETE")
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", securityCheck(false, http.HandlerFunc(createIngressGateway))).Methods("POST")
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", securityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods("DELETE")
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/approve", authorize(true, "user", http.HandlerFunc(uncordonNode))).Methods("POST")
 	r.HandleFunc("/api/nodes/{network}", createNode).Methods("POST")
 	r.HandleFunc("/api/nodes/adm/{network}/lastmodified", authorize(true, "network", http.HandlerFunc(getLastModified))).Methods("GET")
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods("POST")
@@ -78,7 +80,7 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
 				if err := json.Unmarshal([]byte(value), &result); err != nil {
 					continue
 				}
-				if result.MacAddress == authRequest.MacAddress && result.IsPending != "yes" && result.Network == networkname {
+				if (result.ID == authRequest.ID || result.MacAddress == authRequest.MacAddress) && result.IsPending != "yes" && result.Network == networkname {
 					break
 				}
 			}
@@ -97,7 +99,7 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
 				returnErrorResponse(response, request, errorResponse)
 				return
 			} else {
-				tokenString, _ := logic.CreateJWT(authRequest.MacAddress, result.Network)
+				tokenString, _ := logic.CreateJWT(authRequest.ID, authRequest.MacAddress, result.Network)
 
 				if tokenString == "" {
 					errorResponse.Code = http.StatusBadRequest
@@ -177,21 +179,21 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
 			}
 
 			var isAuthorized = false
-			var macaddress = ""
+			var nodeID = ""
 			username, networks, isadmin, errN := logic.VerifyUserToken(authToken)
 			isnetadmin := isadmin
 			if errN == nil && isadmin {
-				macaddress = "mastermac"
+				nodeID = "mastermac"
 				isAuthorized = true
 				r.Header.Set("ismasterkey", "yes")
 			}
 			if !isadmin && params["network"] != "" {
-				if functions.SliceContains(networks, params["network"]) {
+				if logic.StringSliceContains(networks, params["network"]) {
 					isnetadmin = true
 				}
 			}
 			//The mastermac (login with masterkey from config) can do everything!! May be dangerous.
-			if macaddress == "mastermac" {
+			if nodeID == "mastermac" {
 				isAuthorized = true
 				r.Header.Set("ismasterkey", "yes")
 				//for everyone else, there's poor man's RBAC. The "cases" are defined in the routes in the handlers
@@ -201,12 +203,12 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
 				case "all":
 					isAuthorized = true
 				case "nodes":
-					isAuthorized = (macaddress != "") || isnetadmin
+					isAuthorized = (nodeID != "") || isnetadmin
 				case "network":
 					if isnetadmin {
 						isAuthorized = true
 					} else {
-						node, err := logic.GetNodeByMacAddress(params["network"], macaddress)
+						node, err := logic.GetNodeByID(nodeID)
 						if err != nil {
 							errorResponse = models.ErrorResponse{
 								Code: http.StatusUnauthorized, Message: "W1R3: Missing Auth Token.",
@@ -220,7 +222,7 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
 					if isnetadmin {
 						isAuthorized = true
 					} else {
-						isAuthorized = (macaddress == params["macaddress"])
+						isAuthorized = (nodeID == params["netid"])
 					}
 				case "user":
 					isAuthorized = true
@@ -315,12 +317,12 @@ func getNode(w http.ResponseWriter, r *http.Request) {
 
 	var params = mux.Vars(r)
 
-	node, err := logic.GetNode(params["macaddress"], params["network"])
+	node, err := logic.GetNodeByID(params["nodeid"])
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
-	logger.Log(2, r.Header.Get("user"), "fetched node", params["macaddress"])
+	logger.Log(2, r.Header.Get("user"), "fetched node", params["nodeid"])
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)
 }
@@ -404,9 +406,12 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+
 	logger.Log(1, r.Header.Get("user"), "created new node", node.Name, "on network", node.Network)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)
+
+	runUpdates(&node, false)
 }
 
 //Takes node out of pending state
@@ -414,7 +419,8 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 func uncordonNode(w http.ResponseWriter, r *http.Request) {
 	var params = mux.Vars(r)
 	w.Header().Set("Content-Type", "application/json")
-	node, err := logic.UncordonNode(params["network"], params["macaddress"])
+	var nodeid = params["nodeid"]
+	node, err := logic.UncordonNode(nodeid)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -422,6 +428,8 @@ func uncordonNode(w http.ResponseWriter, r *http.Request) {
 	logger.Log(1, r.Header.Get("user"), "uncordoned node", node.Name)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode("SUCCESS")
+
+	runUpdates(&node, true)
 }
 
 func createEgressGateway(w http.ResponseWriter, r *http.Request) {
@@ -434,30 +442,36 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	gateway.NetID = params["network"]
-	gateway.NodeID = params["macaddress"]
+	gateway.NodeID = params["nodeid"]
 	node, err := logic.CreateEgressGateway(gateway)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+
 	logger.Log(1, r.Header.Get("user"), "created egress gateway on node", gateway.NodeID, "on network", gateway.NetID)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)
+
+	runUpdates(&node, true)
 }
 
 func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	var params = mux.Vars(r)
-	nodeMac := params["macaddress"]
+	nodeid := params["nodeid"]
 	netid := params["network"]
-	node, err := logic.DeleteEgressGateway(netid, nodeMac)
+	node, err := logic.DeleteEgressGateway(netid, nodeid)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
-	logger.Log(1, r.Header.Get("user"), "deleted egress gateway", nodeMac, "on network", netid)
+
+	logger.Log(1, r.Header.Get("user"), "deleted egress gateway", nodeid, "on network", netid)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)
+
+	runUpdates(&node, true)
 }
 
 // == INGRESS ==
@@ -465,30 +479,36 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
 func createIngressGateway(w http.ResponseWriter, r *http.Request) {
 	var params = mux.Vars(r)
 	w.Header().Set("Content-Type", "application/json")
-	nodeMac := params["macaddress"]
+	nodeid := params["nodeid"]
 	netid := params["network"]
-	node, err := logic.CreateIngressGateway(netid, nodeMac)
+	node, err := logic.CreateIngressGateway(netid, nodeid)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
-	logger.Log(1, r.Header.Get("user"), "created ingress gateway on node", nodeMac, "on network", netid)
+
+	logger.Log(1, r.Header.Get("user"), "created ingress gateway on node", nodeid, "on network", netid)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)
+
+	runUpdates(&node, true)
 }
 
 func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	var params = mux.Vars(r)
-	nodeMac := params["macaddress"]
-	node, err := logic.DeleteIngressGateway(params["network"], nodeMac)
+	nodeid := params["nodeid"]
+	node, err := logic.DeleteIngressGateway(params["network"], nodeid)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
-	logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeMac)
+
+	logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)
+
+	runUpdates(&node, true)
 }
 
 func updateNode(w http.ResponseWriter, r *http.Request) {
@@ -498,7 +518,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 
 	var node models.Node
 	//start here
-	node, err := logic.GetNodeByMacAddress(params["network"], params["macaddress"])
+	node, err := logic.GetNodeByID(params["nodeid"])
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -543,15 +563,14 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if servercfg.IsDNSMode() {
-		err = logic.SetDNS()
-	}
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		logic.SetDNS()
 	}
-	logger.Log(1, r.Header.Get("user"), "updated node", node.MacAddress, "on network", node.Network)
+
+	logger.Log(1, r.Header.Get("user"), "updated node", node.ID, "on network", node.Network)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(newNode)
+
+	runUpdates(&newNode, true)
 }
 
 func deleteNode(w http.ResponseWriter, r *http.Request) {
@@ -560,17 +579,48 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
 
 	// get params
 	var params = mux.Vars(r)
-	var node, err = logic.GetNode(params["macaddress"], params["network"])
+	var nodeid = params["nodeid"]
+	var node, err = logic.GetNodeByID(nodeid)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
 	}
-	err = logic.DeleteNode(&node, false)
+	if isServer(&node) {
+		returnErrorResponse(w, r, formatError(fmt.Errorf("cannot delete server node"), "badrequest"))
+		return
+	}
+	//send update to node to be deleted before deleting on server otherwise message cannot be sent
+	node.Action = models.NODE_DELETE
+	runUpdates(&node, true)
+	err = logic.DeleteNodeByID(&node, false)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+	logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
+	returnSuccessResponse(w, r, nodeid+" deleted.")
+
+}
+
+func runUpdates(node *models.Node, nodeUpdate bool) error {
+	//don't publish to server node
+
+	if nodeUpdate && !isServer(node) {
+		if err := mq.NodeUpdate(node); err != nil {
+			logger.Log(1, "error publishing node update", err.Error())
+			return err
+		}
+	}
+
+	if err := runServerPeerUpdate(node, isServer(node)); err != nil {
+		logger.Log(1, "internal error when running peer node:", err.Error())
+		return err
+	}
 
-	logger.Log(1, r.Header.Get("user"), "Deleted node", params["macaddress"], "from network", params["network"])
-	returnSuccessResponse(w, r, params["macaddress"]+" deleted.")
+	return nil
 }

+ 136 - 58
controllers/node_grpc.go

@@ -4,13 +4,16 @@ import (
 	"context"
 	"encoding/json"
 	"errors"
-	"strings"
+	"fmt"
+	"time"
 
 	nodepb "github.com/gravitl/netmaker/grpc"
 	"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"
+	"github.com/gravitl/netmaker/serverctl"
 )
 
 // NodeServiceServer - represents the service server for gRPC
@@ -20,20 +23,17 @@ type NodeServiceServer struct {
 
 // NodeServiceServer.ReadNode - reads node and responds with gRPC
 func (s *NodeServiceServer) ReadNode(ctx context.Context, req *nodepb.Object) (*nodepb.Object, error) {
-	// convert string id (from proto) to mongoDB ObjectId
-	macAndNetwork := strings.Split(req.Data, "###")
-
-	if len(macAndNetwork) != 2 {
-		return nil, errors.New("could not read node, invalid node id given")
-	}
-	node, err := logic.GetNode(macAndNetwork[0], macAndNetwork[1])
+	var node, err = getNodeFromRequestData(req.Data)
 	if err != nil {
 		return nil, err
 	}
+
 	node.NetworkSettings, err = logic.GetNetworkSettings(node.Network)
 	if err != nil {
 		return nil, err
 	}
+	getServerAddrs(&node)
+
 	node.SetLastCheckIn()
 	// Cast to ReadNodeRes type
 	nodeData, errN := json.Marshal(&node)
@@ -62,7 +62,6 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.Object)
 	if err != nil {
 		return nil, err
 	}
-
 	if !validKey {
 		if node.NetworkSettings.AllowManualSignUp == "yes" {
 			node.IsPending = "yes"
@@ -70,6 +69,27 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.Object)
 			return nil, errors.New("invalid key, and network does not allow no-key signups")
 		}
 	}
+	getServerAddrs(&node)
+
+	key, keyErr := logic.RetrievePublicTrafficKey()
+	if keyErr != nil {
+		logger.Log(0, "error retrieving key: ", keyErr.Error())
+		return nil, keyErr
+	}
+
+	if key == nil {
+		logger.Log(0, "error: server traffic key is nil")
+		return nil, fmt.Errorf("error: server traffic key is nil")
+	}
+	if node.TrafficKeys.Mine == nil {
+		logger.Log(0, "error: node traffic key is nil")
+		return nil, fmt.Errorf("error: node traffic key is nil")
+	}
+
+	node.TrafficKeys = models.TrafficKeys{
+		Mine:   node.TrafficKeys.Mine,
+		Server: key,
+	}
 
 	err = logic.CreateNode(&node)
 	if err != nil {
@@ -86,25 +106,41 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.Object)
 		Type: nodepb.NODE_TYPE,
 	}
 
-	err = logic.SetNetworkNodesLastModified(node.Network)
-	if err != nil {
-		return nil, err
-	}
+	runUpdates(&node, false)
+
+	go func(node *models.Node) {
+		if node.UDPHolePunch == "yes" {
+			var currentServerNode, getErr = logic.GetNetworkServerLeader(node.Network)
+			if getErr != nil {
+				return
+			}
+			for i := 0; i < 5; i++ {
+				if logic.HasPeerConnected(node) {
+					if logic.ShouldPublishPeerPorts(&currentServerNode) {
+						err = mq.PublishPeerUpdate(&currentServerNode)
+						if err != nil {
+							logger.Log(1, "error publishing port updates when node", node.Name, "joined")
+						}
+						break
+					}
+				}
+				time.Sleep(time.Second << 1) // allow time for client to startup
+			}
+		}
+	}(&node)
 
 	return response, nil
 }
 
 // NodeServiceServer.UpdateNode updates a node and responds over gRPC
 func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.Object) (*nodepb.Object, error) {
-	// Get the node data from the request
+
 	var newnode models.Node
 	if err := json.Unmarshal([]byte(req.GetData()), &newnode); err != nil {
 		return nil, err
 	}
-	macaddress := newnode.MacAddress
-	networkName := newnode.Network
 
-	node, err := logic.GetNodeByMacAddress(networkName, macaddress)
+	node, err := logic.GetNodeByID(newnode.ID)
 	if err != nil {
 		return nil, err
 	}
@@ -122,28 +158,64 @@ func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.Object)
 	if err != nil {
 		return nil, err
 	}
+	getServerAddrs(&newnode)
+
 	nodeData, errN := json.Marshal(&newnode)
 	if errN != nil {
 		return nil, err
 	}
+
+	runUpdates(&newnode, false)
+
 	return &nodepb.Object{
 		Data: string(nodeData),
 		Type: nodepb.NODE_TYPE,
 	}, nil
 }
 
+func getServerAddrs(node *models.Node) {
+	serverNodes := logic.GetServerNodes(node.Network)
+	//pubIP, _ := servercfg.GetPublicIP()
+	if len(serverNodes) == 0 {
+		if err := serverctl.SyncServerNetwork(node.Network); err != nil {
+			return
+		}
+	}
+
+	var serverAddrs = make([]models.ServerAddr, 0)
+
+	for _, node := range serverNodes {
+		if node.Address != "" {
+			serverAddrs = append(serverAddrs, models.ServerAddr{
+				IsLeader: logic.IsLeader(&node),
+				Address:  node.Address,
+			})
+		}
+	}
+
+	networkSettings, _ := logic.GetParentNetwork(node.Network)
+	// TODO consolidate functionality around files
+	networkSettings.NodesLastModified = time.Now().Unix()
+	networkSettings.DefaultServerAddrs = serverAddrs
+	if err := logic.SaveNetwork(&networkSettings); err != nil {
+		logger.Log(1, "unable to save network on serverAddr update", err.Error())
+	}
+	node.NetworkSettings.DefaultServerAddrs = networkSettings.DefaultServerAddrs
+}
+
 // NodeServiceServer.DeleteNode - deletes a node and responds over gRPC
 func (s *NodeServiceServer) DeleteNode(ctx context.Context, req *nodepb.Object) (*nodepb.Object, error) {
-	nodeID := req.GetData()
-	var nodeInfo = strings.Split(nodeID, "###")
-	if len(nodeInfo) != 2 {
-		return nil, errors.New("node not found")
+
+	var node, err = getNodeFromRequestData(req.Data)
+	if err != nil {
+		return nil, err
 	}
-	var node, err = logic.GetNode(nodeInfo[0], nodeInfo[1])
-	err = logic.DeleteNode(&node, true)
+
+	err = logic.DeleteNodeByID(&node, true)
 	if err != nil {
 		return nil, err
 	}
+	runServerPeerUpdate(&node, false)
 
 	return &nodepb.Object{
 		Data: "success",
@@ -153,49 +225,39 @@ func (s *NodeServiceServer) DeleteNode(ctx context.Context, req *nodepb.Object)
 
 // NodeServiceServer.GetPeers - fetches peers over gRPC
 func (s *NodeServiceServer) GetPeers(ctx context.Context, req *nodepb.Object) (*nodepb.Object, error) {
-	macAndNetwork := strings.Split(req.Data, "###")
-	if len(macAndNetwork) == 2 {
-		// TODO: Make constant and new variable for isServer
-		node, err := logic.GetNode(macAndNetwork[0], macAndNetwork[1])
-		if err != nil {
-			return nil, err
-		}
-		if node.IsServer == "yes" && logic.IsLeader(&node) {
-			logic.SetNetworkServerPeers(&node)
-		}
-		excludeIsRelayed := node.IsRelay != "yes"
-		var relayedNode string
-		if node.IsRelayed == "yes" {
-			relayedNode = node.Address
-		}
-		peers, err := logic.GetPeersList(macAndNetwork[1], excludeIsRelayed, relayedNode)
-		if err != nil {
-			return nil, err
-		}
 
-		peersData, err := json.Marshal(&peers)
-		logger.Log(3, node.Address, "checked in successfully")
-		return &nodepb.Object{
-			Data: string(peersData),
-			Type: nodepb.NODE_TYPE,
-		}, err
+	var node, err = getNodeFromRequestData(req.Data)
+	if err != nil {
+		return nil, err
+	}
+
+	excludeIsRelayed := node.IsRelay != "yes"
+	var relayedNode string
+	if node.IsRelayed == "yes" {
+		relayedNode = node.Address
+	}
+	peers, err := logic.GetPeersList(node.Network, excludeIsRelayed, relayedNode)
+	if err != nil {
+		return nil, err
 	}
+
+	peersData, err := json.Marshal(&peers)
+	logger.Log(3, node.Address, "checked in successfully")
 	return &nodepb.Object{
-		Data: "",
+		Data: string(peersData),
 		Type: nodepb.NODE_TYPE,
-	}, errors.New("could not fetch peers, invalid node id")
+	}, err
 }
 
 // NodeServiceServer.GetExtPeers - returns ext peers for a gateway node
 func (s *NodeServiceServer) GetExtPeers(ctx context.Context, req *nodepb.Object) (*nodepb.Object, error) {
-	// Initiate a NodeItem type to write decoded data to
-	//data := &models.PeersResponse{}
-	// collection.Find returns a cursor for our (empty) query
-	macAndNetwork := strings.Split(req.Data, "###")
-	if len(macAndNetwork) != 2 {
-		return nil, errors.New("did not receive valid node id when fetching ext peers")
-	}
-	peers, err := logic.GetExtPeersList(macAndNetwork[0], macAndNetwork[1])
+
+	var node, err = getNodeFromRequestData(req.Data)
+	if err != nil {
+		return nil, err
+	}
+
+	peers, err := logic.GetExtPeersList(&node)
 	if err != nil {
 		return nil, err
 	}
@@ -222,3 +284,19 @@ func (s *NodeServiceServer) GetExtPeers(ctx context.Context, req *nodepb.Object)
 		Type: nodepb.EXT_PEER,
 	}, nil
 }
+
+// == private methods ==
+
+func getNodeFromRequestData(data string) (models.Node, error) {
+	var reqNode models.Node
+	var err error
+
+	if err = json.Unmarshal([]byte(data), &reqNode); err != nil {
+		return models.Node{}, err
+	}
+	return logic.GetNodeByID(reqNode.ID)
+}
+
+func isServer(node *models.Node) bool {
+	return node.IsServer == "yes"
+}

+ 23 - 27
controllers/node_test.go

@@ -13,20 +13,31 @@ func TestCreateEgressGateway(t *testing.T) {
 	var gateway models.EgressGatewayRequest
 	gateway.Interface = "eth0"
 	gateway.Ranges = []string{"10.100.100.0/24"}
+	gateway.NetID = "skynet"
 	database.InitializeDatabase()
 	deleteAllNetworks()
 	createNet()
 	t.Run("NoNodes", func(t *testing.T) {
 		node, err := logic.CreateEgressGateway(gateway)
 		assert.Equal(t, models.Node{}, node)
-		assert.EqualError(t, err, "unable to get record key")
+		assert.EqualError(t, err, "could not find any records")
+	})
+	t.Run("Non-linux node", func(t *testing.T) {
+		createnode := models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: "testnode", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet", OS: "freebsd"}
+		err := logic.CreateNode(&createnode)
+		assert.Nil(t, err)
+		gateway.NodeID = createnode.ID
+		node, err := logic.CreateEgressGateway(gateway)
+		assert.Equal(t, models.Node{}, node)
+		assert.EqualError(t, err, "freebsd is unsupported for egress gateways")
 	})
 	t.Run("Success", func(t *testing.T) {
+		deleteAllNodes()
 		testnode := createTestNode()
-		gateway.NetID = "skynet"
-		gateway.NodeID = testnode.MacAddress
+		gateway.NodeID = testnode.ID
 
 		node, err := logic.CreateEgressGateway(gateway)
+		t.Log(node)
 		assert.Nil(t, err)
 		assert.Equal(t, "yes", node.IsEgressGateway)
 		assert.Equal(t, gateway.Ranges, node.EgressGatewayRanges)
@@ -38,12 +49,11 @@ func TestDeleteEgressGateway(t *testing.T) {
 	database.InitializeDatabase()
 	deleteAllNetworks()
 	createNet()
-	createTestNode()
 	testnode := createTestNode()
 	gateway.Interface = "eth0"
 	gateway.Ranges = []string{"10.100.100.0/24"}
 	gateway.NetID = "skynet"
-	gateway.NodeID = testnode.MacAddress
+	gateway.NodeID = testnode.ID
 	t.Run("Success", func(t *testing.T) {
 		node, err := logic.CreateEgressGateway(gateway)
 		assert.Nil(t, err)
@@ -68,13 +78,8 @@ func TestDeleteEgressGateway(t *testing.T) {
 		node, err := logic.DeleteEgressGateway(gateway.NetID, "01:02:03")
 		assert.EqualError(t, err, "no result found")
 		assert.Equal(t, models.Node{}, node)
+		deleteAllNodes()
 	})
-	t.Run("BadNet", func(t *testing.T) {
-		node, err := logic.DeleteEgressGateway("badnet", gateway.NodeID)
-		assert.EqualError(t, err, "no result found")
-		assert.Equal(t, models.Node{}, node)
-	})
-
 }
 
 func TestGetNetworkNodes(t *testing.T) {
@@ -84,13 +89,12 @@ func TestGetNetworkNodes(t *testing.T) {
 	t.Run("BadNet", func(t *testing.T) {
 		node, err := logic.GetNetworkNodes("badnet")
 		assert.Nil(t, err)
-		assert.Equal(t, []models.Node{}, node)
-		//assert.Equal(t, "mongo: no documents in result", err.Error())
+		assert.Nil(t, node)
 	})
 	t.Run("NoNodes", func(t *testing.T) {
 		node, err := logic.GetNetworkNodes("skynet")
 		assert.Nil(t, err)
-		assert.Equal(t, []models.Node{}, node)
+		assert.Nil(t, node)
 	})
 	t.Run("Success", func(t *testing.T) {
 		createTestNode()
@@ -105,18 +109,13 @@ func TestUncordonNode(t *testing.T) {
 	deleteAllNetworks()
 	createNet()
 	node := createTestNode()
-	t.Run("BadNet", func(t *testing.T) {
-		resp, err := logic.UncordonNode("badnet", node.MacAddress)
-		assert.Equal(t, models.Node{}, resp)
-		assert.EqualError(t, err, "no result found")
-	})
-	t.Run("BadMac", func(t *testing.T) {
-		resp, err := logic.UncordonNode("skynet", "01:02:03")
+	t.Run("BadID", func(t *testing.T) {
+		resp, err := logic.UncordonNode("blahblah")
 		assert.Equal(t, models.Node{}, resp)
 		assert.EqualError(t, err, "no result found")
 	})
 	t.Run("Success", func(t *testing.T) {
-		resp, err := logic.UncordonNode("skynet", node.MacAddress)
+		resp, err := logic.UncordonNode(node.ID)
 		assert.Nil(t, err)
 		assert.Equal(t, "no", resp.IsPending)
 	})
@@ -145,14 +144,11 @@ func TestValidateEgressGateway(t *testing.T) {
 }
 
 func deleteAllNodes() {
-	nodes, _ := logic.GetAllNodes()
-	for _, node := range nodes {
-		logic.DeleteNode(&node, true)
-	}
+	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"}
+	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"}
 	logic.CreateNode(&createnode)
 	return &createnode
 }

+ 14 - 4
controllers/relay.go

@@ -8,6 +8,7 @@ import (
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mq"
 )
 
 func createRelay(w http.ResponseWriter, r *http.Request) {
@@ -20,28 +21,37 @@ func createRelay(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	relay.NetID = params["network"]
-	relay.NodeID = params["macaddress"]
+	relay.NodeID = params["nodeid"]
 	node, err := logic.CreateRelay(relay)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
 	logger.Log(1, r.Header.Get("user"), "created relay on node", relay.NodeID, "on network", relay.NetID)
+	relayedNodes, err := logic.GetNodesByAddress(relay.NetID, relay.RelayAddrs)
+	for _, node := range relayedNodes {
+		err = mq.NodeUpdate(&node)
+		if err != nil {
+			logger.Log(1, "error sending update to relayed node ", node.Address, "on network", relay.NetID, ": ", err.Error())
+		}
+	}
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)
+	runUpdates(&node, true)
 }
 
 func deleteRelay(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	var params = mux.Vars(r)
-	nodeMac := params["macaddress"]
+	nodeid := params["nodeid"]
 	netid := params["network"]
-	node, err := logic.DeleteRelay(netid, nodeMac)
+	node, err := logic.DeleteRelay(netid, nodeid)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
-	logger.Log(1, r.Header.Get("user"), "deleted egress gateway", nodeMac, "on network", netid)
+	logger.Log(1, r.Header.Get("user"), "deleted relay server", nodeid, "on network", netid)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)
+	runUpdates(&node, true)
 }

+ 2 - 4
controllers/server.go

@@ -9,7 +9,6 @@ import (
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
-	"github.com/gravitl/netmaker/serverctl"
 )
 
 func serverHandlers(r *mux.Router) {
@@ -70,9 +69,8 @@ func removeNetwork(w http.ResponseWriter, r *http.Request) {
 	// get params
 	var params = mux.Vars(r)
 
-	success, err := serverctl.RemoveNetwork(params["network"])
-
-	if err != nil || !success {
+	err := logic.DeleteNetwork(params["network"])
+	if err != nil {
 		json.NewEncoder(w).Encode("Could not remove server from network " + params["network"])
 		return
 	}

+ 34 - 0
controllers/server_util.go

@@ -0,0 +1,34 @@
+package controller
+
+import (
+	"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"
+)
+
+func runServerPeerUpdate(node *models.Node, ifaceDelta bool) error {
+
+	err := logic.TimerCheckpoint()
+	if err != nil {
+		logger.Log(3, "error occurred on timer,", err.Error())
+	}
+
+	if err := mq.PublishPeerUpdate(node); err != nil {
+		logger.Log(0, "failed to inform peers of new node ", err.Error())
+	}
+
+	if servercfg.IsClientMode() != "on" {
+		return nil
+	}
+	var currentServerNode, getErr = logic.GetNetworkServerLeader(node.Network)
+	if err != nil {
+		return getErr
+	}
+	if err = logic.ServerUpdate(&currentServerNode, ifaceDelta); err != nil {
+		logger.Log(1, "server node:", currentServerNode.ID, "failed update")
+		return err
+	}
+	return nil
+}

+ 54 - 3
database/database.go

@@ -1,12 +1,17 @@
 package database
 
 import (
+	"crypto/rand"
 	"encoding/json"
 	"errors"
 	"time"
 
+	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/servercfg"
+	"golang.org/x/crypto/nacl/box"
 )
 
 // NETWORKS_TABLE_NAME - networks table
@@ -33,9 +38,15 @@ const INT_CLIENTS_TABLE_NAME = "intclients"
 // PEERS_TABLE_NAME - peers table
 const PEERS_TABLE_NAME = "peers"
 
-// SERVERCONF_TABLE_NAME
+// 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"
 
@@ -105,7 +116,7 @@ func InitializeDatabase() error {
 		time.Sleep(2 * time.Second)
 	}
 	createTables()
-	return nil
+	return initializeUUID()
 }
 
 func createTables() {
@@ -118,6 +129,7 @@ func createTables() {
 	createTable(INT_CLIENTS_TABLE_NAME)
 	createTable(PEERS_TABLE_NAME)
 	createTable(SERVERCONF_TABLE_NAME)
+	createTable(SERVER_UUID_TABLE_NAME)
 	createTable(GENERATED_TABLE_NAME)
 }
 
@@ -128,7 +140,8 @@ func createTable(tableName string) error {
 // IsJSONString - checks if valid json
 func IsJSONString(value string) bool {
 	var jsonInt interface{}
-	return json.Unmarshal([]byte(value), &jsonInt) == nil
+	var nodeInt models.Node
+	return json.Unmarshal([]byte(value), &jsonInt) == nil || json.Unmarshal([]byte(value), &nodeInt) == nil
 }
 
 // Insert - inserts object into db
@@ -184,6 +197,44 @@ func FetchRecords(tableName string) (map[string]string, error) {
 	return getCurrentDB()[FETCH_ALL].(func(string) (map[string]string, error))(tableName)
 }
 
+// initializeUUID - create a UUID record for server if none exists
+func initializeUUID() error {
+	records, err := FetchRecords(SERVER_UUID_TABLE_NAME)
+	if err != nil {
+		if !IsEmptyRecord(err) {
+			return err
+		}
+	} else if len(records) > 0 {
+		return nil
+	}
+	// setup encryption keys
+	var trafficPubKey, trafficPrivKey, errT = box.GenerateKey(rand.Reader) // generate traffic keys
+	if errT != nil {
+		return errT
+	}
+	tPriv, err := ncutils.ConvertKeyToBytes(trafficPrivKey)
+	if err != nil {
+		return err
+	}
+
+	tPub, err := ncutils.ConvertKeyToBytes(trafficPubKey)
+	if err != nil {
+		return err
+	}
+
+	telemetry := models.Telemetry{
+		UUID:           uuid.NewString(),
+		TrafficKeyPriv: tPriv,
+		TrafficKeyPub:  tPub,
+	}
+	telJSON, err := json.Marshal(&telemetry)
+	if err != nil {
+		return err
+	}
+
+	return Insert(SERVER_UUID_RECORD_KEY, string(telJSON), SERVER_UUID_TABLE_NAME)
+}
+
 // CloseDB - closes a database gracefully
 func CloseDB() {
 	getCurrentDB()[CLOSE_DB].(func())()

+ 1 - 1
database/sqlite.go

@@ -30,7 +30,7 @@ var SQLITE_FUNCTIONS = map[string]interface{}{
 func initSqliteDB() error {
 	// == create db file if not present ==
 	if _, err := os.Stat("data"); os.IsNotExist(err) {
-		os.Mkdir("data", 0744)
+		os.Mkdir("data", 0700)
 	}
 	dbFilePath := filepath.Join("data", dbFilename)
 	if _, err := os.Stat(dbFilePath); os.IsNotExist(err) {

+ 0 - 14
defaultvalues.sh

@@ -1,14 +0,0 @@
-#!/bin/bash
-#Source this file if using default mongo settings from readme
-# if i've done my work correctly, this file will be defunct
-#  refer to config folder for new method
-export API_PORT=8081
-export GRPC_PORT=50051
-export MONGO_USER=mongoadmin
-export MONGO_PASS=mongopass
-export MONGO_HOST=localhost
-export MASTER_KEY=c4tsRc001
-export MONGO_PORT=27017
-export MONGO_OPTS='/?authSource=admin'
-export MASTER_TOKEN="mastertoken"
-export CREATE_KEY="newnode123"

+ 39 - 0
docker/Dockerfile-netclient-multiarch

@@ -0,0 +1,39 @@
+FROM gravitl/builder:latest as builder
+# add glib support daemon manager
+WORKDIR /app
+
+COPY . .
+
+ENV GO111MODULE=auto
+
+RUN GOOS=linux GOARCH=arm64 CGO_ENABLED=0 /usr/local/go/bin/go build -ldflags="-w -s" -o netclient-app netclient/main.go
+
+WORKDIR /root/
+
+RUN apk add --update git build-base libmnl-dev iptables
+
+RUN git clone https://git.zx2c4.com/wireguard-go && \
+    cd wireguard-go && \
+    make && \
+    make install
+
+ENV WITH_WGQUICK=yes
+RUN git clone https://git.zx2c4.com/wireguard-tools && \
+    cd wireguard-tools && \
+    cd src && \
+    make && \
+    make install
+
+FROM alpine:3.13.6
+
+WORKDIR /root/
+
+RUN apk add --no-cache --update bash libmnl gcompat iptables openresolv iproute2
+COPY --from=builder /usr/bin/wireguard-go /usr/bin/wg* /usr/bin/
+COPY --from=builder /app/netclient-app ./netclient
+COPY --from=builder /app/scripts/netclient.sh .
+RUN chmod 0755 netclient && chmod 0755 netclient.sh
+
+ENV WG_QUICK_USERSPACE_IMPLEMENTATION=wireguard-go
+
+ENTRYPOINT ["/bin/sh", "./netclient.sh"]

+ 5 - 0
docker/mosquitto.conf

@@ -0,0 +1,5 @@
+persistence true
+
+per_listener_settings true
+listener 1883
+allow_anonymous true

BIN
docs/_build/doctrees/api.doctree


BIN
docs/_build/doctrees/architecture.doctree


BIN
docs/_build/doctrees/client-installation.doctree


BIN
docs/_build/doctrees/environment.pickle


BIN
docs/_build/doctrees/index.doctree


BIN
docs/_build/doctrees/quick-start-nginx.doctree


BIN
docs/_build/doctrees/quick-start.doctree


BIN
docs/_build/doctrees/server-installation.doctree


BIN
docs/_build/doctrees/support.doctree


BIN
docs/_build/doctrees/troubleshoot.doctree


BIN
docs/_build/html/_images/create-user.png


BIN
docs/_build/html/_images/default-net.png


BIN
docs/_build/html/_images/egress4.png


BIN
docs/_build/html/_images/install-server.gif


BIN
docs/_build/html/_images/netcreate.png


BIN
docs/_build/html/_images/nm-diagram.jpg


BIN
docs/_build/html/_images/visit-website.gif


+ 59 - 47
docs/_build/html/_sources/api.rst.txt

@@ -17,8 +17,11 @@ API calls must be authenticated via a header of  the format  `-H "Authorization:
 
 Format of Calls for Curl
 ========================
-Requests take the format of `curl -H "Authorization: Bearer <YOUR_SECRET_KEY>" -H 'Content-Type: application/json' localhost:8081/api/path/to/endpoint`
+Requests take the format of 
 
+.. code-block::
+
+    curl -H "Authorization: Bearer <YOUR_SECRET_KEY>" -H 'Content-Type: application/json' localhost:8081/api/path/to/endpoint
 
 API Documentation
 =================
@@ -41,18 +44,21 @@ Networks API
   
 Networks API Call Examples
 --------------------------  
-  
-**Get All Networks:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks | jq`
 
-**Create Network:** `curl -d '{"addressrange":"10.70.0.0/16","netid":"skynet"}' -H "Authorization: Bearer YOUR_SECRET_KEY" -H 'Content-Type: application/json' localhost:8081/api/networks`
+.. code-block::
+
+    Get All Networks: curl -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks | jq
 
-**Get Network:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet | jq`
+    Create Network: curl -d '{"addressrange":"10.70.0.0/16","netid":"skynet"}' -H "Authorization: Bearer YOUR_SECRET_KEY" -H 'Content-Type: application/json' localhost:8081/api/networks
 
-**Update Network:** `curl -X PUT -d '{"displayname":"my-house"}' -H "Authorization: Bearer YOUR_SECRET_KEY" -H 'Content-Type: application/json' localhost:8081/api/networks/skynet`
+    Get Network: curl -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet | jq
 
-**Delete Network:** `curl -X DELETE -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet`
+    Update Network: curl -X PUT -d '{"displayname":"my-house"}' -H "Authorization: Bearer YOUR_SECRET_KEY" -H 'Content-Type: application/json' localhost:8081/api/networks/skynet
+
+    Delete Network: curl -X DELETE -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet
+
+    Cycle PublicKeys on all Nodes: curl -X POST -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet/keyupdate
 
-**Cycle PublicKeys on all Nodes:** `curl -X POST -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet/keyupdate`
 
 Access Keys API
 ---------------
@@ -66,13 +72,15 @@ Access Keys API
   
 Access Keys API Call Examples
 -----------------------------
-   
-**Get All Keys:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet/keys | jq`
-  
-**Create Key:** `curl -d '{"uses":10,"name":"mykey"}' -H "Authorization: Bearer YOUR_SECRET_KEY" -H 'Content-Type: application/json' localhost:8081/api/networks/skynet/keys`
-  
-**Delete Key:** `curl -X DELETE -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet/keys/mykey`
-  
+
+.. code-block::
+
+    Get All Keys: curl -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet/keys | jq
+    
+    Create Key: curl -d '{"uses":10,"name":"mykey"}' -H "Authorization: Bearer YOUR_SECRET_KEY" -H 'Content-Type: application/json' localhost:8081/api/networks/skynet/keys
+    
+    Delete Key: curl -X DELETE -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet/keys/mykey
+
     
 Nodes API
 ---------
@@ -104,29 +112,31 @@ Nodes API
   
 Nodes API Call Examples
 ----------------------- 
-  
-**Get All Nodes:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/nodes | jq`
-  
-**Get Network Nodes:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/nodes/skynet | jq`
+
+.. code-block::
+
+    Get All Nodes: curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/nodes | jq
     
-**Create Node:** `curl  -d  '{ "endpoint": 100.200.100.200, "publickey": aorijqalrik3ajflaqrdajhkr,"macaddress": "8c:90:b5:06:f1:d9","password": "reallysecret","localaddress": "172.16.16.1","accesskey": "aA3bVG0rnItIRXDx","listenport": 6400}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet`
+    Get Network Nodes: curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/nodes/skynet | jq
+        
+    Create Node: curl  -d  '{ "endpoint": 100.200.100.200, "publickey": aorijqalrik3ajflaqrdajhkr,"macaddress": "8c:90:b5:06:f1:d9","password": "reallysecret","localaddress": "172.16.16.1","accesskey": "aA3bVG0rnItIRXDx","listenport": 6400}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet
+        
+    Get Node: curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/nodes/skynet/{macaddress} | jq  
     
-**Get Node:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/nodes/skynet/{macaddress} | jq`  
-  
-**Update Node:** `curl -X PUT -d '{"name":"laptop1"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9`
-  
-**Delete Node:** `curl -X DELETE -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/skynet/nodes/8c:90:b5:06:f1:d9`
-  
-**Create a Gateway:** `curl  -d  '{ "rangestring": "172.31.0.0/16", "interface": "eth0"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9/creategateway`
-  
-**Delete a Gateway:** `curl -X DELETE -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9/deletegateway`
-  
-**Approve a Pending Node:** `curl -X POST -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9/approve`
-  
-**Get Last Modified Date (Last Modified Node in Network):** `curl -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/adm/skynet/lastmodified`
+    Update Node: curl -X PUT -d '{"name":"laptop1"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9
+    
+    Delete Node: curl -X DELETE -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/skynet/nodes/8c:90:b5:06:f1:d9
+    
+    Create a Gateway: curl  -d  '{ "rangestring": "172.31.0.0/16", "interface": "eth0"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9/creategateway
+    
+    Delete a Gateway: curl -X DELETE -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9/deletegateway
+    
+    Approve a Pending Node: curl -X POST -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9/approve
+    
+    Get Last Modified Date (Last Modified Node in Network): curl -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/adm/skynet/lastmodified
+
+    Authenticate: curl -d  '{"macaddress": "8c:90:b5:06:f1:d9", "password": "YOUR_PASSWORD"}' -H 'Content-Type: application/json' localhost:8081/api/nodes/adm/skynet/authenticate
 
-**Authenticate:** `curl -d  '{"macaddress": "8c:90:b5:06:f1:d9", "password": "YOUR_PASSWORD"}' -H 'Content-Type: application/json' localhost:8081/api/nodes/adm/skynet/authenticate`
-  
 
 Users API
 -----------------------
@@ -148,19 +158,21 @@ Users API
   
 Users API Calls Examples
 ------------------------
-  
-**Get User:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/users/{username} | jq`
 
-**Update User:** `curl -X PUT -d '{"password":"noonewillguessthis"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/users/{username}`
-  
-**Delete User:** `curl -X DELETE -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/users/{username}`
-  
-**Check for Admin User:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/users/adm/hasadmin`
-  
-**Create Admin User:** `curl -d '{ "username": "smartguy", "password": "YOUR_PASS"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/users/adm/createadmin`
-   
-**Authenticate:** `curl -d  '{"username": "smartguy", "password": "YOUR_PASS"}' -H 'Content-Type: application/json' localhost:8081/api/nodes/adm/skynet/authenticate`
-  
+.. code-block::
+
+    Get User: curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/users/{username} | jq
+
+    Update User: curl -X PUT -d '{"password":"noonewillguessthis"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/users/{username}
+    
+    Delete User: curl -X DELETE -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/users/{username}
+    
+    Check for Admin User: curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/users/adm/hasadmin
+    
+    Create Admin User: curl -d '{ "username": "smartguy", "password": "YOUR_PASS"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/users/adm/createadmin
+    
+    Authenticate: curl -d  '{"username": "smartguy", "password": "YOUR_PASS"}' -H 'Content-Type: application/json' localhost:8081/api/nodes/adm/skynet/authenticate
+
 
 Server Management API
 ---------------------

+ 12 - 2
docs/_build/html/_sources/architecture.rst.txt

@@ -47,7 +47,7 @@ Netmaker
 
 Netmaker is a platform built off of WireGuard which enables users to create mesh networks between their devices. Netmaker can create both full and partial mesh networks depending on the use case.
 
-When we refer to Netmaker in aggregate, we are typically referring to Netmaker and the netclient, as well as other supporting services such as CoreDNS, rqlite, and UI webserver.
+When we refer to Netmaker in aggregate, we are typically referring to Netmaker and the netclient, as well as other supporting services such as CoreDNS, rqlite, and UI webserver. There is also almost always a proxy server / LB, which is typically Caddy.
 
 From an end user perspective, they typically interact with the Netmaker UI, or even just run the install script for the netclient on their devices. The other components run in the background invisibly. 
 
@@ -85,6 +85,7 @@ These modes include client mode and dns mode. Either of these can be disabled bu
 
 The Netmaker server interacts with either sqlite (default), postgres, or rqlite, a distributed version of sqlite, as its database. This DB holds information about nodes, networks, users, and other important data. This data is configuration data. For the most part, Netmaker serves configuration data to Nodes, telling them how they should configure themselves. The Netclient is the agent that actually does that configuration.
 
+The components of the server are usually proxied via Caddy, or an alternative like Nginx and Traefik. The proxy handles SSL certificates to secure traffic, and routes to the UI, API, and gRPC server.
 
 Netclient
 ----------------
@@ -124,6 +125,15 @@ CoreDNS
 
 Netmaker allows users to provide and manage Private DNS for their nodes. This requires a nameserver, and CoreDNS is the chosen nameserver. CoreDNS is lightweight and extensible. CoreDNS loads dns settings from a simple file, managed by Netmaker, and serves out DNS info for managed nodes. DNS can be tricky, and DNS management is currently only supported on a small set of devices, specifically those running systemd-resolved. However, the Netmaker CoreDNS instance can be added manually as a nameserver to other devices. DNS mode can also be turned off.
 
+Caddy
+-------
+
+Caddy is the default proxy for Netmaker if you set it up via Quick Start. Caddy is an extremely simple and docker-friendly proxy, which can be compared to Nginx, Traefik, or HAProxy. We use Caddy by default because of the ease of management, and integration with gRPC. A typical setup for Nginx might take dozens of lines of code, and we need to request and manage SSL certificates separately.
+
+Caddy handles all these things automatically in very few lines of code. You can see our default "Caddyfile" here, which is fed to the container and has all the configuration necessary to configure the proxy for our app:
+
+https://github.com/gravitl/netmaker/blob/master/docker/Caddyfile
+
 External Client
 ----------------
 
@@ -182,7 +192,7 @@ The following systems should be operable natively with Netclient in daemon mode:
         - Raspian.
         - Arch
         - CentOS
-        - CoreOS
+        - Fedora CoreOS
 
 To manage DNS (optional), the node must have systemd-resolved. Systems that have this enabled include:
         - Arch

+ 42 - 0
docs/_build/html/_sources/client-installation.rst.txt

@@ -30,6 +30,48 @@ Windows will by default have firewall rules that prevent inbound connections. If
 
 If you want to allow all peers access, but do not want to configure firewall rules for all peers, you can configure access for one peer, and set it as a Relay Server.
 
+Running the install script
+----------------------------
+
+Some file locations have issues running the install script, such as running from the root C:/ folder. Users have noted the following locations work well for running the install powershell script:
+
+- `C:/Program Files/wireguard`
+- `C:/Windows/System32`
+
+Running netclient commands
+----------------------------
+
+If running the netclient manually ("netclient join", "netclient checkin", "netclient pull") it should be run from outside of the installed directory, which will be either:
+
+- `C:/Program Files/netclient`
+- `C:/ProgramData/netclient`
+
+It is better to call it from a different directory.
+
+High CPU Utilization
+--------------------------
+
+With some versions of WireGuard on Windows, high CPU utilization has been found with the netclient. This is typically due to interaction with the WireGuard GUI component (app). If you're experiencing high CPU utilization, close the WireGuard app. WireGuard will still be running, but the CPU usage should go back down to normal.
+
+Notes on OpenWRT
+===========================
+
+Deploying on OpenWRT depends a lot on the version of OpenWRT and the hardware being used. If the primary installer does not work, there are two things you can try:
+
+1. This community-run package for OpenWRT: https://github.com/sbilly/netmaker-openwrt
+
+2. Manual installation:
+
+- download (wget) the netclient package for your hardware from the netclient releases: https://github.com/gravitl/netmaker/releases
+- rename to "netclient"
+- Run as root from a bash shell on OpenWRT
+
+3. You may experience an issue with the length of the token, which has limits on some OpenWRT shells. If you run into this problem, you can use the following script to convert your token into a "netclient join" command:
+
+- `wget https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/token-convert.sh`
+- ./token-convert <token value>
+- Run the output on your OpenWRT machine
+
 Modes and System Compatibility
 ==================================
 

+ 3 - 3
docs/_build/html/_sources/index.rst.txt

@@ -23,7 +23,7 @@ Netmaker is a platform for creating and managing fast, secure, and dynamic virtu
 
 This documentation covers Netmaker's :doc:`installation <./server-installation>`, :doc:`usage <./usage>`, :doc:`troubleshooting <./support>`, and customization, as well as reference documents for the :doc:`API <./api>`, UI and Agent configuration. All of the `source code <https://github.com/gravitl/netmaker>`_ for Netmaker is on GitHub.
 
-**For Kubernetes-specific guidance, please see the** `Netmaker Kubernetes Documentation. <https://nm-k8s.readthedocs.io>`_
+**For Kubernetes-specific guidance, please see the** `Netmaker Kubernetes Documentation. <https://k8s.netmaker.org>`_
 
 About
 --------
@@ -81,9 +81,9 @@ Kubernetes Documentation
 
 .. toctree::
 
-   Kubernetes <https://nm-k8s.readthedocs.io>
+   Kubernetes <https://k8s.netmaker.org>
    
-`Netmaker Kubernetes Documentation <https://nm-k8s.readthedocs.io>`_
+`Netmaker Kubernetes Documentation <https://k8s.netmaker.org>`_
 
 
 Advanced Server Installation

+ 0 - 170
docs/_build/html/_sources/quick-start-nginx.rst.txt

@@ -1,170 +0,0 @@
-==================================
-Install with Nginx (depreciated)
-==================================
-
-This is the old quick start guide, which contains instructions using Nginx and Docker CE. It is recommended to use the new quick start guide with Caddy instead.
-
-0. Introduction
-==================
-
-We assume for this installation that you want all of the Netmaker features enabled, you want your server to be secure, and you want your server to be accessible from anywhere.
-
-This instance will not be HA. However, it should comfortably handle around one hundred concurrent clients and support the most common use cases.
-
-If you are deploying for a business or enterprise use case and this setup will not fit your needs, please contact [email protected], or check out the business subscription plans at https://gravitl.com/plans/business.
-
-By the end of this guide, you will have Netmaker installed on a public VM linked to your custom domain, secured behind an Nginx reverse proxy.
-
-For information about deploying more advanced configurations, see the :doc:`Advanced Installation <./server-installation>` docs. 
-
-
-1. Prerequisites
-==================
--  **Virtual Machine**
-   
-   - Preferably from a cloud provider (e.x: DigitalOcean, Linode, AWS, GCP, etc.)
-      - We do not recommend Oracle Cloud, as VM's here have been known to cause network interference.
-   - Public, static IP 
-   - Min 1GB RAM, 1 CPU (4GB RAM, 2CPU preferred)
-      - Nginx may have performance issues if using a cloud VPS with a single, shared CPU
-   - 2GB+ of storage 
-   - Ubuntu  20.04 Installed
-
-- **Domain**
-
-  - A publicly owned domain (e.x. example.com, mysite.biz) 
-  - Permission and access to modify DNS records via DNS service (e.x: Route53)
-
-2. Install Dependencies
-========================
-
-``ssh root@your-host``
-
-Install Docker
----------------
-Begin by installing the community version of Docker and docker-compose (there are issues with the snap version). You can follow the official `Docker instructions here <https://docs.docker.com/engine/install/>`_. Or, you can use the below series of commands which should work on Ubuntu 20.04.
-
-.. code-block::
-
-  sudo apt-get remove docker docker-engine docker.io containerd runc
-  sudo apt-get update
-  sudo apt-get -y install apt-transport-https ca-certificates curl gnupg lsb-release
-  curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg  
-  echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
-  sudo apt-get update
-  sudo apt-get -y install docker-ce docker-ce-cli containerd.io
-  sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
-  sudo chmod +x /usr/local/bin/docker-compose
-  docker --version
-  docker-compose --version
-
-At this point Docker should be installed.
-
-Install Dependencies
------------------------------
-
-In addition to Docker, this installation requires WireGuard, Nginx, and Certbot.
-
-``sudo apt -y install wireguard wireguard-tools nginx certbot python3-certbot-nginx net-tools``
-
- 
-3. Prepare VM
-===============================
-
-Prepare Domain
-----------------------------
-1. Choose a base domain or subdomain for Netmaker. If you own **example.com**, this should be something like **netmaker.example.com**
-
-- You must point your wildcard domain to the public IP of your VM, e.x: *.example.com --> <your public ip>
-
-2. Add an A record pointing to your VM using your DNS service provider for *.netmaker.example.com (inserting your own subdomain of course).
-3. Netmaker will create three subdomains on top of this. For the example above those subdomains would be:
-
-- dashboard.netmaker.example.com
-
-- api.netmaker.example.com
-
-- grpc.netmaker.example.com
-
-Moving forward we will refer to your base domain using **<your base domain>**. Replace these references with your domain (e.g. netmaker.example.com).
-
-4. ``nslookup host.<your base domain>`` (inserting your domain) should now return the IP of your VM.
-
-5. Generate SSL Certificates using certbot:
-
-``sudo certbot certonly --manual --preferred-challenges=dns --email [email protected] --server https://acme-v02.api.letsencrypt.org/directory --agree-tos --manual-public-ip-logging-ok -d "*.<your base domain>"``
-
-The above command (using your domain instead of <your base domain>), will prompt you to enter a TXT record in your DNS service provider. Do this, and **wait one  minute** before clicking enter, or it may fail and you will have to run the command again.
-
-Prepare Firewall
------------------
-
-Make sure firewall settings are appropriate for Netmaker. You need ports 53 and 443. On the server you can run:
-
-
-.. code-block::
-
-  sudo ufw allow proto tcp from any to any port 443 && sudo ufw allow 53/udp && sudo ufw allow 53/tcp
-
-**Based on your cloud provider, you may also need to set inbound security rules for your server. This will be dependent on your cloud provider. Be sure to check before moving on:**
-  - allow 443/tcp from all
-  - allow 53/udp and 53/tcp from all
-
-In addition to the above ports, you will need to make sure that your cloud's firewall or security groups are opened for the range of ports that Netmaker's WireGuard interfaces consume.
-
-Netmaker will create one interface per network, starting from 51821. So, if you plan on having 5 networks, you will want to have at least 51821-51825 open (udp).
-
-Prepare Nginx
------------------
-
-Nginx will serve the SSL certificate with your chosen domain and forward traffic to netmaker.
-
-Get the nginx configuration file:
-
-``wget https://raw.githubusercontent.com/gravitl/netmaker/master/nginx/netmaker-nginx-template.conf``
-
-Insert your domain in the configuration file and add to nginx:
-
-.. code-block::
-
-  sed -i 's/NETMAKER_BASE_DOMAIN/<your base domain>/g' netmaker-nginx-template.conf
-  sudo cp netmaker-nginx-template.conf /etc/nginx/conf.d/<your base domain>.conf
-  nginx -t && nginx -s reload
-  systemctl restart nginx
-
-4. Install Netmaker
-====================
-
-Prepare Templates
-------------------
-
-**Note on COREDNS_IP:** Depending on your cloud provider, the public IP may not be bound directly to the VM on which you are running. In such cases, CoreDNS cannot bind to this IP, and you should use the IP of the default interface on your machine in place of COREDNS_IP. If the public IP **is** bound to the VM, you can simply use the same IP as SERVER_PUBLIC_IP.
-
-.. code-block::
-
-  wget https://raw.githubusercontent.com/gravitl/netmaker/master/compose/docker-compose.yml
-  sed -i 's/NETMAKER_BASE_DOMAIN/<your base domain>/g' docker-compose.yml
-  sed -i 's/SERVER_PUBLIC_IP/<your server ip>/g' docker-compose.yml
-  sed -i 's/COREDNS_IP/<your server ip>/g' docker-compose.yml
-
-Generate a unique master key and insert it:
-
-.. code-block::
-
-  tr -dc A-Za-z0-9 </dev/urandom | head -c 30 ; echo ''
-  sed -i 's/REPLACE_MASTER_KEY/<your generated key>/g' docker-compose.yml
-
-You may want to save this key for future use with the API.
-
-Start Netmaker
-----------------
-
-``sudo docker-compose -f docker-compose.yml up -d``
-
-navigate to dashboard.<your base domain> to log into the UI.
-
-To troubleshoot issues, start with:
-
-``docker logs netmaker``
-
-Or check out the :doc:`troubleshoooting docs <./troubleshoot>`.

+ 1 - 1
docs/_build/html/_sources/quick-start.rst.txt

@@ -44,7 +44,7 @@ For information about deploying more advanced configurations, see the :doc:`Adva
 1. Prepare DNS
 ================
 
-Create a wildcard A record pointing to the public IP of your VM. As an example, *.netmaker.example.com.
+Create a wildcard A record pointing to the public IP of your VM. As an example, \*.netmaker.example.com.
 
 Caddy will create 3 subdomains with this wildcard, EX:
 

+ 58 - 8
docs/_build/html/_sources/server-installation.rst.txt

@@ -138,8 +138,31 @@ CLIENT_MODE:
 
     **Description:** Specifies if server should deploy itself as a node (client) in each network. May be turned to "off" for more restricted servers.
 
+RCE:  
+    **Default:** "off"
+
+    **Description:** The server enables you to set PostUp and PostDown commands for nodes, which is standard for WireGuard with wg-quick, but is also **Remote Code Execution**, which is a critical vulnerability if the server is exploited. Because of this, it's turned off by default, but if turned on, PostUp and PostDown become editable.
+
+SERVER_GRPC_WIREGUARD
+    **Depreciated:** no longer in use
+
+DISPLAY_KEYS
+    **Default:** "on"
+
+    **Description:** If "on", will allow you to always show the key values of "access keys". This could be considered a vulnerability, so if turned "off", will only display key values once, and it is up to the users to store the key values locally.
+
+NODE_ID
+    **Default:** <system mac addres>
+
+    **Description:** This setting is used for HA configurations of the server, to identify between different servers. Nodes are given ID's like netmaker-1, netmaker-2, and netmaker-3. If the server is not HA, there is no reason to set this field.
+
+TELEMETRY
+    **Default:** "on"
+
+    **Description:** If "on", the server will send anonymous telemetry data once daily, which is used to improve the product. Data sent includes counts (integer values) for the number of nodes, types of nodes, users, and networks. It also sends the version of the server.
+
 Config File Reference
-----------------------
+-----------------------
 A config file may be placed under config/environments/<env-name>.yml. To read this file at runtime, provide the environment variable NETMAKER_ENV at runtime. For instance, dev.yml paired with ENV=dev. Netmaker will load the specified Config file. This allows you to store and manage configurations for different environments. Below is a reference Config File you may use.
 
 .. literalinclude:: ../config/environments/dev.yaml
@@ -153,6 +176,20 @@ All environment variables and options are enabled in this file. It is the equiva
 .. literalinclude:: ../compose/docker-compose.reference.yml
   :language: YAML
 
+Available docker-compose files
+---------------------------------
+
+The default options for docker-compose can be found here: https://github.com/gravitl/netmaker/tree/master/compose
+
+The following is a brief description of each:
+
+- `docker-compose.contained.yml <https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.contained.yml>`_ - This is the default docker-compose, used in the quick start and deployment script in the README on GitHub. It deploys Netmaker with all options included (Caddy and CoreDNS) and has "self-contained" netclients, meaning they do not affect host networking.
+- `docker-compose.coredns.yml <https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.coredns.yml>`_ - This is a simple compose used to spin up a standalone CoreDNS server. Can be useful if, for instance, you are unning Netmaker on baremetal but need CoreDNS.
+- `docker-compose.hostnetwork.yml <https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.hostnetwork.yml>`_ - This is similar to the docker-compose.contained.yml but with a key difference: it has advanced permissions and mounts host volumes to control networking on the host level.
+- `docker-compose.nocaddy.yml <https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.nocaddy.yml>`_ -= This is the same as docker-compose.contained.yml but without Caddy, in case you need to use a different proxy like Nginx, Traefik, or HAProxy.
+- `docker-compose.nodns.yml <https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.nodns.yml>`_ - This is the same as docker-compose.contained.yml but without CoreDNS, in which case you will not have the Private DNS feature.
+- `docker-compose.reference.yml <https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.reference.yml>`_ - This is the same as docker-compose.contained.yml but with all variable options on display and annotated (it's what we show right above this section). Use this to determine which variables you should add or change in your configuration.
+- `docker-compose.yml <https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.yml>`_ - This is a renamed docker-compose.contained.yml. It is meant only to act as a placeholder for what we consider the "primary" docker-compose that users should work with.
 
 DNS Mode Setup
 ====================================
@@ -224,18 +261,21 @@ This template is equivalent but omits CoreDNS.
 Linux Install without Docker
 =============================
 
-Most systems support Docker, but some do not. In such environments, there are many options for installing Netmaker. Netmaker is available as a binary file, and there is a zip file of the Netmaker UI static HTML on GitHub. Beyond the UI and Server, you need to install MongoDB and CoreDNS (optional). 
-
-To start, we recommend following the Nginx instructions in the :doc:`Quick Install <./quick-start>` guide to enable SSL for your environment.
+Most systems support Docker, but some do not. In such environments, there are many options for installing Netmaker. Netmaker is available as a binary file, and there is a zip file of the Netmaker UI static HTML on GitHub. Beyond the UI and Server, you may want to optionally install a database (sqlite is embedded, rqlite or postgres are supported) and CoreDNS (also optional). 
 
 Once this is enabled and configured for a domain, you can continue with the below. The recommended server runs Ubuntu 20.04.
 
-rqlite Setup
-----------------
+Database Setup (optional)
+--------------------------
+
+You can run the netmaker binary standalone and it will run an embedded sqlite server. Data goes in the data/ directory. Optionally, you can run PostgreSQL or rqlite. Instructions for rqlite are below.
+
 1. Install rqlite on your server: https://github.com/rqlite/rqlite
 
 2. Run rqlite: rqlited -node-id 1 ~/node.1
 
+If using rqlite or postgres, you must change the DATABASE environment/config variable and enter connection details.
+
 Server Setup
 -------------
 1. **Run the install script:** 
@@ -265,8 +305,18 @@ The following uses Nginx as an http server. You may alternatively use Apache or
   sudo sh -c 'BACKEND_URL=http://<YOUR BACKEND API URL>:PORT /usr/share/nginx/html/generate_config_js.sh >/usr/share/nginx/html/config.js'
   sudo systemctl start nginx
 
-CoreDNS Setup
-----------------
+CoreDNS Setup (optional)
+----------------------------
+
+CoreDNS is only required if you want private DNS features. Once installed, you must set the CoreDNS variables in the env settings of the server.
+
+See https://coredns.io/manual/toc/#installation
+
+Proxy / Load Balancer
+------------------------
+
+You will need to proxy connections to your UI and Server. By default the ports are 8081, 8082, and 50051 (grpc). This proxy should handle SSL certificates. We recommend Caddy or Nginx (you can follow the Nginx guide in these docs). The proxy must be able to handle gRPC connections.
+
 
 .. _KubeInstall:
 

+ 27 - 0
docs/_build/html/_sources/support.rst.txt

@@ -39,6 +39,33 @@ We believe the SSPL lets most people run the project the way they want, for both
 
 If you believe the SSPL will negatively impact your ability to use the project, please do not hesitate to reach out.
 
+Telemetry
+==============
+
+As of v0.10.0, Netmaker collects "opt-out" telemetry data. To opt out, simply set "TELEMETRY=off" in your docker-compose file.
+
+Please consider participating in telemetry, as it helps us focus on the features and bug fixes which are most useful to users. Netmaker is a broad platform, and without this data, it is difficult to know where the team should spend its limited resources.
+
+The following is the full list of telemetry data we collect. Besides "Server Version" all data is simply an integer count:
+
+- Randomized server ID
+- Count of nodes
+- Count of "non-server" nodes
+- Count of external clients
+- Count of networks
+- Count of users
+- Count of linux nodes
+- Count of freebsd nodes
+- Count of macos nodes
+- Count of windows nodes
+- Count of docker nodes
+- Count of k8s nodes
+- Server version
+
+We use  `PostHog <https://https://posthog.com/>`_, an open source and trusted framework for telemetry data.
+
+To look at exactly we collect telemetry, you can view the source code under serverctl/telemetry.go: https://github.com/gravitl/netmaker/blob/master/serverctl/telemetry.go
+
 Contact
 ===========
 If you need help, try the discord or open a GitHub ticket.

+ 24 - 0
docs/_build/html/_sources/troubleshoot.rst.txt

@@ -29,6 +29,13 @@ Common Issues
 Server
 -------
 
+**How do I use a private address from the Netmaker Server? How do I contact nodes using their private addresses from the server?**
+  Default nodes appear in each network with the "netmaker" name. These nodes are created by, and attached to, the server. The server is contained in docker, meaning these clients are also contained in docker. Their networking stack is also contained in docker. The "netmaker" nodes are meant to function as network utilities. They assist with UDP Hole Punching and can run Relays, Egress, and Ingress. However, they are meant to stay contained in the server. They do not touch the host networking stack.
+
+  If you want to give the physical server / VM a private IP in the netmaker network, you must deploy an **additional** node using the standard netclient. The only note here is that the server consumes ports 51821-51831, so you will need to give it a port outside this range, e.x. `./netclient join <token> --port 51835`.
+
+  One a netclient is deployed to the underlying server/VM, you will be able to use the private address to reach other nodes from the host, or to reach the server over the private network.
+
 **I upgraded from 0.7 to 0.8 and now I dont have any data in my server!**
   In 0.8, sqlite becomes the default database. If you were running with rqlite, you must set the DATABASE environment variable to rqlite in order to continue using rqlite.
 
@@ -105,6 +112,23 @@ Netclient
 **I have a hard to reach machine behind a firewall or a corporate NAT, what can I do?**
   In this situation you can use the Relay Server functionality introduced in Netmaker v0.8 to designate a node as a relay to your "stuck" machine. Simply click the button to make a node into a relay and tell it to relay traffic to this hard-to-reach peer. 
 
+**I am unable to run the netclient on my OpenWRT machine, what's wrong?**
+  Deploying on OpenWRT depends a lot on the version of OpenWRT and the hardware being used. If the primary installer does not work, there are two things you can try:
+
+  1. This community-run package for OpenWRT: https://github.com/sbilly/netmaker-openwrt
+
+  2. Manual installation:
+
+  - download (wget) the netclient package for your hardware from the netclient releases: https://github.com/gravitl/netmaker/releases
+  - rename to "netclient"
+  - Run as root from a bash shell on OpenWRT
+
+  3. You may experience an issue with the length of the token, which has limits on some OpenWRT shells. If you run into this problem, you can use the following script to convert your token into a "netclient join" command:
+
+  - `wget https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/token-convert.sh`
+  - ./token-convert <token value>
+  - Run the output on your OpenWRT machine
+
 
 CoreDNS
 --------

+ 1 - 1
docs/_build/html/about.html

@@ -306,7 +306,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>

+ 75 - 35
docs/_build/html/api.html

@@ -209,7 +209,7 @@
     <li class="md-nav__item">
     
     
-      <a href="about.html" class="md-nav__link">1. About</a>
+      <a href="about.html" class="md-nav__link">About</a>
       
     
     </li>
@@ -244,49 +244,56 @@
     <li class="md-nav__item">
     
     
-      <a href="server-installation.html" class="md-nav__link">Advanced Server Installation</a>
+      <a href="external-clients.html" class="md-nav__link">Ingress + External Clients</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="oauth.html" class="md-nav__link">Integrating OAuth</a>
+      <a href="egress-gateway.html" class="md-nav__link">Egress Gateway</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="client-installation.html" class="md-nav__link">Client Installation</a>
+      <a href="relay-server.html" class="md-nav__link">Relay Servers</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="external-clients.html" class="md-nav__link">Ingress GW + External Clients</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="egress-gateway.html" class="md-nav__link">Egress Gateway</a>
+      <a href="server-installation.html" class="md-nav__link">Advanced Server Installation</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="relay-server.html" class="md-nav__link">Relay Servers</a>
+      <a href="client-installation.html" class="md-nav__link">Advanced Client Installation</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="usage.html" class="md-nav__link">Using Netmaker</a>
+      <a href="oauth.html" class="md-nav__link">Integrating OAuth</a>
+      
+    
+    </li>
+    <li class="md-nav__item">
+    
+    
+      <a href="usage.html" class="md-nav__link">External Guides</a>
       
     
     </li>
@@ -482,7 +489,10 @@
 
 
 <h2 id="format-of-calls-for-curl">Format of Calls for Curl<a class="headerlink" href="#format-of-calls-for-curl" title="Permalink to this headline">¶</a></h2>
-<p>Requests take the format of <cite>curl -H “Authorization: Bearer &lt;YOUR_SECRET_KEY&gt;” -H ‘Content-Type: application/json’ localhost:8081/api/path/to/endpoint</cite></p>
+<p>Requests take the format of</p>
+<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">curl</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"Authorization: Bearer &lt;YOUR_SECRET_KEY&gt;"</span> <span class="o">-</span><span class="n">H</span> <span class="s1">'Content-Type: application/json'</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">path</span><span class="o">/</span><span class="n">to</span><span class="o">/</span><span class="n">endpoint</span>
+</pre></div>
+</div>
 
 
 <h2 id="api-documentation">API Documentation<a class="headerlink" href="#api-documentation" title="Permalink to this headline">¶</a></h2>
@@ -497,12 +507,19 @@
 
 
 <h3 id="networks-api-call-examples">Networks API Call Examples<a class="headerlink" href="#networks-api-call-examples" title="Permalink to this headline">¶</a></h3>
-<p><strong>Get All Networks:</strong> <cite>curl -H “Authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/networks | jq</cite></p>
-<p><strong>Create Network:</strong> <cite>curl -d ‘{“addressrange”:”10.70.0.0/16”,”netid”:”skynet”}’ -H “Authorization: Bearer YOUR_SECRET_KEY” -H ‘Content-Type: application/json’ localhost:8081/api/networks</cite></p>
-<p><strong>Get Network:</strong> <cite>curl -H “Authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/networks/skynet | jq</cite></p>
-<p><strong>Update Network:</strong> <cite>curl -X PUT -d ‘{“displayname”:”my-house”}’ -H “Authorization: Bearer YOUR_SECRET_KEY” -H ‘Content-Type: application/json’ localhost:8081/api/networks/skynet</cite></p>
-<p><strong>Delete Network:</strong> <cite>curl -X DELETE -H “Authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/networks/skynet</cite></p>
-<p><strong>Cycle PublicKeys on all Nodes:</strong> <cite>curl -X POST -H “Authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/networks/skynet/keyupdate</cite></p>
+<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Get</span> <span class="n">All</span> <span class="n">Networks</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"Authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">networks</span> <span class="o">|</span> <span class="n">jq</span>
+
+<span class="n">Create</span> <span class="n">Network</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">d</span> <span class="s1">'{"addressrange":"10.70.0.0/16","netid":"skynet"}'</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"Authorization: Bearer YOUR_SECRET_KEY"</span> <span class="o">-</span><span class="n">H</span> <span class="s1">'Content-Type: application/json'</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">networks</span>
+
+<span class="n">Get</span> <span class="n">Network</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"Authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">networks</span><span class="o">/</span><span class="n">skynet</span> <span class="o">|</span> <span class="n">jq</span>
+
+<span class="n">Update</span> <span class="n">Network</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">X</span> <span class="n">PUT</span> <span class="o">-</span><span class="n">d</span> <span class="s1">'{"displayname":"my-house"}'</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"Authorization: Bearer YOUR_SECRET_KEY"</span> <span class="o">-</span><span class="n">H</span> <span class="s1">'Content-Type: application/json'</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">networks</span><span class="o">/</span><span class="n">skynet</span>
+
+<span class="n">Delete</span> <span class="n">Network</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">X</span> <span class="n">DELETE</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"Authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">networks</span><span class="o">/</span><span class="n">skynet</span>
+
+<span class="n">Cycle</span> <span class="n">PublicKeys</span> <span class="n">on</span> <span class="nb">all</span> <span class="n">Nodes</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">X</span> <span class="n">POST</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"Authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">networks</span><span class="o">/</span><span class="n">skynet</span><span class="o">/</span><span class="n">keyupdate</span>
+</pre></div>
+</div>
 
 
 <h3 id="access-keys-api">Access Keys API<a class="headerlink" href="#access-keys-api" title="Permalink to this headline">¶</a></h3>
@@ -512,9 +529,13 @@
 
 
 <h3 id="access-keys-api-call-examples">Access Keys API Call Examples<a class="headerlink" href="#access-keys-api-call-examples" title="Permalink to this headline">¶</a></h3>
-<p><strong>Get All Keys:</strong> <cite>curl -H “Authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/networks/skynet/keys | jq</cite></p>
-<p><strong>Create Key:</strong> <cite>curl -d ‘{“uses”:10,”name”:”mykey”}’ -H “Authorization: Bearer YOUR_SECRET_KEY” -H ‘Content-Type: application/json’ localhost:8081/api/networks/skynet/keys</cite></p>
-<p><strong>Delete Key:</strong> <cite>curl -X DELETE -H “Authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/networks/skynet/keys/mykey</cite></p>
+<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Get</span> <span class="n">All</span> <span class="n">Keys</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"Authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">networks</span><span class="o">/</span><span class="n">skynet</span><span class="o">/</span><span class="n">keys</span> <span class="o">|</span> <span class="n">jq</span>
+
+<span class="n">Create</span> <span class="n">Key</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">d</span> <span class="s1">'{"uses":10,"name":"mykey"}'</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"Authorization: Bearer YOUR_SECRET_KEY"</span> <span class="o">-</span><span class="n">H</span> <span class="s1">'Content-Type: application/json'</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">networks</span><span class="o">/</span><span class="n">skynet</span><span class="o">/</span><span class="n">keys</span>
+
+<span class="n">Delete</span> <span class="n">Key</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">X</span> <span class="n">DELETE</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"Authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">networks</span><span class="o">/</span><span class="n">skynet</span><span class="o">/</span><span class="n">keys</span><span class="o">/</span><span class="n">mykey</span>
+</pre></div>
+</div>
 
 
 <h3 id="nodes-api">Nodes API<a class="headerlink" href="#nodes-api" title="Permalink to this headline">¶</a></h3>
@@ -533,17 +554,29 @@
 
 
 <h3 id="nodes-api-call-examples">Nodes API Call Examples<a class="headerlink" href="#nodes-api-call-examples" title="Permalink to this headline">¶</a></h3>
-<p><strong>Get All Nodes:</strong> <cite>curl -H “Authorization: Bearer YOUR_SECRET_KEY” http://localhost:8081/api/nodes | jq</cite></p>
-<p><strong>Get Network Nodes:</strong> <cite>curl -H “Authorization: Bearer YOUR_SECRET_KEY” http://localhost:8081/api/nodes/skynet | jq</cite></p>
-<p><strong>Create Node:</strong> <cite>curl  -d  ‘{ “endpoint”: 100.200.100.200, “publickey”: aorijqalrik3ajflaqrdajhkr,”macaddress”: “8c:90:b5:06:f1:d9”,”password”: “reallysecret”,”localaddress”: “172.16.16.1”,”accesskey”: “aA3bVG0rnItIRXDx”,”listenport”: 6400}’ -H ‘Content-Type: application/json’ -H “authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/nodes/skynet</cite></p>
-<p><strong>Get Node:</strong> <cite>curl -H “Authorization: Bearer YOUR_SECRET_KEY” http://localhost:8081/api/nodes/skynet/{macaddress} | jq</cite></p>
-<p><strong>Update Node:</strong> <cite>curl -X PUT -d ‘{“name”:”laptop1”}’ -H ‘Content-Type: application/json’ -H “authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9</cite></p>
-<p><strong>Delete Node:</strong> <cite>curl -X DELETE -H “authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/skynet/nodes/8c:90:b5:06:f1:d9</cite></p>
-<p><strong>Create a Gateway:</strong> <cite>curl  -d  ‘{ “rangestring”: “172.31.0.0/16”, “interface”: “eth0”}’ -H ‘Content-Type: application/json’ -H “authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9/creategateway</cite></p>
-<p><strong>Delete a Gateway:</strong> <cite>curl -X DELETE -H “authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9/deletegateway</cite></p>
-<p><strong>Approve a Pending Node:</strong> <cite>curl -X POST -H “authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9/approve</cite></p>
-<p><strong>Get Last Modified Date (Last Modified Node in Network):</strong> <cite>curl -H “authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/nodes/adm/skynet/lastmodified</cite></p>
-<p><strong>Authenticate:</strong> <cite>curl -d  ‘{“macaddress”: “8c:90:b5:06:f1:d9”, “password”: “YOUR_PASSWORD”}’ -H ‘Content-Type: application/json’ localhost:8081/api/nodes/adm/skynet/authenticate</cite></p>
+<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Get</span> <span class="n">All</span> <span class="n">Nodes</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"Authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">nodes</span> <span class="o">|</span> <span class="n">jq</span>
+
+<span class="n">Get</span> <span class="n">Network</span> <span class="n">Nodes</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"Authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">nodes</span><span class="o">/</span><span class="n">skynet</span> <span class="o">|</span> <span class="n">jq</span>
+
+<span class="n">Create</span> <span class="n">Node</span><span class="p">:</span> <span class="n">curl</span>  <span class="o">-</span><span class="n">d</span>  <span class="s1">'{ "endpoint": 100.200.100.200, "publickey": aorijqalrik3ajflaqrdajhkr,"macaddress": "8c:90:b5:06:f1:d9","password": "reallysecret","localaddress": "172.16.16.1","accesskey": "aA3bVG0rnItIRXDx","listenport": 6400}'</span> <span class="o">-</span><span class="n">H</span> <span class="s1">'Content-Type: application/json'</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">nodes</span><span class="o">/</span><span class="n">skynet</span>
+
+<span class="n">Get</span> <span class="n">Node</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"Authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">nodes</span><span class="o">/</span><span class="n">skynet</span><span class="o">/</span><span class="p">{</span><span class="n">macaddress</span><span class="p">}</span> <span class="o">|</span> <span class="n">jq</span>
+
+<span class="n">Update</span> <span class="n">Node</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">X</span> <span class="n">PUT</span> <span class="o">-</span><span class="n">d</span> <span class="s1">'{"name":"laptop1"}'</span> <span class="o">-</span><span class="n">H</span> <span class="s1">'Content-Type: application/json'</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">nodes</span><span class="o">/</span><span class="n">skynet</span><span class="o">/</span><span class="mi">8</span><span class="n">c</span><span class="p">:</span><span class="mi">90</span><span class="p">:</span><span class="n">b5</span><span class="p">:</span><span class="mi">06</span><span class="p">:</span><span class="n">f1</span><span class="p">:</span><span class="n">d9</span>
+
+<span class="n">Delete</span> <span class="n">Node</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">X</span> <span class="n">DELETE</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">skynet</span><span class="o">/</span><span class="n">nodes</span><span class="o">/</span><span class="mi">8</span><span class="n">c</span><span class="p">:</span><span class="mi">90</span><span class="p">:</span><span class="n">b5</span><span class="p">:</span><span class="mi">06</span><span class="p">:</span><span class="n">f1</span><span class="p">:</span><span class="n">d9</span>
+
+<span class="n">Create</span> <span class="n">a</span> <span class="n">Gateway</span><span class="p">:</span> <span class="n">curl</span>  <span class="o">-</span><span class="n">d</span>  <span class="s1">'{ "rangestring": "172.31.0.0/16", "interface": "eth0"}'</span> <span class="o">-</span><span class="n">H</span> <span class="s1">'Content-Type: application/json'</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">nodes</span><span class="o">/</span><span class="n">skynet</span><span class="o">/</span><span class="mi">8</span><span class="n">c</span><span class="p">:</span><span class="mi">90</span><span class="p">:</span><span class="n">b5</span><span class="p">:</span><span class="mi">06</span><span class="p">:</span><span class="n">f1</span><span class="p">:</span><span class="n">d9</span><span class="o">/</span><span class="n">creategateway</span>
+
+<span class="n">Delete</span> <span class="n">a</span> <span class="n">Gateway</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">X</span> <span class="n">DELETE</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">nodes</span><span class="o">/</span><span class="n">skynet</span><span class="o">/</span><span class="mi">8</span><span class="n">c</span><span class="p">:</span><span class="mi">90</span><span class="p">:</span><span class="n">b5</span><span class="p">:</span><span class="mi">06</span><span class="p">:</span><span class="n">f1</span><span class="p">:</span><span class="n">d9</span><span class="o">/</span><span class="n">deletegateway</span>
+
+<span class="n">Approve</span> <span class="n">a</span> <span class="n">Pending</span> <span class="n">Node</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">X</span> <span class="n">POST</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">nodes</span><span class="o">/</span><span class="n">skynet</span><span class="o">/</span><span class="mi">8</span><span class="n">c</span><span class="p">:</span><span class="mi">90</span><span class="p">:</span><span class="n">b5</span><span class="p">:</span><span class="mi">06</span><span class="p">:</span><span class="n">f1</span><span class="p">:</span><span class="n">d9</span><span class="o">/</span><span class="n">approve</span>
+
+<span class="n">Get</span> <span class="n">Last</span> <span class="n">Modified</span> <span class="n">Date</span> <span class="p">(</span><span class="n">Last</span> <span class="n">Modified</span> <span class="n">Node</span> <span class="ow">in</span> <span class="n">Network</span><span class="p">):</span> <span class="n">curl</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">nodes</span><span class="o">/</span><span class="n">adm</span><span class="o">/</span><span class="n">skynet</span><span class="o">/</span><span class="n">lastmodified</span>
+
+<span class="n">Authenticate</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">d</span>  <span class="s1">'{"macaddress": "8c:90:b5:06:f1:d9", "password": "YOUR_PASSWORD"}'</span> <span class="o">-</span><span class="n">H</span> <span class="s1">'Content-Type: application/json'</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">nodes</span><span class="o">/</span><span class="n">adm</span><span class="o">/</span><span class="n">skynet</span><span class="o">/</span><span class="n">authenticate</span>
+</pre></div>
+</div>
 
 
 <h3 id="users-api">Users API<a class="headerlink" href="#users-api" title="Permalink to this headline">¶</a></h3>
@@ -557,12 +590,19 @@
 
 
 <h3 id="users-api-calls-examples">Users API Calls Examples<a class="headerlink" href="#users-api-calls-examples" title="Permalink to this headline">¶</a></h3>
-<p><strong>Get User:</strong> <cite>curl -H “Authorization: Bearer YOUR_SECRET_KEY” http://localhost:8081/api/users/{username} | jq</cite></p>
-<p><strong>Update User:</strong> <cite>curl -X PUT -d ‘{“password”:”noonewillguessthis”}’ -H ‘Content-Type: application/json’ -H “authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/users/{username}</cite></p>
-<p><strong>Delete User:</strong> <cite>curl -X DELETE -H “authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/users/{username}</cite></p>
-<p><strong>Check for Admin User:</strong> <cite>curl -H “Authorization: Bearer YOUR_SECRET_KEY” http://localhost:8081/api/users/adm/hasadmin</cite></p>
-<p><strong>Create Admin User:</strong> <cite>curl -d ‘{ “username”: “smartguy”, “password”: “YOUR_PASS”}’ -H ‘Content-Type: application/json’ -H “authorization: Bearer YOUR_SECRET_KEY” localhost:8081/api/users/adm/createadmin</cite></p>
-<p><strong>Authenticate:</strong> <cite>curl -d  ‘{“username”: “smartguy”, “password”: “YOUR_PASS”}’ -H ‘Content-Type: application/json’ localhost:8081/api/nodes/adm/skynet/authenticate</cite></p>
+<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Get</span> <span class="n">User</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"Authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">users</span><span class="o">/</span><span class="p">{</span><span class="n">username</span><span class="p">}</span> <span class="o">|</span> <span class="n">jq</span>
+
+<span class="n">Update</span> <span class="n">User</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">X</span> <span class="n">PUT</span> <span class="o">-</span><span class="n">d</span> <span class="s1">'{"password":"noonewillguessthis"}'</span> <span class="o">-</span><span class="n">H</span> <span class="s1">'Content-Type: application/json'</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">users</span><span class="o">/</span><span class="p">{</span><span class="n">username</span><span class="p">}</span>
+
+<span class="n">Delete</span> <span class="n">User</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">X</span> <span class="n">DELETE</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">users</span><span class="o">/</span><span class="p">{</span><span class="n">username</span><span class="p">}</span>
+
+<span class="n">Check</span> <span class="k">for</span> <span class="n">Admin</span> <span class="n">User</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"Authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">users</span><span class="o">/</span><span class="n">adm</span><span class="o">/</span><span class="n">hasadmin</span>
+
+<span class="n">Create</span> <span class="n">Admin</span> <span class="n">User</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">d</span> <span class="s1">'{ "username": "smartguy", "password": "YOUR_PASS"}'</span> <span class="o">-</span><span class="n">H</span> <span class="s1">'Content-Type: application/json'</span> <span class="o">-</span><span class="n">H</span> <span class="s2">"authorization: Bearer YOUR_SECRET_KEY"</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">users</span><span class="o">/</span><span class="n">adm</span><span class="o">/</span><span class="n">createadmin</span>
+
+<span class="n">Authenticate</span><span class="p">:</span> <span class="n">curl</span> <span class="o">-</span><span class="n">d</span>  <span class="s1">'{"username": "smartguy", "password": "YOUR_PASS"}'</span> <span class="o">-</span><span class="n">H</span> <span class="s1">'Content-Type: application/json'</span> <span class="n">localhost</span><span class="p">:</span><span class="mi">8081</span><span class="o">/</span><span class="n">api</span><span class="o">/</span><span class="n">nodes</span><span class="o">/</span><span class="n">adm</span><span class="o">/</span><span class="n">skynet</span><span class="o">/</span><span class="n">authenticate</span>
+</pre></div>
+</div>
 
 
 <h3 id="server-management-api">Server Management API<a class="headerlink" href="#server-management-api" title="Permalink to this headline">¶</a></h3>

+ 14 - 3
docs/_build/html/architecture.html

@@ -253,6 +253,8 @@
         </li>
         <li class="md-nav__item"><a href="#coredns" class="md-nav__link">CoreDNS</a>
         </li>
+        <li class="md-nav__item"><a href="#caddy" class="md-nav__link">Caddy</a>
+        </li>
         <li class="md-nav__item"><a href="#external-client" class="md-nav__link">External Client</a>
         </li></ul>
             </nav>
@@ -350,7 +352,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>
@@ -473,6 +475,8 @@
         </li>
         <li class="md-nav__item"><a href="#coredns" class="md-nav__link">CoreDNS</a>
         </li>
+        <li class="md-nav__item"><a href="#caddy" class="md-nav__link">Caddy</a>
+        </li>
         <li class="md-nav__item"><a href="#external-client" class="md-nav__link">External Client</a>
         </li></ul>
             </nav>
@@ -518,7 +522,7 @@
 
 <h3 id="netmaker">Netmaker<a class="headerlink" href="#netmaker" title="Permalink to this headline">¶</a></h3>
 <p>Netmaker is a platform built off of WireGuard which enables users to create mesh networks between their devices. Netmaker can create both full and partial mesh networks depending on the use case.</p>
-<p>When we refer to Netmaker in aggregate, we are typically referring to Netmaker and the netclient, as well as other supporting services such as CoreDNS, rqlite, and UI webserver.</p>
+<p>When we refer to Netmaker in aggregate, we are typically referring to Netmaker and the netclient, as well as other supporting services such as CoreDNS, rqlite, and UI webserver. There is also almost always a proxy server / LB, which is typically Caddy.</p>
 <p>From an end user perspective, they typically interact with the Netmaker UI, or even just run the install script for the netclient on their devices. The other components run in the background invisibly.</p>
 <p>Netmaker does a lot of work to set configurations for you, so that you don’t have to. This includes things like WireGuard ports, endpoints, public IPs, keys, and peers. Netmaker works to abstract away as much of the network management as possible, so that you can just click to create a network, and click to add a machine to a network. That said, every machine (node) is different, and may require special configuration. That is why, while Netmaker sets practical default settings, everything within Netmaker is fully configurable.</p>
 
@@ -543,6 +547,7 @@
 <p>Most server settings are configurable via a config file, or by environment variables (which take precedence). If the server finds neither of these, it sets sensible defaults, including things like the server’s reachable IP, ports, and which “modes” to run in.</p>
 <p>These modes include client mode and dns mode. Either of these can be disabled but are enabled by default. Client mode allows you to treat the Netmaker host machine (operating system) as a network Node, installing the netclient and controlling the host network. DNS mode has the server write config settings for CoreDNS, a separate component and nameserver, which picks up the config settings to manage node DNS.</p>
 <p>The Netmaker server interacts with either sqlite (default), postgres, or rqlite, a distributed version of sqlite, as its database. This DB holds information about nodes, networks, users, and other important data. This data is configuration data. For the most part, Netmaker serves configuration data to Nodes, telling them how they should configure themselves. The Netclient is the agent that actually does that configuration.</p>
+<p>The components of the server are usually proxied via Caddy, or an alternative like Nginx and Traefik. The proxy handles SSL certificates to secure traffic, and routes to the UI, API, and gRPC server.</p>
 
 
 <h3 id="netclient">Netclient<a class="headerlink" href="#netclient" title="Permalink to this headline">¶</a></h3>
@@ -569,6 +574,12 @@
 <p>Netmaker allows users to provide and manage Private DNS for their nodes. This requires a nameserver, and CoreDNS is the chosen nameserver. CoreDNS is lightweight and extensible. CoreDNS loads dns settings from a simple file, managed by Netmaker, and serves out DNS info for managed nodes. DNS can be tricky, and DNS management is currently only supported on a small set of devices, specifically those running systemd-resolved. However, the Netmaker CoreDNS instance can be added manually as a nameserver to other devices. DNS mode can also be turned off.</p>
 
 
+<h3 id="caddy">Caddy<a class="headerlink" href="#caddy" title="Permalink to this headline">¶</a></h3>
+<p>Caddy is the default proxy for Netmaker if you set it up via Quick Start. Caddy is an extremely simple and docker-friendly proxy, which can be compared to Nginx, Traefik, or HAProxy. We use Caddy by default because of the ease of management, and integration with gRPC. A typical setup for Nginx might take dozens of lines of code, and we need to request and manage SSL certificates separately.</p>
+<p>Caddy handles all these things automatically in very few lines of code. You can see our default “Caddyfile” here, which is fed to the container and has all the configuration necessary to configure the proxy for our app:</p>
+<p><a class="reference external" href="https://github.com/gravitl/netmaker/blob/master/docker/Caddyfile">https://github.com/gravitl/netmaker/blob/master/docker/Caddyfile</a></p>
+
+
 <h3 id="external-client">External Client<a class="headerlink" href="#external-client" title="Permalink to this headline">¶</a></h3>
 <p>The external client is simply a manually configured WireGuard connection to your network, which Netmaker helps to manage.</p>
 <p>Most machines can run WireGuard. It is fairly simple to set up a WireGuard connection to a single endpoint. It is setting up mesh networks and other topologies like site-to-site which becomes complicated.</p>
@@ -619,7 +630,7 @@
 <li><p>Raspian.</p></li>
 <li><p>Arch</p></li>
 <li><p>CentOS</p></li>
-<li><p>CoreOS</p></li>
+<li><p>Fedora CoreOS</p></li>
 </ul>
 </dd>
 <dt>To manage DNS (optional), the node must have systemd-resolved. Systems that have this enabled include:</dt><dd><ul class="simple">

+ 72 - 3
docs/_build/html/client-installation.html

@@ -265,7 +265,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>
@@ -292,7 +292,17 @@
               <ul class="md-nav__list">
         <li class="md-nav__item"><a href="#introduction-to-netclient" class="md-nav__link">Introduction to Netclient</a>
         </li>
-        <li class="md-nav__item"><a href="#notes-on-windows" class="md-nav__link">Notes on Windows</a>
+        <li class="md-nav__item"><a href="#notes-on-windows" class="md-nav__link">Notes on Windows</a><nav class="md-nav">
+              <ul class="md-nav__list">
+        <li class="md-nav__item"><a href="#running-the-install-script" class="md-nav__link">Running the install script</a>
+        </li>
+        <li class="md-nav__item"><a href="#running-netclient-commands" class="md-nav__link">Running netclient commands</a>
+        </li>
+        <li class="md-nav__item"><a href="#high-cpu-utilization" class="md-nav__link">High CPU Utilization</a>
+        </li></ul>
+            </nav>
+        </li>
+        <li class="md-nav__item"><a href="#notes-on-openwrt" class="md-nav__link">Notes on OpenWRT</a>
         </li>
         <li class="md-nav__item"><a href="#modes-and-system-compatibility" class="md-nav__link">Modes and System Compatibility</a><nav class="md-nav">
               <ul class="md-nav__list">
@@ -346,6 +356,13 @@
       <a href="#notes-on-windows" class="md-nav__link">Notes on Windows</a>
       
     
+    </li>
+    <li class="md-nav__item">
+    
+    
+      <a href="#notes-on-openwrt" class="md-nav__link">Notes on OpenWRT</a>
+      
+    
     </li>
     <li class="md-nav__item">
     
@@ -465,7 +482,17 @@
               <ul class="md-nav__list">
         <li class="md-nav__item"><a href="#introduction-to-netclient" class="md-nav__link">Introduction to Netclient</a>
         </li>
-        <li class="md-nav__item"><a href="#notes-on-windows" class="md-nav__link">Notes on Windows</a>
+        <li class="md-nav__item"><a href="#notes-on-windows" class="md-nav__link">Notes on Windows</a><nav class="md-nav">
+              <ul class="md-nav__list">
+        <li class="md-nav__item"><a href="#running-the-install-script" class="md-nav__link">Running the install script</a>
+        </li>
+        <li class="md-nav__item"><a href="#running-netclient-commands" class="md-nav__link">Running netclient commands</a>
+        </li>
+        <li class="md-nav__item"><a href="#high-cpu-utilization" class="md-nav__link">High CPU Utilization</a>
+        </li></ul>
+            </nav>
+        </li>
+        <li class="md-nav__item"><a href="#notes-on-openwrt" class="md-nav__link">Notes on OpenWRT</a>
         </li>
         <li class="md-nav__item"><a href="#modes-and-system-compatibility" class="md-nav__link">Modes and System Compatibility</a><nav class="md-nav">
               <ul class="md-nav__list">
@@ -531,6 +558,48 @@
 <p><code class="docutils literal notranslate"><span class="pre">netsh</span> <span class="pre">advfirewall</span> <span class="pre">firewall</span> <span class="pre">add</span> <span class="pre">rule</span> <span class="pre">name="Allow</span> <span class="pre">from</span> <span class="pre">&lt;peer</span> <span class="pre">private</span> <span class="pre">addr&gt;"</span> <span class="pre">dir=in</span> <span class="pre">action=allow</span> <span class="pre">protocol=ANY</span> <span class="pre">remoteip=&lt;peer</span> <span class="pre">private</span> <span class="pre">addr&gt;</span></code></p>
 <p>If you want to allow all peers access, but do not want to configure firewall rules for all peers, you can configure access for one peer, and set it as a Relay Server.</p>
 
+<h3 id="running-the-install-script">Running the install script<a class="headerlink" href="#running-the-install-script" title="Permalink to this headline">¶</a></h3>
+<p>Some file locations have issues running the install script, such as running from the root C:/ folder. Users have noted the following locations work well for running the install powershell script:</p>
+<ul class="simple">
+<li><p><cite>C:/Program Files/wireguard</cite></p></li>
+<li><p><cite>C:/Windows/System32</cite></p></li>
+</ul>
+
+
+<h3 id="running-netclient-commands">Running netclient commands<a class="headerlink" href="#running-netclient-commands" title="Permalink to this headline">¶</a></h3>
+<p>If running the netclient manually (“netclient join”, “netclient checkin”, “netclient pull”) it should be run from outside of the installed directory, which will be either:</p>
+<ul class="simple">
+<li><p><cite>C:/Program Files/netclient</cite></p></li>
+<li><p><cite>C:/ProgramData/netclient</cite></p></li>
+</ul>
+<p>It is better to call it from a different directory.</p>
+
+
+<h3 id="high-cpu-utilization">High CPU Utilization<a class="headerlink" href="#high-cpu-utilization" title="Permalink to this headline">¶</a></h3>
+<p>With some versions of WireGuard on Windows, high CPU utilization has been found with the netclient. This is typically due to interaction with the WireGuard GUI component (app). If you’re experiencing high CPU utilization, close the WireGuard app. WireGuard will still be running, but the CPU usage should go back down to normal.</p>
+
+
+
+<h2 id="notes-on-openwrt">Notes on OpenWRT<a class="headerlink" href="#notes-on-openwrt" title="Permalink to this headline">¶</a></h2>
+<p>Deploying on OpenWRT depends a lot on the version of OpenWRT and the hardware being used. If the primary installer does not work, there are two things you can try:</p>
+<ol class="arabic simple">
+<li><p>This community-run package for OpenWRT: <a class="reference external" href="https://github.com/sbilly/netmaker-openwrt">https://github.com/sbilly/netmaker-openwrt</a></p></li>
+<li><p>Manual installation:</p></li>
+</ol>
+<ul class="simple">
+<li><p>download (wget) the netclient package for your hardware from the netclient releases: <a class="reference external" href="https://github.com/gravitl/netmaker/releases">https://github.com/gravitl/netmaker/releases</a></p></li>
+<li><p>rename to “netclient”</p></li>
+<li><p>Run as root from a bash shell on OpenWRT</p></li>
+</ul>
+<ol class="arabic simple" start="3">
+<li><p>You may experience an issue with the length of the token, which has limits on some OpenWRT shells. If you run into this problem, you can use the following script to convert your token into a “netclient join” command:</p></li>
+</ol>
+<ul class="simple">
+<li><p><cite>wget https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/token-convert.sh</cite></p></li>
+<li><p>./token-convert &lt;token value&gt;</p></li>
+<li><p>Run the output on your OpenWRT machine</p></li>
+</ul>
+
 
 <h2 id="modes-and-system-compatibility">Modes and System Compatibility<a class="headerlink" href="#modes-and-system-compatibility" title="Permalink to this headline">¶</a></h2>
 <p><strong>Note: If you would like to connect non-Linux/Unix machines to your network such as phones and Windows desktops, please see the documentation on External Clients</strong></p>

+ 15 - 8
docs/_build/html/conduct.html

@@ -209,7 +209,7 @@
     <li class="md-nav__item">
     
     
-      <a href="about.html" class="md-nav__link">1. About</a>
+      <a href="about.html" class="md-nav__link">About</a>
       
     
     </li>
@@ -244,49 +244,56 @@
     <li class="md-nav__item">
     
     
-      <a href="server-installation.html" class="md-nav__link">Advanced Server Installation</a>
+      <a href="external-clients.html" class="md-nav__link">Ingress + External Clients</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="oauth.html" class="md-nav__link">Integrating OAuth</a>
+      <a href="egress-gateway.html" class="md-nav__link">Egress Gateway</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="client-installation.html" class="md-nav__link">Client Installation</a>
+      <a href="relay-server.html" class="md-nav__link">Relay Servers</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="external-clients.html" class="md-nav__link">Ingress GW + External Clients</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="egress-gateway.html" class="md-nav__link">Egress Gateway</a>
+      <a href="server-installation.html" class="md-nav__link">Advanced Server Installation</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="relay-server.html" class="md-nav__link">Relay Servers</a>
+      <a href="client-installation.html" class="md-nav__link">Advanced Client Installation</a>
+      
+    
+    </li>
+    <li class="md-nav__item">
+    
+    
+      <a href="oauth.html" class="md-nav__link">Integrating OAuth</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="usage.html" class="md-nav__link">Using Netmaker</a>
+      <a href="usage.html" class="md-nav__link">External Guides</a>
       
     
     </li>

+ 1 - 1
docs/_build/html/egress-gateway.html

@@ -312,7 +312,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>

+ 1 - 1
docs/_build/html/external-clients.html

@@ -315,7 +315,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>

+ 1 - 1
docs/_build/html/genindex.html

@@ -263,7 +263,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>

+ 18 - 11
docs/_build/html/getting-started.html

@@ -56,7 +56,7 @@
     <link rel="author" title="About these documents" href="about.html" />
     <link rel="index" title="Index" href="genindex.html" />
     <link rel="search" title="Search" href="search.html" />
-    <link rel="next" title="Advanced Server Installation" href="server-installation.html" />
+    <link rel="next" title="Ingress + External Clients" href="external-clients.html" />
     <link rel="prev" title="Quick Install" href="quick-start.html" />
   
    
@@ -209,7 +209,7 @@
     <li class="md-nav__item">
     
     
-      <a href="about.html" class="md-nav__link">1. About</a>
+      <a href="about.html" class="md-nav__link">About</a>
       
     
     </li>
@@ -325,49 +325,56 @@
     <li class="md-nav__item">
     
     
-      <a href="server-installation.html" class="md-nav__link">Advanced Server Installation</a>
+      <a href="external-clients.html" class="md-nav__link">Ingress + External Clients</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="oauth.html" class="md-nav__link">Integrating OAuth</a>
+      <a href="egress-gateway.html" class="md-nav__link">Egress Gateway</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="client-installation.html" class="md-nav__link">Client Installation</a>
+      <a href="relay-server.html" class="md-nav__link">Relay Servers</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="external-clients.html" class="md-nav__link">Ingress GW + External Clients</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="egress-gateway.html" class="md-nav__link">Egress Gateway</a>
+      <a href="server-installation.html" class="md-nav__link">Advanced Server Installation</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="relay-server.html" class="md-nav__link">Relay Servers</a>
+      <a href="client-installation.html" class="md-nav__link">Advanced Client Installation</a>
+      
+    
+    </li>
+    <li class="md-nav__item">
+    
+    
+      <a href="oauth.html" class="md-nav__link">Integrating OAuth</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="usage.html" class="md-nav__link">Using Netmaker</a>
+      <a href="usage.html" class="md-nav__link">External Guides</a>
       
     
     </li>
@@ -578,12 +585,12 @@
             </a>
           
           
-            <a href="server-installation.html" title="Advanced Server Installation"
+            <a href="external-clients.html" title="Ingress + External Clients"
                class="md-flex md-footer-nav__link md-footer-nav__link--next"
                rel="next">
             <div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title"><span
                 class="md-flex__ellipsis"> <span
-                class="md-footer-nav__direction"> Next </span> Advanced Server Installation </span>
+                class="md-footer-nav__direction"> Next </span> Ingress + External Clients </span>
             </div>
             <div class="md-flex__cell md-flex__cell--shrink"><i
                 class="md-icon md-icon--arrow-forward md-footer-nav__button"></i>

+ 6 - 4
docs/_build/html/index.html

@@ -264,7 +264,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>
@@ -408,7 +408,7 @@
 <h1 id="index--page-root">Welcome to the Netmaker Documentation<a class="headerlink" href="#index--page-root" title="Permalink to this headline">¶</a></h1>
 <p>Netmaker is a platform for creating and managing fast, secure, and dynamic virtual overlay networks using WireGuard.</p>
 <p>This documentation covers Netmaker’s <a class="reference internal" href="server-installation.html"><span class="doc">installation</span></a>, <a class="reference internal" href="usage.html"><span class="doc">usage</span></a>, <a class="reference internal" href="support.html"><span class="doc">troubleshooting</span></a>, and customization, as well as reference documents for the <a class="reference internal" href="api.html"><span class="doc">API</span></a>, UI and Agent configuration. All of the <a class="reference external" href="https://github.com/gravitl/netmaker">source code</a> for Netmaker is on GitHub.</p>
-<p><strong>For Kubernetes-specific guidance, please see the</strong> <a class="reference external" href="https://nm-k8s.readthedocs.io">Netmaker Kubernetes Documentation.</a></p>
+<p><strong>For Kubernetes-specific guidance, please see the</strong> <a class="reference external" href="https://k8s.netmaker.org">Netmaker Kubernetes Documentation.</a></p>
 
 <h2 id="about">About<a class="headerlink" href="#about" title="Permalink to this headline">¶</a></h2>
 <p>High-level information about what Netmaker is and how it works.</p>
@@ -499,10 +499,10 @@
 <h2 id="kubernetes-documentation">Kubernetes Documentation<a class="headerlink" href="#kubernetes-documentation" title="Permalink to this headline">¶</a></h2>
 <div class="toctree-wrapper compound">
 <ul>
-<li class="toctree-l1"><a class="reference external" href="https://nm-k8s.readthedocs.io">Kubernetes</a></li>
+<li class="toctree-l1"><a class="reference external" href="https://k8s.netmaker.org">Kubernetes</a></li>
 </ul>
 </div>
-<p><a class="reference external" href="https://nm-k8s.readthedocs.io">Netmaker Kubernetes Documentation</a></p>
+<p><a class="reference external" href="https://k8s.netmaker.org">Netmaker Kubernetes Documentation</a></p>
 
 
 <h2 id="advanced-server-installation">Advanced Server Installation<a class="headerlink" href="#advanced-server-installation" title="Permalink to this headline">¶</a></h2>
@@ -532,6 +532,7 @@
 <li class="toctree-l1"><a class="reference internal" href="client-installation.html">Advanced Client Installation</a><ul>
 <li class="toctree-l2"><a class="reference internal" href="client-installation.html#introduction-to-netclient">Introduction to Netclient</a></li>
 <li class="toctree-l2"><a class="reference internal" href="client-installation.html#notes-on-windows">Notes on Windows</a></li>
+<li class="toctree-l2"><a class="reference internal" href="client-installation.html#notes-on-openwrt">Notes on OpenWRT</a></li>
 <li class="toctree-l2"><a class="reference internal" href="client-installation.html#modes-and-system-compatibility">Modes and System Compatibility</a></li>
 <li class="toctree-l2"><a class="reference internal" href="client-installation.html#prerequisites">Prerequisites</a></li>
 <li class="toctree-l2"><a class="reference internal" href="client-installation.html#configuration">Configuration</a></li>
@@ -628,6 +629,7 @@
 <ul>
 <li class="toctree-l1"><a class="reference internal" href="support.html">Support</a><ul>
 <li class="toctree-l2"><a class="reference internal" href="support.html#faq">FAQ</a></li>
+<li class="toctree-l2"><a class="reference internal" href="support.html#telemetry">Telemetry</a></li>
 <li class="toctree-l2"><a class="reference internal" href="support.html#contact">Contact</a></li>
 </ul>
 </li>

+ 1 - 1
docs/_build/html/install.html

@@ -274,7 +274,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>

+ 15 - 8
docs/_build/html/license.html

@@ -208,7 +208,7 @@
     <li class="md-nav__item">
     
     
-      <a href="about.html" class="md-nav__link">1. About</a>
+      <a href="about.html" class="md-nav__link">About</a>
       
     
     </li>
@@ -243,49 +243,56 @@
     <li class="md-nav__item">
     
     
-      <a href="server-installation.html" class="md-nav__link">Advanced Server Installation</a>
+      <a href="external-clients.html" class="md-nav__link">Ingress + External Clients</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="oauth.html" class="md-nav__link">Integrating OAuth</a>
+      <a href="egress-gateway.html" class="md-nav__link">Egress Gateway</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="client-installation.html" class="md-nav__link">Client Installation</a>
+      <a href="relay-server.html" class="md-nav__link">Relay Servers</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="external-clients.html" class="md-nav__link">Ingress GW + External Clients</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="egress-gateway.html" class="md-nav__link">Egress Gateway</a>
+      <a href="server-installation.html" class="md-nav__link">Advanced Server Installation</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="relay-server.html" class="md-nav__link">Relay Servers</a>
+      <a href="client-installation.html" class="md-nav__link">Advanced Client Installation</a>
+      
+    
+    </li>
+    <li class="md-nav__item">
+    
+    
+      <a href="oauth.html" class="md-nav__link">Integrating OAuth</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="usage.html" class="md-nav__link">Using Netmaker</a>
+      <a href="usage.html" class="md-nav__link">External Guides</a>
       
     
     </li>

+ 1 - 1
docs/_build/html/oauth.html

@@ -265,7 +265,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>

+ 0 - 1112
docs/_build/html/quick-start-nginx.html

@@ -1,1112 +0,0 @@
-
-<!DOCTYPE html>
-
-<html>
-  <head>
-    <meta charset="utf-8" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-  <meta name="viewport" content="width=device-width,initial-scale=1">
-  <meta http-equiv="x-ua-compatible" content="ie=edge">
-  <meta name="lang:clipboard.copy" content="Copy to clipboard">
-  <meta name="lang:clipboard.copied" content="Copied to clipboard">
-  <meta name="lang:search.language" content="en">
-  <meta name="lang:search.pipeline.stopwords" content="True">
-  <meta name="lang:search.pipeline.trimmer" content="True">
-  <meta name="lang:search.result.none" content="No matching documents">
-  <meta name="lang:search.result.one" content="1 matching document">
-  <meta name="lang:search.result.other" content="# matching documents">
-  <meta name="lang:search.tokenizer" content="[\s\-]+">
-
-  
-    <link href="https://fonts.gstatic.com/" rel="preconnect" crossorigin>
-    <link href="https://fonts.googleapis.com/css?family=Roboto+Mono:400,500,700|Roboto:300,400,400i,700&display=fallback" rel="stylesheet">
-
-    <style>
-      body,
-      input {
-        font-family: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif
-      }
-
-      code,
-      kbd,
-      pre {
-        font-family: "Roboto Mono", "Courier New", Courier, monospace
-      }
-    </style>
-  
-
-  <link rel="stylesheet" href="_static/stylesheets/application.css"/>
-  <link rel="stylesheet" href="_static/stylesheets/application-palette.css"/>
-  <link rel="stylesheet" href="_static/stylesheets/application-fixes.css"/>
-  
-  <link rel="stylesheet" href="_static/fonts/material-icons.css"/>
-  
-  <meta name="theme-color" content="#3f51b5">
-  <script src="_static/javascripts/modernizr.js"></script>
-  
-  
-  
-    <title>Install with Nginx (depreciated) &#8212; Netmaker 0.9.4 documentation</title>
-    <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
-    <link rel="stylesheet" type="text/css" href="_static/material.css" />
-    <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
-    <script src="_static/jquery.js"></script>
-    <script src="_static/underscore.js"></script>
-    <script src="_static/doctools.js"></script>
-    <link rel="author" title="About these documents" href="about.html" />
-    <link rel="index" title="Index" href="genindex.html" />
-    <link rel="search" title="Search" href="search.html" />
-    <link rel="next" title="Advanced Server Installation" href="server-installation.html" />
-    <link rel="prev" title="Getting Started" href="getting-started.html" />
-  
-   
-
-  </head>
-  <body dir=ltr
-        data-md-color-primary=indigo data-md-color-accent=light-blue>
-  
-  <svg class="md-svg">
-    <defs data-children-count="0">
-      
-      <svg xmlns="http://www.w3.org/2000/svg" width="416" height="448" viewBox="0 0 416 448" id="__github"><path fill="currentColor" d="M160 304q0 10-3.125 20.5t-10.75 19T128 352t-18.125-8.5-10.75-19T96 304t3.125-20.5 10.75-19T128 256t18.125 8.5 10.75 19T160 304zm160 0q0 10-3.125 20.5t-10.75 19T288 352t-18.125-8.5-10.75-19T256 304t3.125-20.5 10.75-19T288 256t18.125 8.5 10.75 19T320 304zm40 0q0-30-17.25-51T296 232q-10.25 0-48.75 5.25Q229.5 240 208 240t-39.25-2.75Q130.75 232 120 232q-29.5 0-46.75 21T56 304q0 22 8 38.375t20.25 25.75 30.5 15 35 7.375 37.25 1.75h42q20.5 0 37.25-1.75t35-7.375 30.5-15 20.25-25.75T360 304zm56-44q0 51.75-15.25 82.75-9.5 19.25-26.375 33.25t-35.25 21.5-42.5 11.875-42.875 5.5T212 416q-19.5 0-35.5-.75t-36.875-3.125-38.125-7.5-34.25-12.875T37 371.5t-21.5-28.75Q0 312 0 260q0-59.25 34-99-6.75-20.5-6.75-42.5 0-29 12.75-54.5 27 0 47.5 9.875t47.25 30.875Q171.5 96 212 96q37 0 70 8 26.25-20.5 46.75-30.25T376 64q12.75 25.5 12.75 54.5 0 21.75-6.75 42 34 40 34 99.5z"/></svg>
-      
-    </defs>
-  </svg>
-  
-  <input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer">
-  <input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search">
-  <label class="md-overlay" data-md-component="overlay" for="__drawer"></label>
-  <a href="#quick-start-nginx" tabindex="1" class="md-skip"> Skip to content </a>
-  <header class="md-header" data-md-component="header">
-  <nav class="md-header-nav md-grid">
-    <div class="md-flex navheader">
-      <div class="md-flex__cell md-flex__cell--shrink">
-        <a href="index.html" title="Netmaker 0.9.4 documentation"
-           class="md-header-nav__button md-logo">
-          
-            <i class="md-icon">&#xe869</i>
-          
-        </a>
-      </div>
-      <div class="md-flex__cell md-flex__cell--shrink">
-        <label class="md-icon md-icon--menu md-header-nav__button" for="__drawer"></label>
-      </div>
-      <div class="md-flex__cell md-flex__cell--stretch">
-        <div class="md-flex__ellipsis md-header-nav__title" data-md-component="title">
-          <span class="md-header-nav__topic">Netmaker Docs</span>
-          <span class="md-header-nav__topic"> Install with Nginx (depreciated) </span>
-        </div>
-      </div>
-      <div class="md-flex__cell md-flex__cell--shrink">
-        <label class="md-icon md-icon--search md-header-nav__button" for="__search"></label>
-        
-<div class="md-search" data-md-component="search" role="dialog">
-  <label class="md-search__overlay" for="__search"></label>
-  <div class="md-search__inner" role="search">
-    <form class="md-search__form" action="search.html" method="get" name="search">
-      <input type="text" class="md-search__input" name="q" placeholder="Search"
-             autocapitalize="off" autocomplete="off" spellcheck="false"
-             data-md-component="query" data-md-state="active">
-      <label class="md-icon md-search__icon" for="__search"></label>
-      <button type="reset" class="md-icon md-search__icon" data-md-component="reset" tabindex="-1">
-        &#xE5CD;
-      </button>
-    </form>
-    <div class="md-search__output">
-      <div class="md-search__scrollwrap" data-md-scrollfix>
-        <div class="md-search-result" data-md-component="result">
-          <div class="md-search-result__meta">
-            Type to start searching
-          </div>
-          <ol class="md-search-result__list"></ol>
-        </div>
-      </div>
-    </div>
-  </div>
-</div>
-
-      </div>
-      
-        <div class="md-flex__cell md-flex__cell--shrink">
-          <div class="md-header-nav__source">
-            <a href="https://github.com/gravitl/netmaker/" title="Go to repository" class="md-source" data-md-source="github">
-
-    <div class="md-source__icon">
-      <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="28" height="28">
-        <use xlink:href="#__github" width="24" height="24"></use>
-      </svg>
-    </div>
-  
-  <div class="md-source__repository">
-    Netmaker
-  </div>
-</a>
-          </div>
-        </div>
-      
-      
-  
-  <script src="_static/javascripts/version_dropdown.js"></script>
-  <script>
-    var json_loc = ""versions.json"",
-        target_loc = "../",
-        text = "Versions";
-    $( document ).ready( add_version_dropdown(json_loc, target_loc, text));
-  </script>
-  
-
-    </div>
-  </nav>
-</header>
-
-  
-  <div class="md-container">
-    
-    
-    
-  <nav class="md-tabs" data-md-component="tabs">
-    <div class="md-tabs__inner md-grid">
-      <ul class="md-tabs__list">
-          <li class="md-tabs__item"><a href="index.html" class="md-tabs__link">Netmaker 0.9.4 documentation</a></li>
-      </ul>
-    </div>
-  </nav>
-    <main class="md-main">
-      <div class="md-main__inner md-grid" data-md-component="container">
-        
-          <div class="md-sidebar md-sidebar--primary" data-md-component="navigation">
-            <div class="md-sidebar__scrollwrap">
-              <div class="md-sidebar__inner">
-                <nav class="md-nav md-nav--primary" data-md-level="0">
-  <label class="md-nav__title md-nav__title--site" for="__drawer">
-    <a href="index.html" title="Netmaker 0.9.4 documentation" class="md-nav__button md-logo">
-      
-        <i class="md-icon">&#xe869</i>
-      
-    </a>
-    <a href="index.html"
-       title="Netmaker 0.9.4 documentation">Netmaker Docs</a>
-  </label>
-    <div class="md-nav__source">
-      <a href="https://github.com/gravitl/netmaker/" title="Go to repository" class="md-source" data-md-source="github">
-
-    <div class="md-source__icon">
-      <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="28" height="28">
-        <use xlink:href="#__github" width="24" height="24"></use>
-      </svg>
-    </div>
-  
-  <div class="md-source__repository">
-    Netmaker
-  </div>
-</a>
-    </div>
-  
-  
-
-  
-  <ul class="md-nav__list">
-    <li class="md-nav__item">
-    
-    
-      <a href="about.html" class="md-nav__link">About</a>
-      <ul class="md-nav__list"> 
-    <li class="md-nav__item">
-    
-    
-      <a href="about.html#what-is-netmaker" class="md-nav__link">What is Netmaker?</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="about.html#how-does-netmaker-work" class="md-nav__link">How Does Netmaker Work?</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="about.html#use-cases-for-netmaker" class="md-nav__link">Use Cases for Netmaker</a>
-      
-    
-    </li></ul>
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="architecture.html" class="md-nav__link">Architecture</a>
-      <ul class="md-nav__list"> 
-    <li class="md-nav__item">
-    
-    
-      <a href="architecture.html#core-concepts" class="md-nav__link">Core Concepts</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="architecture.html#components" class="md-nav__link">Components</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="architecture.html#technical-process" class="md-nav__link">Technical Process</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="architecture.html#compatible-systems-for-netclient" class="md-nav__link">Compatible Systems for Netclient</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="architecture.html#limitations" class="md-nav__link">Limitations</a>
-      
-    
-    </li></ul>
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="install.html" class="md-nav__link">Install</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="quick-start.html" class="md-nav__link">Quick Install</a>
-      <ul class="md-nav__list"> 
-    <li class="md-nav__item">
-    
-    
-      <a href="quick-start.html#introduction" class="md-nav__link">Introduction</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="quick-start.html#prerequisites" class="md-nav__link">0. Prerequisites</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="quick-start.html#prepare-dns" class="md-nav__link">1. Prepare DNS</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="quick-start.html#install-dependencies" class="md-nav__link">2. Install Dependencies</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="quick-start.html#open-firewall" class="md-nav__link">3. Open Firewall</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="quick-start.html#install-netmaker" class="md-nav__link">4. Install Netmaker</a>
-      
-    
-    </li></ul>
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="getting-started.html" class="md-nav__link">Getting Started</a>
-      <ul class="md-nav__list"> 
-    <li class="md-nav__item">
-    
-    
-      <a href="getting-started.html#setup" class="md-nav__link">Setup</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="getting-started.html#create-a-network" class="md-nav__link">Create a Network</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="getting-started.html#create-a-key" class="md-nav__link">Create a Key</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="getting-started.html#deploy-nodes" class="md-nav__link">Deploy Nodes</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="getting-started.html#manage-nodes" class="md-nav__link">Manage Nodes</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="getting-started.html#uninstalling-the-netclient" class="md-nav__link">Uninstalling the netclient</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="getting-started.html#uninstalling-netmaker" class="md-nav__link">Uninstalling Netmaker</a>
-      
-    
-    </li></ul>
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-    <input class="md-toggle md-nav__toggle" data-md-toggle="toc" type="checkbox" id="__toc">
-    <label class="md-nav__link md-nav__link--active" for="__toc"> Install with Nginx (depreciated) </label>
-    
-      <a href="#" class="md-nav__link md-nav__link--active">Install with Nginx (depreciated)</a>
-      
-        
-<nav class="md-nav md-nav--secondary">
-    <label class="md-nav__title" for="__toc">Contents</label>
-  <ul class="md-nav__list" data-md-scrollfix="">
-        <li class="md-nav__item"><a href="#quick-start-nginx--page-root" class="md-nav__link">Install with Nginx (depreciated)</a><nav class="md-nav">
-              <ul class="md-nav__list">
-        <li class="md-nav__item"><a href="#introduction" class="md-nav__link">0. Introduction</a>
-        </li>
-        <li class="md-nav__item"><a href="#prerequisites" class="md-nav__link">1. Prerequisites</a>
-        </li>
-        <li class="md-nav__item"><a href="#install-dependencies" class="md-nav__link">2. Install Dependencies</a><nav class="md-nav">
-              <ul class="md-nav__list">
-        <li class="md-nav__item"><a href="#install-docker" class="md-nav__link">Install Docker</a>
-        </li>
-        <li class="md-nav__item"><a href="#id1" class="md-nav__link">Install Dependencies</a>
-        </li></ul>
-            </nav>
-        </li>
-        <li class="md-nav__item"><a href="#prepare-vm" class="md-nav__link">3. Prepare VM</a><nav class="md-nav">
-              <ul class="md-nav__list">
-        <li class="md-nav__item"><a href="#prepare-domain" class="md-nav__link">Prepare Domain</a>
-        </li>
-        <li class="md-nav__item"><a href="#prepare-firewall" class="md-nav__link">Prepare Firewall</a>
-        </li>
-        <li class="md-nav__item"><a href="#prepare-nginx" class="md-nav__link">Prepare Nginx</a>
-        </li></ul>
-            </nav>
-        </li>
-        <li class="md-nav__item"><a href="#install-netmaker" class="md-nav__link">4. Install Netmaker</a><nav class="md-nav">
-              <ul class="md-nav__list">
-        <li class="md-nav__item"><a href="#prepare-templates" class="md-nav__link">Prepare Templates</a>
-        </li>
-        <li class="md-nav__item"><a href="#start-netmaker" class="md-nav__link">Start Netmaker</a>
-        </li></ul>
-            </nav>
-        </li></ul>
-            </nav>
-        </li>
-  </ul>
-</nav>
-      <ul class="md-nav__list"> 
-    <li class="md-nav__item">
-    
-    
-      <a href="#introduction" class="md-nav__link">0. Introduction</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="#prerequisites" class="md-nav__link">1. Prerequisites</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="#install-dependencies" class="md-nav__link">2. Install Dependencies</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="#prepare-vm" class="md-nav__link">3. Prepare VM</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="#install-netmaker" class="md-nav__link">4. Install Netmaker</a>
-      
-    
-    </li></ul>
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="server-installation.html" class="md-nav__link">Advanced Server Installation</a>
-      <ul class="md-nav__list"> 
-    <li class="md-nav__item">
-    
-    
-      <a href="server-installation.html#system-compatibility" class="md-nav__link">System Compatibility</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="server-installation.html#server-configuration-reference" class="md-nav__link">Server Configuration Reference</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="server-installation.html#dns-mode-setup" class="md-nav__link">DNS Mode Setup</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="server-installation.html#docker-compose-install" class="md-nav__link">Docker Compose Install</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="server-installation.html#linux-install-without-docker" class="md-nav__link">Linux Install without Docker</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="server-installation.html#kubernetes-install" class="md-nav__link">Kubernetes Install</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="server-installation.html#nginx-reverse-proxy-setup-with-https" class="md-nav__link">Nginx Reverse Proxy Setup with https</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="server-installation.html#highly-available-installation-kubernetes" class="md-nav__link">Highly Available Installation (Kubernetes)</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="server-installation.html#highly-available-installation-vms-bare-metal" class="md-nav__link">Highly Available Installation (VMs/Bare Metal)</a>
-      
-    
-    </li></ul>
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="oauth.html" class="md-nav__link">Integrating OAuth</a>
-      <ul class="md-nav__list"> 
-    <li class="md-nav__item">
-    
-    
-      <a href="oauth.html#introduction" class="md-nav__link">Introduction</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="oauth.html#configuring-your-provider" class="md-nav__link">Configuring your provider</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="oauth.html#configuring-netmaker" class="md-nav__link">Configuring Netmaker</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="oauth.html#configuring-user-permissions" class="md-nav__link">Configuring User Permissions</a>
-      
-    
-    </li></ul>
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="client-installation.html" class="md-nav__link">Client Installation</a>
-      <ul class="md-nav__list"> 
-    <li class="md-nav__item">
-    
-    
-      <a href="client-installation.html#introduction-to-netclient" class="md-nav__link">Introduction to Netclient</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="client-installation.html#notes-on-windows" class="md-nav__link">Notes on Windows</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="client-installation.html#modes-and-system-compatibility" class="md-nav__link">Modes and System Compatibility</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="client-installation.html#prerequisites" class="md-nav__link">Prerequisites</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="client-installation.html#configuration" class="md-nav__link">Configuration</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="client-installation.html#installation" class="md-nav__link">Installation</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="client-installation.html#managing-netclient" class="md-nav__link">Managing Netclient</a>
-      
-    
-    </li></ul>
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="external-clients.html" class="md-nav__link">External Clients</a>
-      <ul class="md-nav__list"> 
-    <li class="md-nav__item">
-    
-    
-      <a href="external-clients.html#introduction" class="md-nav__link">Introduction</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="external-clients.html#configuring-an-ingress-gateway" class="md-nav__link">Configuring an Ingress Gateway</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="external-clients.html#adding-clients-to-a-gateway" class="md-nav__link">Adding Clients to a Gateway</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="external-clients.html#configuring-dns-for-ext-clients-optional" class="md-nav__link">Configuring DNS for Ext Clients (OPTIONAL)</a>
-      
-    
-    </li></ul>
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="usage.html" class="md-nav__link">Using Netmaker</a>
-      <ul class="md-nav__list"> 
-    <li class="md-nav__item">
-    
-    
-      <a href="usage.html#external-tutorials" class="md-nav__link">External Tutorials</a>
-      
-    
-    </li></ul>
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="api.html" class="md-nav__link">API Reference</a>
-      <ul class="md-nav__list"> 
-    <li class="md-nav__item">
-    
-    
-      <a href="api.html#api-usage" class="md-nav__link">API Usage</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="api.html#authentication" class="md-nav__link">Authentication</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="api.html#format-of-calls-for-curl" class="md-nav__link">Format of Calls for Curl</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="api.html#api-documentation" class="md-nav__link">API Documentation</a>
-      
-    
-    </li></ul>
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="troubleshoot.html" class="md-nav__link">Troubleshooting</a>
-      <ul class="md-nav__list"> 
-    <li class="md-nav__item">
-    
-    
-      <a href="troubleshoot.html#common-issues" class="md-nav__link">Common Issues</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="troubleshoot.html#server" class="md-nav__link">Server</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="troubleshoot.html#ui" class="md-nav__link">UI</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="troubleshoot.html#netclient" class="md-nav__link">Netclient</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="troubleshoot.html#coredns" class="md-nav__link">CoreDNS</a>
-      
-    
-    </li></ul>
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="support.html" class="md-nav__link">Support</a>
-      <ul class="md-nav__list"> 
-    <li class="md-nav__item">
-    
-    
-      <a href="support.html#faq" class="md-nav__link">FAQ</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="support.html#contact" class="md-nav__link">Contact</a>
-      
-    
-    </li></ul>
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="conduct.html" class="md-nav__link">Code of Conduct</a>
-      <ul class="md-nav__list"> 
-    <li class="md-nav__item">
-    
-    
-      <a href="conduct.html#our-pledge" class="md-nav__link">Our Pledge</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="conduct.html#our-standards" class="md-nav__link">Our Standards</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="conduct.html#our-responsibilities" class="md-nav__link">Our Responsibilities</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="conduct.html#scope" class="md-nav__link">Scope</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="conduct.html#enforcement" class="md-nav__link">Enforcement</a>
-      
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="conduct.html#attribution" class="md-nav__link">Attribution</a>
-      
-    
-    </li></ul>
-    
-    </li>
-    <li class="md-nav__item">
-    
-    
-      <a href="license.html" class="md-nav__link">License</a>
-      
-    
-    </li>
-  </ul>
-  
-
-</nav>
-              </div>
-            </div>
-          </div>
-          <div class="md-sidebar md-sidebar--secondary" data-md-component="toc">
-            <div class="md-sidebar__scrollwrap">
-              <div class="md-sidebar__inner">
-                
-<nav class="md-nav md-nav--secondary">
-    <label class="md-nav__title" for="__toc">Contents</label>
-  <ul class="md-nav__list" data-md-scrollfix="">
-        <li class="md-nav__item"><a href="#quick-start-nginx--page-root" class="md-nav__link">Install with Nginx (depreciated)</a><nav class="md-nav">
-              <ul class="md-nav__list">
-        <li class="md-nav__item"><a href="#introduction" class="md-nav__link">0. Introduction</a>
-        </li>
-        <li class="md-nav__item"><a href="#prerequisites" class="md-nav__link">1. Prerequisites</a>
-        </li>
-        <li class="md-nav__item"><a href="#install-dependencies" class="md-nav__link">2. Install Dependencies</a><nav class="md-nav">
-              <ul class="md-nav__list">
-        <li class="md-nav__item"><a href="#install-docker" class="md-nav__link">Install Docker</a>
-        </li>
-        <li class="md-nav__item"><a href="#id1" class="md-nav__link">Install Dependencies</a>
-        </li></ul>
-            </nav>
-        </li>
-        <li class="md-nav__item"><a href="#prepare-vm" class="md-nav__link">3. Prepare VM</a><nav class="md-nav">
-              <ul class="md-nav__list">
-        <li class="md-nav__item"><a href="#prepare-domain" class="md-nav__link">Prepare Domain</a>
-        </li>
-        <li class="md-nav__item"><a href="#prepare-firewall" class="md-nav__link">Prepare Firewall</a>
-        </li>
-        <li class="md-nav__item"><a href="#prepare-nginx" class="md-nav__link">Prepare Nginx</a>
-        </li></ul>
-            </nav>
-        </li>
-        <li class="md-nav__item"><a href="#install-netmaker" class="md-nav__link">4. Install Netmaker</a><nav class="md-nav">
-              <ul class="md-nav__list">
-        <li class="md-nav__item"><a href="#prepare-templates" class="md-nav__link">Prepare Templates</a>
-        </li>
-        <li class="md-nav__item"><a href="#start-netmaker" class="md-nav__link">Start Netmaker</a>
-        </li></ul>
-            </nav>
-        </li></ul>
-            </nav>
-        </li>
-  </ul>
-</nav>
-              </div>
-            </div>
-          </div>
-        
-        <div class="md-content">
-          <article class="md-content__inner md-typeset" role="main">
-            
-  
-<h1 id="quick-start-nginx--page-root">Install with Nginx (depreciated)<a class="headerlink" href="#quick-start-nginx--page-root" title="Permalink to this headline">¶</a></h1>
-<p>This is the old quick start guide, which contains instructions using Nginx and Docker CE. It is recommended to use the new quick start guide with Caddy instead.</p>
-
-<h2 id="introduction">0. Introduction<a class="headerlink" href="#introduction" title="Permalink to this headline">¶</a></h2>
-<p>We assume for this installation that you want all of the Netmaker features enabled, you want your server to be secure, and you want your server to be accessible from anywhere.</p>
-<p>This instance will not be HA. However, it should comfortably handle around one hundred concurrent clients and support the most common use cases.</p>
-<p>If you are deploying for a business or enterprise use case and this setup will not fit your needs, please contact <a class="reference external" href="mailto:info%40gravitl.com">info<span>@</span>gravitl<span>.</span>com</a>, or check out the business subscription plans at <a class="reference external" href="https://gravitl.com/plans/business">https://gravitl.com/plans/business</a>.</p>
-<p>By the end of this guide, you will have Netmaker installed on a public VM linked to your custom domain, secured behind an Nginx reverse proxy.</p>
-<p>For information about deploying more advanced configurations, see the <a class="reference internal" href="server-installation.html"><span class="doc">Advanced Installation</span></a> docs.</p>
-
-
-<h2 id="prerequisites">1. Prerequisites<a class="headerlink" href="#prerequisites" title="Permalink to this headline">¶</a></h2>
-<ul class="simple">
-<li><p><strong>Virtual Machine</strong></p>
-<ul>
-<li><dl class="simple">
-<dt>Preferably from a cloud provider (e.x: DigitalOcean, Linode, AWS, GCP, etc.)</dt><dd><ul>
-<li><p>We do not recommend Oracle Cloud, as VM’s here have been known to cause network interference.</p></li>
-</ul>
-</dd>
-</dl>
-</li>
-<li><p>Public, static IP</p></li>
-<li><dl class="simple">
-<dt>Min 1GB RAM, 1 CPU (4GB RAM, 2CPU preferred)</dt><dd><ul>
-<li><p>Nginx may have performance issues if using a cloud VPS with a single, shared CPU</p></li>
-</ul>
-</dd>
-</dl>
-</li>
-<li><p>2GB+ of storage</p></li>
-<li><p>Ubuntu  20.04 Installed</p></li>
-</ul>
-</li>
-<li><p><strong>Domain</strong></p>
-<ul>
-<li><p>A publicly owned domain (e.x. example.com, mysite.biz)</p></li>
-<li><p>Permission and access to modify DNS records via DNS service (e.x: Route53)</p></li>
-</ul>
-</li>
-</ul>
-
-
-<h2 id="install-dependencies">2. Install Dependencies<a class="headerlink" href="#install-dependencies" title="Permalink to this headline">¶</a></h2>
-<p><code class="docutils literal notranslate"><span class="pre">ssh</span> <span class="pre">root@your-host</span></code></p>
-
-<h3 id="install-docker">Install Docker<a class="headerlink" href="#install-docker" title="Permalink to this headline">¶</a></h3>
-<p>Begin by installing the community version of Docker and docker-compose (there are issues with the snap version). You can follow the official <a class="reference external" href="https://docs.docker.com/engine/install/">Docker instructions here</a>. Or, you can use the below series of commands which should work on Ubuntu 20.04.</p>
-<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">sudo</span> <span class="n">apt</span><span class="o">-</span><span class="n">get</span> <span class="n">remove</span> <span class="n">docker</span> <span class="n">docker</span><span class="o">-</span><span class="n">engine</span> <span class="n">docker</span><span class="o">.</span><span class="n">io</span> <span class="n">containerd</span> <span class="n">runc</span>
-<span class="n">sudo</span> <span class="n">apt</span><span class="o">-</span><span class="n">get</span> <span class="n">update</span>
-<span class="n">sudo</span> <span class="n">apt</span><span class="o">-</span><span class="n">get</span> <span class="o">-</span><span class="n">y</span> <span class="n">install</span> <span class="n">apt</span><span class="o">-</span><span class="n">transport</span><span class="o">-</span><span class="n">https</span> <span class="n">ca</span><span class="o">-</span><span class="n">certificates</span> <span class="n">curl</span> <span class="n">gnupg</span> <span class="n">lsb</span><span class="o">-</span><span class="n">release</span>
-<span class="n">curl</span> <span class="o">-</span><span class="n">fsSL</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">download</span><span class="o">.</span><span class="n">docker</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">linux</span><span class="o">/</span><span class="n">ubuntu</span><span class="o">/</span><span class="n">gpg</span> <span class="o">|</span> <span class="n">sudo</span> <span class="n">gpg</span> <span class="o">--</span><span class="n">dearmor</span> <span class="o">-</span><span class="n">o</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">share</span><span class="o">/</span><span class="n">keyrings</span><span class="o">/</span><span class="n">docker</span><span class="o">-</span><span class="n">archive</span><span class="o">-</span><span class="n">keyring</span><span class="o">.</span><span class="n">gpg</span>
-<span class="n">echo</span> <span class="s2">"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"</span> <span class="o">|</span> <span class="n">sudo</span> <span class="n">tee</span> <span class="o">/</span><span class="n">etc</span><span class="o">/</span><span class="n">apt</span><span class="o">/</span><span class="n">sources</span><span class="o">.</span><span class="n">list</span><span class="o">.</span><span class="n">d</span><span class="o">/</span><span class="n">docker</span><span class="o">.</span><span class="n">list</span> <span class="o">&gt;</span> <span class="o">/</span><span class="n">dev</span><span class="o">/</span><span class="n">null</span>
-<span class="n">sudo</span> <span class="n">apt</span><span class="o">-</span><span class="n">get</span> <span class="n">update</span>
-<span class="n">sudo</span> <span class="n">apt</span><span class="o">-</span><span class="n">get</span> <span class="o">-</span><span class="n">y</span> <span class="n">install</span> <span class="n">docker</span><span class="o">-</span><span class="n">ce</span> <span class="n">docker</span><span class="o">-</span><span class="n">ce</span><span class="o">-</span><span class="n">cli</span> <span class="n">containerd</span><span class="o">.</span><span class="n">io</span>
-<span class="n">sudo</span> <span class="n">curl</span> <span class="o">-</span><span class="n">L</span> <span class="s2">"https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)"</span> <span class="o">-</span><span class="n">o</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">local</span><span class="o">/</span><span class="nb">bin</span><span class="o">/</span><span class="n">docker</span><span class="o">-</span><span class="n">compose</span>
-<span class="n">sudo</span> <span class="n">chmod</span> <span class="o">+</span><span class="n">x</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">local</span><span class="o">/</span><span class="nb">bin</span><span class="o">/</span><span class="n">docker</span><span class="o">-</span><span class="n">compose</span>
-<span class="n">docker</span> <span class="o">--</span><span class="n">version</span>
-<span class="n">docker</span><span class="o">-</span><span class="n">compose</span> <span class="o">--</span><span class="n">version</span>
-</pre></div>
-</div>
-<p>At this point Docker should be installed.</p>
-
-
-<h3 id="id1">Install Dependencies<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h3>
-<p>In addition to Docker, this installation requires WireGuard, Nginx, and Certbot.</p>
-<p><code class="docutils literal notranslate"><span class="pre">sudo</span> <span class="pre">apt</span> <span class="pre">-y</span> <span class="pre">install</span> <span class="pre">wireguard</span> <span class="pre">wireguard-tools</span> <span class="pre">nginx</span> <span class="pre">certbot</span> <span class="pre">python3-certbot-nginx</span> <span class="pre">net-tools</span></code></p>
-
-
-
-<h2 id="prepare-vm">3. Prepare VM<a class="headerlink" href="#prepare-vm" title="Permalink to this headline">¶</a></h2>
-
-<h3 id="prepare-domain">Prepare Domain<a class="headerlink" href="#prepare-domain" title="Permalink to this headline">¶</a></h3>
-<ol class="arabic simple">
-<li><p>Choose a base domain or subdomain for Netmaker. If you own <strong>example.com</strong>, this should be something like <strong>netmaker.example.com</strong></p></li>
-</ol>
-<ul class="simple">
-<li><p>You must point your wildcard domain to the public IP of your VM, e.x: <a href="#id2"><span class="problematic" id="id3">*</span></a>.example.com –&gt; &lt;your public ip&gt;</p></li>
-</ul>
-<ol class="arabic simple" start="2">
-<li><p>Add an A record pointing to your VM using your DNS service provider for <a href="#id4"><span class="problematic" id="id5">*</span></a>.netmaker.example.com (inserting your own subdomain of course).</p></li>
-<li><p>Netmaker will create three subdomains on top of this. For the example above those subdomains would be:</p></li>
-</ol>
-<ul class="simple">
-<li><p>dashboard.netmaker.example.com</p></li>
-<li><p>api.netmaker.example.com</p></li>
-<li><p>grpc.netmaker.example.com</p></li>
-</ul>
-<p>Moving forward we will refer to your base domain using <strong>&lt;your base domain&gt;</strong>. Replace these references with your domain (e.g. netmaker.example.com).</p>
-<ol class="arabic simple" start="4">
-<li><p><code class="docutils literal notranslate"><span class="pre">nslookup</span> <span class="pre">host.&lt;your</span> <span class="pre">base</span> <span class="pre">domain&gt;</span></code> (inserting your domain) should now return the IP of your VM.</p></li>
-<li><p>Generate SSL Certificates using certbot:</p></li>
-</ol>
-<p><code class="docutils literal notranslate"><span class="pre">sudo</span> <span class="pre">certbot</span> <span class="pre">certonly</span> <span class="pre">--manual</span> <span class="pre">--preferred-challenges=dns</span> <span class="pre">--email</span> <span class="pre">[email protected]</span> <span class="pre">--server</span> <span class="pre">https://acme-v02.api.letsencrypt.org/directory</span> <span class="pre">--agree-tos</span> <span class="pre">--manual-public-ip-logging-ok</span> <span class="pre">-d</span> <span class="pre">"*.&lt;your</span> <span class="pre">base</span> <span class="pre">domain&gt;"</span></code></p>
-<p>The above command (using your domain instead of &lt;your base domain&gt;), will prompt you to enter a TXT record in your DNS service provider. Do this, and <strong>wait one  minute</strong> before clicking enter, or it may fail and you will have to run the command again.</p>
-
-
-<h3 id="prepare-firewall">Prepare Firewall<a class="headerlink" href="#prepare-firewall" title="Permalink to this headline">¶</a></h3>
-<p>Make sure firewall settings are appropriate for Netmaker. You need ports 53 and 443. On the server you can run:</p>
-<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">sudo</span> <span class="n">ufw</span> <span class="n">allow</span> <span class="n">proto</span> <span class="n">tcp</span> <span class="kn">from</span> <span class="nn">any</span> <span class="n">to</span> <span class="nb">any</span> <span class="n">port</span> <span class="mi">443</span> <span class="o">&amp;&amp;</span> <span class="n">sudo</span> <span class="n">ufw</span> <span class="n">allow</span> <span class="mi">53</span><span class="o">/</span><span class="n">udp</span> <span class="o">&amp;&amp;</span> <span class="n">sudo</span> <span class="n">ufw</span> <span class="n">allow</span> <span class="mi">53</span><span class="o">/</span><span class="n">tcp</span>
-</pre></div>
-</div>
-<dl class="simple">
-<dt><strong>Based on your cloud provider, you may also need to set inbound security rules for your server. This will be dependent on your cloud provider. Be sure to check before moving on:</strong></dt><dd><ul class="simple">
-<li><p>allow 443/tcp from all</p></li>
-<li><p>allow 53/udp and 53/tcp from all</p></li>
-</ul>
-</dd>
-</dl>
-<p>In addition to the above ports, you will need to make sure that your cloud’s firewall or security groups are opened for the range of ports that Netmaker’s WireGuard interfaces consume.</p>
-<p>Netmaker will create one interface per network, starting from 51821. So, if you plan on having 5 networks, you will want to have at least 51821-51825 open (udp).</p>
-
-
-<h3 id="prepare-nginx">Prepare Nginx<a class="headerlink" href="#prepare-nginx" title="Permalink to this headline">¶</a></h3>
-<p>Nginx will serve the SSL certificate with your chosen domain and forward traffic to netmaker.</p>
-<p>Get the nginx configuration file:</p>
-<p><code class="docutils literal notranslate"><span class="pre">wget</span> <span class="pre">https://raw.githubusercontent.com/gravitl/netmaker/master/nginx/netmaker-nginx-template.conf</span></code></p>
-<p>Insert your domain in the configuration file and add to nginx:</p>
-<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">sed</span> <span class="o">-</span><span class="n">i</span> <span class="s1">'s/NETMAKER_BASE_DOMAIN/&lt;your base domain&gt;/g'</span> <span class="n">netmaker</span><span class="o">-</span><span class="n">nginx</span><span class="o">-</span><span class="n">template</span><span class="o">.</span><span class="n">conf</span>
-<span class="n">sudo</span> <span class="n">cp</span> <span class="n">netmaker</span><span class="o">-</span><span class="n">nginx</span><span class="o">-</span><span class="n">template</span><span class="o">.</span><span class="n">conf</span> <span class="o">/</span><span class="n">etc</span><span class="o">/</span><span class="n">nginx</span><span class="o">/</span><span class="n">conf</span><span class="o">.</span><span class="n">d</span><span class="o">/&lt;</span><span class="n">your</span> <span class="n">base</span> <span class="n">domain</span><span class="o">&gt;.</span><span class="n">conf</span>
-<span class="n">nginx</span> <span class="o">-</span><span class="n">t</span> <span class="o">&amp;&amp;</span> <span class="n">nginx</span> <span class="o">-</span><span class="n">s</span> <span class="n">reload</span>
-<span class="n">systemctl</span> <span class="n">restart</span> <span class="n">nginx</span>
-</pre></div>
-</div>
-
-
-
-<h2 id="install-netmaker">4. Install Netmaker<a class="headerlink" href="#install-netmaker" title="Permalink to this headline">¶</a></h2>
-
-<h3 id="prepare-templates">Prepare Templates<a class="headerlink" href="#prepare-templates" title="Permalink to this headline">¶</a></h3>
-<p><strong>Note on COREDNS_IP:</strong> Depending on your cloud provider, the public IP may not be bound directly to the VM on which you are running. In such cases, CoreDNS cannot bind to this IP, and you should use the IP of the default interface on your machine in place of COREDNS_IP. If the public IP <strong>is</strong> bound to the VM, you can simply use the same IP as SERVER_PUBLIC_IP.</p>
-<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">wget</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">raw</span><span class="o">.</span><span class="n">githubusercontent</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">gravitl</span><span class="o">/</span><span class="n">netmaker</span><span class="o">/</span><span class="n">master</span><span class="o">/</span><span class="n">compose</span><span class="o">/</span><span class="n">docker</span><span class="o">-</span><span class="n">compose</span><span class="o">.</span><span class="n">yml</span>
-<span class="n">sed</span> <span class="o">-</span><span class="n">i</span> <span class="s1">'s/NETMAKER_BASE_DOMAIN/&lt;your base domain&gt;/g'</span> <span class="n">docker</span><span class="o">-</span><span class="n">compose</span><span class="o">.</span><span class="n">yml</span>
-<span class="n">sed</span> <span class="o">-</span><span class="n">i</span> <span class="s1">'s/SERVER_PUBLIC_IP/&lt;your server ip&gt;/g'</span> <span class="n">docker</span><span class="o">-</span><span class="n">compose</span><span class="o">.</span><span class="n">yml</span>
-<span class="n">sed</span> <span class="o">-</span><span class="n">i</span> <span class="s1">'s/COREDNS_IP/&lt;your server ip&gt;/g'</span> <span class="n">docker</span><span class="o">-</span><span class="n">compose</span><span class="o">.</span><span class="n">yml</span>
-</pre></div>
-</div>
-<p>Generate a unique master key and insert it:</p>
-<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">tr</span> <span class="o">-</span><span class="n">dc</span> <span class="n">A</span><span class="o">-</span><span class="n">Za</span><span class="o">-</span><span class="n">z0</span><span class="o">-</span><span class="mi">9</span> <span class="o">&lt;/</span><span class="n">dev</span><span class="o">/</span><span class="n">urandom</span> <span class="o">|</span> <span class="n">head</span> <span class="o">-</span><span class="n">c</span> <span class="mi">30</span> <span class="p">;</span> <span class="n">echo</span> <span class="s1">''</span>
-<span class="n">sed</span> <span class="o">-</span><span class="n">i</span> <span class="s1">'s/REPLACE_MASTER_KEY/&lt;your generated key&gt;/g'</span> <span class="n">docker</span><span class="o">-</span><span class="n">compose</span><span class="o">.</span><span class="n">yml</span>
-</pre></div>
-</div>
-<p>You may want to save this key for future use with the API.</p>
-
-
-<h3 id="start-netmaker">Start Netmaker<a class="headerlink" href="#start-netmaker" title="Permalink to this headline">¶</a></h3>
-<p><code class="docutils literal notranslate"><span class="pre">sudo</span> <span class="pre">docker-compose</span> <span class="pre">-f</span> <span class="pre">docker-compose.yml</span> <span class="pre">up</span> <span class="pre">-d</span></code></p>
-<p>navigate to dashboard.&lt;your base domain&gt; to log into the UI.</p>
-<p>To troubleshoot issues, start with:</p>
-<p><code class="docutils literal notranslate"><span class="pre">docker</span> <span class="pre">logs</span> <span class="pre">netmaker</span></code></p>
-<p>Or check out the <a class="reference internal" href="troubleshoot.html"><span class="doc">troubleshoooting docs</span></a>.</p>
-
-
-
-
-
-          </article>
-        </div>
-      </div>
-    </main>
-  </div>
-  <footer class="md-footer">
-    <div class="md-footer-nav">
-      <nav class="md-footer-nav__inner md-grid">
-          
-            <a href="getting-started.html" title="Getting Started"
-               class="md-flex md-footer-nav__link md-footer-nav__link--prev"
-               rel="prev">
-              <div class="md-flex__cell md-flex__cell--shrink">
-                <i class="md-icon md-icon--arrow-back md-footer-nav__button"></i>
-              </div>
-              <div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title">
-                <span class="md-flex__ellipsis">
-                  <span
-                      class="md-footer-nav__direction"> Previous </span> Getting Started </span>
-              </div>
-            </a>
-          
-          
-            <a href="server-installation.html" title="Advanced Server Installation"
-               class="md-flex md-footer-nav__link md-footer-nav__link--next"
-               rel="next">
-            <div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title"><span
-                class="md-flex__ellipsis"> <span
-                class="md-footer-nav__direction"> Next </span> Advanced Server Installation </span>
-            </div>
-            <div class="md-flex__cell md-flex__cell--shrink"><i
-                class="md-icon md-icon--arrow-forward md-footer-nav__button"></i>
-            </div>
-          
-        </a>
-        
-      </nav>
-    </div>
-    <div class="md-footer-meta md-typeset">
-      <div class="md-footer-meta__inner md-grid">
-        <div class="md-footer-copyright">
-          <div class="md-footer-copyright__highlight">
-              &#169; Copyright 2021, Alex Feiszli.
-              
-          </div>
-            Created using
-            <a href="http://www.sphinx-doc.org/">Sphinx</a> 4.3.0.
-             and
-            <a href="https://github.com/bashtage/sphinx-material/">Material for
-              Sphinx</a>
-        </div>
-      </div>
-    </div>
-  </footer>
-  <script src="_static/javascripts/application.js"></script>
-  <script>app.initialize({version: "1.0.4", url: {base: ".."}})</script>
-  </body>
-</html>

+ 16 - 9
docs/_build/html/quick-start.html

@@ -209,7 +209,7 @@
     <li class="md-nav__item">
     
     
-      <a href="about.html" class="md-nav__link">1. About</a>
+      <a href="about.html" class="md-nav__link">About</a>
       
     
     </li>
@@ -320,49 +320,56 @@
     <li class="md-nav__item">
     
     
-      <a href="server-installation.html" class="md-nav__link">Advanced Server Installation</a>
+      <a href="external-clients.html" class="md-nav__link">Ingress + External Clients</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="oauth.html" class="md-nav__link">Integrating OAuth</a>
+      <a href="egress-gateway.html" class="md-nav__link">Egress Gateway</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="client-installation.html" class="md-nav__link">Client Installation</a>
+      <a href="relay-server.html" class="md-nav__link">Relay Servers</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="external-clients.html" class="md-nav__link">Ingress GW + External Clients</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="egress-gateway.html" class="md-nav__link">Egress Gateway</a>
+      <a href="server-installation.html" class="md-nav__link">Advanced Server Installation</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="relay-server.html" class="md-nav__link">Relay Servers</a>
+      <a href="client-installation.html" class="md-nav__link">Advanced Client Installation</a>
+      
+    
+    </li>
+    <li class="md-nav__item">
+    
+    
+      <a href="oauth.html" class="md-nav__link">Integrating OAuth</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="usage.html" class="md-nav__link">Using Netmaker</a>
+      <a href="usage.html" class="md-nav__link">External Guides</a>
       
     
     </li>
@@ -497,7 +504,7 @@
 
 
 <h2 id="prepare-dns">1. Prepare DNS<a class="headerlink" href="#prepare-dns" title="Permalink to this headline">¶</a></h2>
-<p>Create a wildcard A record pointing to the public IP of your VM. As an example, <a href="#id1"><span class="problematic" id="id2">*</span></a>.netmaker.example.com.</p>
+<p>Create a wildcard A record pointing to the public IP of your VM. As an example, *.netmaker.example.com.</p>
 <p>Caddy will create 3 subdomains with this wildcard, EX:</p>
 <ul class="simple">
 <li><p>dashboard.netmaker.example.com</p></li>

+ 1 - 1
docs/_build/html/relay-server.html

@@ -297,7 +297,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>

+ 1 - 1
docs/_build/html/search.html

@@ -269,7 +269,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>

File diff suppressed because it is too large
+ 0 - 0
docs/_build/html/searchindex.js


+ 59 - 12
docs/_build/html/server-installation.html

@@ -265,7 +265,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>
@@ -292,6 +292,8 @@
         <li class="md-nav__item"><a href="#config-file-reference" class="md-nav__link">Config File Reference</a>
         </li>
         <li class="md-nav__item"><a href="#compose-file-annotated" class="md-nav__link">Compose File - Annotated</a>
+        </li>
+        <li class="md-nav__item"><a href="#available-docker-compose-files" class="md-nav__link">Available docker-compose files</a>
         </li></ul>
             </nav>
         </li>
@@ -309,13 +311,15 @@
         </li>
         <li class="md-nav__item"><a href="#linux-install-without-docker" class="md-nav__link">Linux Install without Docker</a><nav class="md-nav">
               <ul class="md-nav__list">
-        <li class="md-nav__item"><a href="#rqlite-setup" class="md-nav__link">rqlite Setup</a>
+        <li class="md-nav__item"><a href="#database-setup-optional" class="md-nav__link">Database Setup (optional)</a>
         </li>
         <li class="md-nav__item"><a href="#server-setup" class="md-nav__link">Server Setup</a>
         </li>
         <li class="md-nav__item"><a href="#ui-setup" class="md-nav__link">UI Setup</a>
         </li>
-        <li class="md-nav__item"><a href="#coredns-setup" class="md-nav__link">CoreDNS Setup</a>
+        <li class="md-nav__item"><a href="#coredns-setup-optional" class="md-nav__link">CoreDNS Setup (optional)</a>
+        </li>
+        <li class="md-nav__item"><a href="#proxy-load-balancer" class="md-nav__link">Proxy / Load Balancer</a>
         </li></ul>
             </nav>
         </li>
@@ -351,7 +355,7 @@
               <ul class="md-nav__list">
         <li class="md-nav__item"><a href="#load-balancer-setup" class="md-nav__link">1. Load Balancer Setup</a>
         </li>
-        <li class="md-nav__item"><a href="#id1" class="md-nav__link">2. RQLite Setup</a>
+        <li class="md-nav__item"><a href="#rqlite-setup" class="md-nav__link">2. RQLite Setup</a>
         </li>
         <li class="md-nav__item"><a href="#netmaker-setup" class="md-nav__link">3. Netmaker Setup</a>
         </li>
@@ -524,6 +528,8 @@
         <li class="md-nav__item"><a href="#config-file-reference" class="md-nav__link">Config File Reference</a>
         </li>
         <li class="md-nav__item"><a href="#compose-file-annotated" class="md-nav__link">Compose File - Annotated</a>
+        </li>
+        <li class="md-nav__item"><a href="#available-docker-compose-files" class="md-nav__link">Available docker-compose files</a>
         </li></ul>
             </nav>
         </li>
@@ -541,13 +547,15 @@
         </li>
         <li class="md-nav__item"><a href="#linux-install-without-docker" class="md-nav__link">Linux Install without Docker</a><nav class="md-nav">
               <ul class="md-nav__list">
-        <li class="md-nav__item"><a href="#rqlite-setup" class="md-nav__link">rqlite Setup</a>
+        <li class="md-nav__item"><a href="#database-setup-optional" class="md-nav__link">Database Setup (optional)</a>
         </li>
         <li class="md-nav__item"><a href="#server-setup" class="md-nav__link">Server Setup</a>
         </li>
         <li class="md-nav__item"><a href="#ui-setup" class="md-nav__link">UI Setup</a>
         </li>
-        <li class="md-nav__item"><a href="#coredns-setup" class="md-nav__link">CoreDNS Setup</a>
+        <li class="md-nav__item"><a href="#coredns-setup-optional" class="md-nav__link">CoreDNS Setup (optional)</a>
+        </li>
+        <li class="md-nav__item"><a href="#proxy-load-balancer" class="md-nav__link">Proxy / Load Balancer</a>
         </li></ul>
             </nav>
         </li>
@@ -583,7 +591,7 @@
               <ul class="md-nav__list">
         <li class="md-nav__item"><a href="#load-balancer-setup" class="md-nav__link">1. Load Balancer Setup</a>
         </li>
-        <li class="md-nav__item"><a href="#id1" class="md-nav__link">2. RQLite Setup</a>
+        <li class="md-nav__item"><a href="#rqlite-setup" class="md-nav__link">2. RQLite Setup</a>
         </li>
         <li class="md-nav__item"><a href="#netmaker-setup" class="md-nav__link">3. Netmaker Setup</a>
         </li>
@@ -689,6 +697,20 @@
 <dt>CLIENT_MODE:</dt><dd><p><strong>Default:</strong> “on”</p>
 <p><strong>Description:</strong> Specifies if server should deploy itself as a node (client) in each network. May be turned to “off” for more restricted servers.</p>
 </dd>
+<dt>RCE:</dt><dd><p><strong>Default:</strong> “off”</p>
+<p><strong>Description:</strong> The server enables you to set PostUp and PostDown commands for nodes, which is standard for WireGuard with wg-quick, but is also <strong>Remote Code Execution</strong>, which is a critical vulnerability if the server is exploited. Because of this, it’s turned off by default, but if turned on, PostUp and PostDown become editable.</p>
+</dd>
+<dt>SERVER_GRPC_WIREGUARD</dt><dd><p><strong>Depreciated:</strong> no longer in use</p>
+</dd>
+<dt>DISPLAY_KEYS</dt><dd><p><strong>Default:</strong> “on”</p>
+<p><strong>Description:</strong> If “on”, will allow you to always show the key values of “access keys”. This could be considered a vulnerability, so if turned “off”, will only display key values once, and it is up to the users to store the key values locally.</p>
+</dd>
+<dt>NODE_ID</dt><dd><p><strong>Default:</strong> &lt;system mac addres&gt;</p>
+<p><strong>Description:</strong> This setting is used for HA configurations of the server, to identify between different servers. Nodes are given ID’s like netmaker-1, netmaker-2, and netmaker-3. If the server is not HA, there is no reason to set this field.</p>
+</dd>
+<dt>TELEMETRY</dt><dd><p><strong>Default:</strong> “on”</p>
+<p><strong>Description:</strong> If “on”, the server will send anonymous telemetry data once daily, which is used to improve the product. Data sent includes counts (integer values) for the number of nodes, types of nodes, users, and networks. It also sends the version of the server.</p>
+</dd>
 </dl>
 
 
@@ -708,6 +730,7 @@
   <span class="nt">sqlconn</span><span class="p">:</span> <span class="s">""</span> <span class="c1"># defaults to "http://" or SQL_CONN (if set)</span>
   <span class="nt">disableremoteipcheck</span><span class="p">:</span> <span class="s">""</span> <span class="c1"># defaults to "false" or DISABLE_REMOTE_IP_CHECK (if set)</span>
   <span class="nt">version</span><span class="p">:</span> <span class="s">""</span> <span class="c1"># version of server</span>
+  <span class="nt">rce</span><span class="p">:</span> <span class="s">""</span> <span class="c1"># defaults to "off"</span>
 </pre></div>
 </div>
 
@@ -753,6 +776,9 @@
       <span class="nt">DISPLAY_KEYS</span><span class="p">:</span> <span class="s">"on"</span> <span class="c1"># Show keys permanently in UI (until deleted) as opposed to 1-time display.</span>
       <span class="nt">SERVER_API_CONN_STRING</span><span class="p">:</span> <span class="s">""</span> <span class="c1"># Changes the api connection string. IP:PORT format. By default is empty and uses SERVER_HOST:API_PORT</span>
       <span class="nt">SERVER_GRPC_CONN_STRING</span><span class="p">:</span> <span class="s">""</span> <span class="c1"># Changes the grpc connection string. IP:PORT format. By default is empty and uses SERVER_HOST:GRPC_PORT</span>
+      <span class="nt">RCE</span><span class="p">:</span> <span class="s">"off"</span> <span class="c1"># Enables setting PostUp and PostDown (arbitrary commands) on nodes from the server. Off by default.</span>
+      <span class="nt">NODE_ID</span><span class="p">:</span> <span class="s">""</span> <span class="c1"># Sets the name/id of the nodes that the server creates. Necessary for HA configurations to identify between servers (for instance, netmaker-1, netmaker-2, etc). For non-HA deployments, is not necessary.</span>
+      <span class="nt">TELEMETRY</span><span class="p">:</span> <span class="s">"on"</span> <span class="c1"># Whether or not to send telemetry data to help improve Netmaker. Switch to "off" to opt out of sending telemetry.</span>
   <span class="nt">netmaker-ui</span><span class="p">:</span> <span class="c1"># The Netmaker UI Component</span>
     <span class="nt">container_name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">netmaker-ui</span>
     <span class="nt">depends_on</span><span class="p">:</span>
@@ -782,6 +808,20 @@
 </div>
 
 
+<h3 id="available-docker-compose-files">Available docker-compose files<a class="headerlink" href="#available-docker-compose-files" title="Permalink to this headline">¶</a></h3>
+<p>The default options for docker-compose can be found here: <a class="reference external" href="https://github.com/gravitl/netmaker/tree/master/compose">https://github.com/gravitl/netmaker/tree/master/compose</a></p>
+<p>The following is a brief description of each:</p>
+<ul class="simple">
+<li><p><a class="reference external" href="https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.contained.yml">docker-compose.contained.yml</a> - This is the default docker-compose, used in the quick start and deployment script in the README on GitHub. It deploys Netmaker with all options included (Caddy and CoreDNS) and has “self-contained” netclients, meaning they do not affect host networking.</p></li>
+<li><p><a class="reference external" href="https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.coredns.yml">docker-compose.coredns.yml</a> - This is a simple compose used to spin up a standalone CoreDNS server. Can be useful if, for instance, you are unning Netmaker on baremetal but need CoreDNS.</p></li>
+<li><p><a class="reference external" href="https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.hostnetwork.yml">docker-compose.hostnetwork.yml</a> - This is similar to the docker-compose.contained.yml but with a key difference: it has advanced permissions and mounts host volumes to control networking on the host level.</p></li>
+<li><p><a class="reference external" href="https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.nocaddy.yml">docker-compose.nocaddy.yml</a> -= This is the same as docker-compose.contained.yml but without Caddy, in case you need to use a different proxy like Nginx, Traefik, or HAProxy.</p></li>
+<li><p><a class="reference external" href="https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.nodns.yml">docker-compose.nodns.yml</a> - This is the same as docker-compose.contained.yml but without CoreDNS, in which case you will not have the Private DNS feature.</p></li>
+<li><p><a class="reference external" href="https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.reference.yml">docker-compose.reference.yml</a> - This is the same as docker-compose.contained.yml but with all variable options on display and annotated (it’s what we show right above this section). Use this to determine which variables you should add or change in your configuration.</p></li>
+<li><p><a class="reference external" href="https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.yml">docker-compose.yml</a> - This is a renamed docker-compose.contained.yml. It is meant only to act as a placeholder for what we consider the “primary” docker-compose that users should work with.</p></li>
+</ul>
+
+
 
 <h2 id="dns-mode-setup">DNS Mode Setup<a class="headerlink" href="#dns-mode-setup" title="Permalink to this headline">¶</a></h2>
 <p>If you plan on running the server in DNS Mode, know that a <a class="reference external" href="https://coredns.io/manual/toc/">CoreDNS Server</a> will be installed. CoreDNS is a light-weight, fast, and easy-to-configure DNS server. It is recommended to bind CoreDNS to port 53 of the host system, and it will do so by default. The clients will expect the nameserver to be on port 53, and many systems have issues resolving a different port.</p>
@@ -837,15 +877,16 @@ docker-compose up -d`
 
 
 <span id="nodocker"></span><h2 id="linux-install-without-docker">Linux Install without Docker<a class="headerlink" href="#linux-install-without-docker" title="Permalink to this headline">¶</a></h2>
-<p>Most systems support Docker, but some do not. In such environments, there are many options for installing Netmaker. Netmaker is available as a binary file, and there is a zip file of the Netmaker UI static HTML on GitHub. Beyond the UI and Server, you need to install MongoDB and CoreDNS (optional).</p>
-<p>To start, we recommend following the Nginx instructions in the <a class="reference internal" href="quick-start.html"><span class="doc">Quick Install</span></a> guide to enable SSL for your environment.</p>
+<p>Most systems support Docker, but some do not. In such environments, there are many options for installing Netmaker. Netmaker is available as a binary file, and there is a zip file of the Netmaker UI static HTML on GitHub. Beyond the UI and Server, you may want to optionally install a database (sqlite is embedded, rqlite or postgres are supported) and CoreDNS (also optional).</p>
 <p>Once this is enabled and configured for a domain, you can continue with the below. The recommended server runs Ubuntu 20.04.</p>
 
-<h3 id="rqlite-setup">rqlite Setup<a class="headerlink" href="#rqlite-setup" title="Permalink to this headline">¶</a></h3>
+<h3 id="database-setup-optional">Database Setup (optional)<a class="headerlink" href="#database-setup-optional" title="Permalink to this headline">¶</a></h3>
+<p>You can run the netmaker binary standalone and it will run an embedded sqlite server. Data goes in the data/ directory. Optionally, you can run PostgreSQL or rqlite. Instructions for rqlite are below.</p>
 <ol class="arabic simple">
 <li><p>Install rqlite on your server: <a class="reference external" href="https://github.com/rqlite/rqlite">https://github.com/rqlite/rqlite</a></p></li>
 <li><p>Run rqlite: rqlited -node-id 1 ~/node.1</p></li>
 </ol>
+<p>If using rqlite or postgres, you must change the DATABASE environment/config variable and enter connection details.</p>
 
 
 <h3 id="server-setup">Server Setup<a class="headerlink" href="#server-setup" title="Permalink to this headline">¶</a></h3>
@@ -878,7 +919,13 @@ docker-compose up -d`
 </div>
 
 
-<h3 id="coredns-setup">CoreDNS Setup<a class="headerlink" href="#coredns-setup" title="Permalink to this headline">¶</a></h3>
+<h3 id="coredns-setup-optional">CoreDNS Setup (optional)<a class="headerlink" href="#coredns-setup-optional" title="Permalink to this headline">¶</a></h3>
+<p>CoreDNS is only required if you want private DNS features. Once installed, you must set the CoreDNS variables in the env settings of the server.</p>
+<p>See <a class="reference external" href="https://coredns.io/manual/toc/#installation">https://coredns.io/manual/toc/#installation</a></p>
+
+
+<h3 id="proxy-load-balancer">Proxy / Load Balancer<a class="headerlink" href="#proxy-load-balancer" title="Permalink to this headline">¶</a></h3>
+<p>You will need to proxy connections to your UI and Server. By default the ports are 8081, 8082, and 50051 (grpc). This proxy should handle SSL certificates. We recommend Caddy or Nginx (you can follow the Nginx guide in these docs). The proxy must be able to handle gRPC connections.</p>
 
 
 
@@ -1086,7 +1133,7 @@ This install has some notable exceptions:
 <p>Your load balancer of choice will send requests to the Netmaker servers. Setup is similar to the various guides we have created for Nginx, Caddy, and Traefik. SSL certificates must also be configured and handled by the LB.</p>
 
 
-<h3 id="id1">2. RQLite Setup<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h3>
+<h3 id="rqlite-setup">2. RQLite Setup<a class="headerlink" href="#rqlite-setup" title="Permalink to this headline">¶</a></h3>
 <p>RQLite is the included distributed datastore for an HA Netmaker installation. If you have a different corporate database you wish to integrate, Netmaker is easily extended to other DB’s. If this is a requirement, please contact us.</p>
 <p>Assuming you use Rqlite, you must run it on each Netmaker server VM, or alongside that VM as a container. Setup a config.json for database credentials (password supports BCRYPT HASHING) and mount in working directory of rqlite and specify with <cite>-auth config.json</cite> :</p>
 <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[{</span>

+ 35 - 1
docs/_build/html/support.html

@@ -265,7 +265,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>
@@ -351,6 +351,8 @@
         </li></ul>
             </nav>
         </li>
+        <li class="md-nav__item"><a href="#telemetry" class="md-nav__link">Telemetry</a>
+        </li>
         <li class="md-nav__item"><a href="#contact" class="md-nav__link">Contact</a>
         </li></ul>
             </nav>
@@ -364,6 +366,13 @@
       <a href="#faq" class="md-nav__link">FAQ</a>
       
     
+    </li>
+    <li class="md-nav__item">
+    
+    
+      <a href="#telemetry" class="md-nav__link">Telemetry</a>
+      
+    
     </li>
     <li class="md-nav__item">
     
@@ -416,6 +425,8 @@
         </li></ul>
             </nav>
         </li>
+        <li class="md-nav__item"><a href="#telemetry" class="md-nav__link">Telemetry</a>
+        </li>
         <li class="md-nav__item"><a href="#contact" class="md-nav__link">Contact</a>
         </li></ul>
             </nav>
@@ -459,6 +470,29 @@
 
 
 
+<h2 id="telemetry">Telemetry<a class="headerlink" href="#telemetry" title="Permalink to this headline">¶</a></h2>
+<p>As of v0.10.0, Netmaker collects “opt-out” telemetry data. To opt out, simply set “TELEMETRY=off” in your docker-compose file.</p>
+<p>Please consider participating in telemetry, as it helps us focus on the features and bug fixes which are most useful to users. Netmaker is a broad platform, and without this data, it is difficult to know where the team should spend its limited resources.</p>
+<p>The following is the full list of telemetry data we collect. Besides “Server Version” all data is simply an integer count:</p>
+<ul class="simple">
+<li><p>Randomized server ID</p></li>
+<li><p>Count of nodes</p></li>
+<li><p>Count of “non-server” nodes</p></li>
+<li><p>Count of external clients</p></li>
+<li><p>Count of networks</p></li>
+<li><p>Count of users</p></li>
+<li><p>Count of linux nodes</p></li>
+<li><p>Count of freebsd nodes</p></li>
+<li><p>Count of macos nodes</p></li>
+<li><p>Count of windows nodes</p></li>
+<li><p>Count of docker nodes</p></li>
+<li><p>Count of k8s nodes</p></li>
+<li><p>Server version</p></li>
+</ul>
+<p>We use  <a class="reference external" href="https://https://posthog.com/">PostHog</a>, an open source and trusted framework for telemetry data.</p>
+<p>To look at exactly we collect telemetry, you can view the source code under serverctl/telemetry.go: <a class="reference external" href="https://github.com/gravitl/netmaker/blob/master/serverctl/telemetry.go">https://github.com/gravitl/netmaker/blob/master/serverctl/telemetry.go</a></p>
+
+
 <h2 id="contact">Contact<a class="headerlink" href="#contact" title="Permalink to this headline">¶</a></h2>
 <p>If you need help, try the discord or open a GitHub ticket.</p>
 <p>Email: <a class="reference external" href="mailto:info%40gravitl.com">info<span>@</span>gravitl<span>.</span>com</a></p>

+ 39 - 9
docs/_build/html/troubleshoot.html

@@ -209,7 +209,7 @@
     <li class="md-nav__item">
     
     
-      <a href="about.html" class="md-nav__link">1. About</a>
+      <a href="about.html" class="md-nav__link">About</a>
       
     
     </li>
@@ -244,49 +244,56 @@
     <li class="md-nav__item">
     
     
-      <a href="server-installation.html" class="md-nav__link">Advanced Server Installation</a>
+      <a href="external-clients.html" class="md-nav__link">Ingress + External Clients</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="oauth.html" class="md-nav__link">Integrating OAuth</a>
+      <a href="egress-gateway.html" class="md-nav__link">Egress Gateway</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="client-installation.html" class="md-nav__link">Client Installation</a>
+      <a href="relay-server.html" class="md-nav__link">Relay Servers</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="external-clients.html" class="md-nav__link">Ingress GW + External Clients</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="egress-gateway.html" class="md-nav__link">Egress Gateway</a>
+      <a href="server-installation.html" class="md-nav__link">Advanced Server Installation</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="relay-server.html" class="md-nav__link">Relay Servers</a>
+      <a href="client-installation.html" class="md-nav__link">Advanced Client Installation</a>
+      
+    
+    </li>
+    <li class="md-nav__item">
+    
+    
+      <a href="oauth.html" class="md-nav__link">Integrating OAuth</a>
       
     
     </li>
     <li class="md-nav__item">
     
     
-      <a href="usage.html" class="md-nav__link">Using Netmaker</a>
+      <a href="usage.html" class="md-nav__link">External Guides</a>
       
     
     </li>
@@ -460,7 +467,11 @@ You can also sign-up for updates at our <a class="reference external" href="http
 
 
 <h2 id="server">Server<a class="headerlink" href="#server" title="Permalink to this headline">¶</a></h2>
-<dl class="simple">
+<dl>
+<dt><strong>How do I use a private address from the Netmaker Server? How do I contact nodes using their private addresses from the server?</strong></dt><dd><p>Default nodes appear in each network with the “netmaker” name. These nodes are created by, and attached to, the server. The server is contained in docker, meaning these clients are also contained in docker. Their networking stack is also contained in docker. The “netmaker” nodes are meant to function as network utilities. They assist with UDP Hole Punching and can run Relays, Egress, and Ingress. However, they are meant to stay contained in the server. They do not touch the host networking stack.</p>
+<p>If you want to give the physical server / VM a private IP in the netmaker network, you must deploy an <strong>additional</strong> node using the standard netclient. The only note here is that the server consumes ports 51821-51831, so you will need to give it a port outside this range, e.x. <cite>./netclient join &lt;token&gt; –port 51835</cite>.</p>
+<p>One a netclient is deployed to the underlying server/VM, you will be able to use the private address to reach other nodes from the host, or to reach the server over the private network.</p>
+</dd>
 <dt><strong>I upgraded from 0.7 to 0.8 and now I dont have any data in my server!</strong></dt><dd><p>In 0.8, sqlite becomes the default database. If you were running with rqlite, you must set the DATABASE environment variable to rqlite in order to continue using rqlite.</p>
 </dd>
 <dt><strong>Can I secure/encrypt all the traffic to my server and UI?</strong></dt><dd><p>This can fairly simple to achieve assuming you have access to a domain and are familiar with Nginx.
@@ -525,6 +536,25 @@ You can also see the current WireGuard configuration with <code class="docutils
 </dd>
 <dt><strong>I have a hard to reach machine behind a firewall or a corporate NAT, what can I do?</strong></dt><dd><p>In this situation you can use the Relay Server functionality introduced in Netmaker v0.8 to designate a node as a relay to your “stuck” machine. Simply click the button to make a node into a relay and tell it to relay traffic to this hard-to-reach peer.</p>
 </dd>
+<dt><strong>I am unable to run the netclient on my OpenWRT machine, what’s wrong?</strong></dt><dd><p>Deploying on OpenWRT depends a lot on the version of OpenWRT and the hardware being used. If the primary installer does not work, there are two things you can try:</p>
+<ol class="arabic simple">
+<li><p>This community-run package for OpenWRT: <a class="reference external" href="https://github.com/sbilly/netmaker-openwrt">https://github.com/sbilly/netmaker-openwrt</a></p></li>
+<li><p>Manual installation:</p></li>
+</ol>
+<ul class="simple">
+<li><p>download (wget) the netclient package for your hardware from the netclient releases: <a class="reference external" href="https://github.com/gravitl/netmaker/releases">https://github.com/gravitl/netmaker/releases</a></p></li>
+<li><p>rename to “netclient”</p></li>
+<li><p>Run as root from a bash shell on OpenWRT</p></li>
+</ul>
+<ol class="arabic simple" start="3">
+<li><p>You may experience an issue with the length of the token, which has limits on some OpenWRT shells. If you run into this problem, you can use the following script to convert your token into a “netclient join” command:</p></li>
+</ol>
+<ul class="simple">
+<li><p><cite>wget https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/token-convert.sh</cite></p></li>
+<li><p>./token-convert &lt;token value&gt;</p></li>
+<li><p>Run the output on your OpenWRT machine</p></li>
+</ul>
+</dd>
 </dl>
 
 

+ 1 - 1
docs/_build/html/ui-reference.html

@@ -265,7 +265,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>

+ 1 - 1
docs/_build/html/upgrades.html

@@ -265,7 +265,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>

+ 1 - 1
docs/_build/html/usage.html

@@ -265,7 +265,7 @@
     <li class="md-nav__item">
     
     
-      <a href="https://nm-k8s.readthedocs.io" class="md-nav__link">Kubernetes</a>
+      <a href="https://k8s.netmaker.org" class="md-nav__link">Kubernetes</a>
       
     
     </li>

+ 59 - 47
docs/api.rst

@@ -17,8 +17,11 @@ API calls must be authenticated via a header of  the format  `-H "Authorization:
 
 Format of Calls for Curl
 ========================
-Requests take the format of `curl -H "Authorization: Bearer <YOUR_SECRET_KEY>" -H 'Content-Type: application/json' localhost:8081/api/path/to/endpoint`
+Requests take the format of 
 
+.. code-block::
+
+    curl -H "Authorization: Bearer <YOUR_SECRET_KEY>" -H 'Content-Type: application/json' localhost:8081/api/path/to/endpoint
 
 API Documentation
 =================
@@ -41,18 +44,21 @@ Networks API
   
 Networks API Call Examples
 --------------------------  
-  
-**Get All Networks:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks | jq`
 
-**Create Network:** `curl -d '{"addressrange":"10.70.0.0/16","netid":"skynet"}' -H "Authorization: Bearer YOUR_SECRET_KEY" -H 'Content-Type: application/json' localhost:8081/api/networks`
+.. code-block::
+
+    Get All Networks: curl -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks | jq
 
-**Get Network:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet | jq`
+    Create Network: curl -d '{"addressrange":"10.70.0.0/16","netid":"skynet"}' -H "Authorization: Bearer YOUR_SECRET_KEY" -H 'Content-Type: application/json' localhost:8081/api/networks
 
-**Update Network:** `curl -X PUT -d '{"displayname":"my-house"}' -H "Authorization: Bearer YOUR_SECRET_KEY" -H 'Content-Type: application/json' localhost:8081/api/networks/skynet`
+    Get Network: curl -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet | jq
 
-**Delete Network:** `curl -X DELETE -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet`
+    Update Network: curl -X PUT -d '{"displayname":"my-house"}' -H "Authorization: Bearer YOUR_SECRET_KEY" -H 'Content-Type: application/json' localhost:8081/api/networks/skynet
+
+    Delete Network: curl -X DELETE -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet
+
+    Cycle PublicKeys on all Nodes: curl -X POST -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet/keyupdate
 
-**Cycle PublicKeys on all Nodes:** `curl -X POST -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet/keyupdate`
 
 Access Keys API
 ---------------
@@ -66,13 +72,15 @@ Access Keys API
   
 Access Keys API Call Examples
 -----------------------------
-   
-**Get All Keys:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet/keys | jq`
-  
-**Create Key:** `curl -d '{"uses":10,"name":"mykey"}' -H "Authorization: Bearer YOUR_SECRET_KEY" -H 'Content-Type: application/json' localhost:8081/api/networks/skynet/keys`
-  
-**Delete Key:** `curl -X DELETE -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet/keys/mykey`
-  
+
+.. code-block::
+
+    Get All Keys: curl -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet/keys | jq
+    
+    Create Key: curl -d '{"uses":10,"name":"mykey"}' -H "Authorization: Bearer YOUR_SECRET_KEY" -H 'Content-Type: application/json' localhost:8081/api/networks/skynet/keys
+    
+    Delete Key: curl -X DELETE -H "Authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/networks/skynet/keys/mykey
+
     
 Nodes API
 ---------
@@ -104,29 +112,31 @@ Nodes API
   
 Nodes API Call Examples
 ----------------------- 
-  
-**Get All Nodes:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/nodes | jq`
-  
-**Get Network Nodes:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/nodes/skynet | jq`
+
+.. code-block::
+
+    Get All Nodes: curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/nodes | jq
     
-**Create Node:** `curl  -d  '{ "endpoint": 100.200.100.200, "publickey": aorijqalrik3ajflaqrdajhkr,"macaddress": "8c:90:b5:06:f1:d9","password": "reallysecret","localaddress": "172.16.16.1","accesskey": "aA3bVG0rnItIRXDx","listenport": 6400}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet`
+    Get Network Nodes: curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/nodes/skynet | jq
+        
+    Create Node: curl  -d  '{ "endpoint": 100.200.100.200, "publickey": aorijqalrik3ajflaqrdajhkr,"macaddress": "8c:90:b5:06:f1:d9","password": "reallysecret","localaddress": "172.16.16.1","accesskey": "aA3bVG0rnItIRXDx","listenport": 6400}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet
+        
+    Get Node: curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/nodes/skynet/{macaddress} | jq  
     
-**Get Node:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/nodes/skynet/{macaddress} | jq`  
-  
-**Update Node:** `curl -X PUT -d '{"name":"laptop1"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9`
-  
-**Delete Node:** `curl -X DELETE -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/skynet/nodes/8c:90:b5:06:f1:d9`
-  
-**Create a Gateway:** `curl  -d  '{ "rangestring": "172.31.0.0/16", "interface": "eth0"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9/creategateway`
-  
-**Delete a Gateway:** `curl -X DELETE -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9/deletegateway`
-  
-**Approve a Pending Node:** `curl -X POST -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9/approve`
-  
-**Get Last Modified Date (Last Modified Node in Network):** `curl -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/adm/skynet/lastmodified`
+    Update Node: curl -X PUT -d '{"name":"laptop1"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9
+    
+    Delete Node: curl -X DELETE -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/skynet/nodes/8c:90:b5:06:f1:d9
+    
+    Create a Gateway: curl  -d  '{ "rangestring": "172.31.0.0/16", "interface": "eth0"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9/creategateway
+    
+    Delete a Gateway: curl -X DELETE -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9/deletegateway
+    
+    Approve a Pending Node: curl -X POST -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/skynet/8c:90:b5:06:f1:d9/approve
+    
+    Get Last Modified Date (Last Modified Node in Network): curl -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/nodes/adm/skynet/lastmodified
+
+    Authenticate: curl -d  '{"macaddress": "8c:90:b5:06:f1:d9", "password": "YOUR_PASSWORD"}' -H 'Content-Type: application/json' localhost:8081/api/nodes/adm/skynet/authenticate
 
-**Authenticate:** `curl -d  '{"macaddress": "8c:90:b5:06:f1:d9", "password": "YOUR_PASSWORD"}' -H 'Content-Type: application/json' localhost:8081/api/nodes/adm/skynet/authenticate`
-  
 
 Users API
 -----------------------
@@ -148,19 +158,21 @@ Users API
   
 Users API Calls Examples
 ------------------------
-  
-**Get User:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/users/{username} | jq`
 
-**Update User:** `curl -X PUT -d '{"password":"noonewillguessthis"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/users/{username}`
-  
-**Delete User:** `curl -X DELETE -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/users/{username}`
-  
-**Check for Admin User:** `curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/users/adm/hasadmin`
-  
-**Create Admin User:** `curl -d '{ "username": "smartguy", "password": "YOUR_PASS"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/users/adm/createadmin`
-   
-**Authenticate:** `curl -d  '{"username": "smartguy", "password": "YOUR_PASS"}' -H 'Content-Type: application/json' localhost:8081/api/nodes/adm/skynet/authenticate`
-  
+.. code-block::
+
+    Get User: curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/users/{username} | jq
+
+    Update User: curl -X PUT -d '{"password":"noonewillguessthis"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/users/{username}
+    
+    Delete User: curl -X DELETE -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/users/{username}
+    
+    Check for Admin User: curl -H "Authorization: Bearer YOUR_SECRET_KEY" http://localhost:8081/api/users/adm/hasadmin
+    
+    Create Admin User: curl -d '{ "username": "smartguy", "password": "YOUR_PASS"}' -H 'Content-Type: application/json' -H "authorization: Bearer YOUR_SECRET_KEY" localhost:8081/api/users/adm/createadmin
+    
+    Authenticate: curl -d  '{"username": "smartguy", "password": "YOUR_PASS"}' -H 'Content-Type: application/json' localhost:8081/api/nodes/adm/skynet/authenticate
+
 
 Server Management API
 ---------------------

+ 12 - 2
docs/architecture.rst

@@ -47,7 +47,7 @@ Netmaker
 
 Netmaker is a platform built off of WireGuard which enables users to create mesh networks between their devices. Netmaker can create both full and partial mesh networks depending on the use case.
 
-When we refer to Netmaker in aggregate, we are typically referring to Netmaker and the netclient, as well as other supporting services such as CoreDNS, rqlite, and UI webserver.
+When we refer to Netmaker in aggregate, we are typically referring to Netmaker and the netclient, as well as other supporting services such as CoreDNS, rqlite, and UI webserver. There is also almost always a proxy server / LB, which is typically Caddy.
 
 From an end user perspective, they typically interact with the Netmaker UI, or even just run the install script for the netclient on their devices. The other components run in the background invisibly. 
 
@@ -85,6 +85,7 @@ These modes include client mode and dns mode. Either of these can be disabled bu
 
 The Netmaker server interacts with either sqlite (default), postgres, or rqlite, a distributed version of sqlite, as its database. This DB holds information about nodes, networks, users, and other important data. This data is configuration data. For the most part, Netmaker serves configuration data to Nodes, telling them how they should configure themselves. The Netclient is the agent that actually does that configuration.
 
+The components of the server are usually proxied via Caddy, or an alternative like Nginx and Traefik. The proxy handles SSL certificates to secure traffic, and routes to the UI, API, and gRPC server.
 
 Netclient
 ----------------
@@ -124,6 +125,15 @@ CoreDNS
 
 Netmaker allows users to provide and manage Private DNS for their nodes. This requires a nameserver, and CoreDNS is the chosen nameserver. CoreDNS is lightweight and extensible. CoreDNS loads dns settings from a simple file, managed by Netmaker, and serves out DNS info for managed nodes. DNS can be tricky, and DNS management is currently only supported on a small set of devices, specifically those running systemd-resolved. However, the Netmaker CoreDNS instance can be added manually as a nameserver to other devices. DNS mode can also be turned off.
 
+Caddy
+-------
+
+Caddy is the default proxy for Netmaker if you set it up via Quick Start. Caddy is an extremely simple and docker-friendly proxy, which can be compared to Nginx, Traefik, or HAProxy. We use Caddy by default because of the ease of management, and integration with gRPC. A typical setup for Nginx might take dozens of lines of code, and we need to request and manage SSL certificates separately.
+
+Caddy handles all these things automatically in very few lines of code. You can see our default "Caddyfile" here, which is fed to the container and has all the configuration necessary to configure the proxy for our app:
+
+https://github.com/gravitl/netmaker/blob/master/docker/Caddyfile
+
 External Client
 ----------------
 
@@ -182,7 +192,7 @@ The following systems should be operable natively with Netclient in daemon mode:
         - Raspian.
         - Arch
         - CentOS
-        - CoreOS
+        - Fedora CoreOS
 
 To manage DNS (optional), the node must have systemd-resolved. Systems that have this enabled include:
         - Arch

+ 42 - 0
docs/client-installation.rst

@@ -30,6 +30,48 @@ Windows will by default have firewall rules that prevent inbound connections. If
 
 If you want to allow all peers access, but do not want to configure firewall rules for all peers, you can configure access for one peer, and set it as a Relay Server.
 
+Running the install script
+----------------------------
+
+Some file locations have issues running the install script, such as running from the root C:/ folder. Users have noted the following locations work well for running the install powershell script:
+
+- `C:/Program Files/wireguard`
+- `C:/Windows/System32`
+
+Running netclient commands
+----------------------------
+
+If running the netclient manually ("netclient join", "netclient checkin", "netclient pull") it should be run from outside of the installed directory, which will be either:
+
+- `C:/Program Files/netclient`
+- `C:/ProgramData/netclient`
+
+It is better to call it from a different directory.
+
+High CPU Utilization
+--------------------------
+
+With some versions of WireGuard on Windows, high CPU utilization has been found with the netclient. This is typically due to interaction with the WireGuard GUI component (app). If you're experiencing high CPU utilization, close the WireGuard app. WireGuard will still be running, but the CPU usage should go back down to normal.
+
+Notes on OpenWRT
+===========================
+
+Deploying on OpenWRT depends a lot on the version of OpenWRT and the hardware being used. If the primary installer does not work, there are two things you can try:
+
+1. This community-run package for OpenWRT: https://github.com/sbilly/netmaker-openwrt
+
+2. Manual installation:
+
+- download (wget) the netclient package for your hardware from the netclient releases: https://github.com/gravitl/netmaker/releases
+- rename to "netclient"
+- Run as root from a bash shell on OpenWRT
+
+3. You may experience an issue with the length of the token, which has limits on some OpenWRT shells. If you run into this problem, you can use the following script to convert your token into a "netclient join" command:
+
+- `wget https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/token-convert.sh`
+- ./token-convert <token value>
+- Run the output on your OpenWRT machine
+
 Modes and System Compatibility
 ==================================
 

+ 3 - 3
docs/index.rst

@@ -23,7 +23,7 @@ Netmaker is a platform for creating and managing fast, secure, and dynamic virtu
 
 This documentation covers Netmaker's :doc:`installation <./server-installation>`, :doc:`usage <./usage>`, :doc:`troubleshooting <./support>`, and customization, as well as reference documents for the :doc:`API <./api>`, UI and Agent configuration. All of the `source code <https://github.com/gravitl/netmaker>`_ for Netmaker is on GitHub.
 
-**For Kubernetes-specific guidance, please see the** `Netmaker Kubernetes Documentation. <https://nm-k8s.readthedocs.io>`_
+**For Kubernetes-specific guidance, please see the** `Netmaker Kubernetes Documentation. <https://k8s.netmaker.org>`_
 
 About
 --------
@@ -81,9 +81,9 @@ Kubernetes Documentation
 
 .. toctree::
 
-   Kubernetes <https://nm-k8s.readthedocs.io>
+   Kubernetes <https://k8s.netmaker.org>
    
-`Netmaker Kubernetes Documentation <https://nm-k8s.readthedocs.io>`_
+`Netmaker Kubernetes Documentation <https://k8s.netmaker.org>`_
 
 
 Advanced Server Installation

+ 1 - 1
docs/quick-start.rst

@@ -44,7 +44,7 @@ For information about deploying more advanced configurations, see the :doc:`Adva
 1. Prepare DNS
 ================
 
-Create a wildcard A record pointing to the public IP of your VM. As an example, *.netmaker.example.com.
+Create a wildcard A record pointing to the public IP of your VM. As an example, \*.netmaker.example.com.
 
 Caddy will create 3 subdomains with this wildcard, EX:
 

+ 58 - 8
docs/server-installation.rst

@@ -138,8 +138,31 @@ CLIENT_MODE:
 
     **Description:** Specifies if server should deploy itself as a node (client) in each network. May be turned to "off" for more restricted servers.
 
+RCE:  
+    **Default:** "off"
+
+    **Description:** The server enables you to set PostUp and PostDown commands for nodes, which is standard for WireGuard with wg-quick, but is also **Remote Code Execution**, which is a critical vulnerability if the server is exploited. Because of this, it's turned off by default, but if turned on, PostUp and PostDown become editable.
+
+SERVER_GRPC_WIREGUARD
+    **Depreciated:** no longer in use
+
+DISPLAY_KEYS
+    **Default:** "on"
+
+    **Description:** If "on", will allow you to always show the key values of "access keys". This could be considered a vulnerability, so if turned "off", will only display key values once, and it is up to the users to store the key values locally.
+
+NODE_ID
+    **Default:** <system mac addres>
+
+    **Description:** This setting is used for HA configurations of the server, to identify between different servers. Nodes are given ID's like netmaker-1, netmaker-2, and netmaker-3. If the server is not HA, there is no reason to set this field.
+
+TELEMETRY
+    **Default:** "on"
+
+    **Description:** If "on", the server will send anonymous telemetry data once daily, which is used to improve the product. Data sent includes counts (integer values) for the number of nodes, types of nodes, users, and networks. It also sends the version of the server.
+
 Config File Reference
-----------------------
+-----------------------
 A config file may be placed under config/environments/<env-name>.yml. To read this file at runtime, provide the environment variable NETMAKER_ENV at runtime. For instance, dev.yml paired with ENV=dev. Netmaker will load the specified Config file. This allows you to store and manage configurations for different environments. Below is a reference Config File you may use.
 
 .. literalinclude:: ../config/environments/dev.yaml
@@ -153,6 +176,20 @@ All environment variables and options are enabled in this file. It is the equiva
 .. literalinclude:: ../compose/docker-compose.reference.yml
   :language: YAML
 
+Available docker-compose files
+---------------------------------
+
+The default options for docker-compose can be found here: https://github.com/gravitl/netmaker/tree/master/compose
+
+The following is a brief description of each:
+
+- `docker-compose.contained.yml <https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.contained.yml>`_ - This is the default docker-compose, used in the quick start and deployment script in the README on GitHub. It deploys Netmaker with all options included (Caddy and CoreDNS) and has "self-contained" netclients, meaning they do not affect host networking.
+- `docker-compose.coredns.yml <https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.coredns.yml>`_ - This is a simple compose used to spin up a standalone CoreDNS server. Can be useful if, for instance, you are unning Netmaker on baremetal but need CoreDNS.
+- `docker-compose.hostnetwork.yml <https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.hostnetwork.yml>`_ - This is similar to the docker-compose.contained.yml but with a key difference: it has advanced permissions and mounts host volumes to control networking on the host level.
+- `docker-compose.nocaddy.yml <https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.nocaddy.yml>`_ -= This is the same as docker-compose.contained.yml but without Caddy, in case you need to use a different proxy like Nginx, Traefik, or HAProxy.
+- `docker-compose.nodns.yml <https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.nodns.yml>`_ - This is the same as docker-compose.contained.yml but without CoreDNS, in which case you will not have the Private DNS feature.
+- `docker-compose.reference.yml <https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.reference.yml>`_ - This is the same as docker-compose.contained.yml but with all variable options on display and annotated (it's what we show right above this section). Use this to determine which variables you should add or change in your configuration.
+- `docker-compose.yml <https://github.com/gravitl/netmaker/blob/master/compose/docker-compose.yml>`_ - This is a renamed docker-compose.contained.yml. It is meant only to act as a placeholder for what we consider the "primary" docker-compose that users should work with.
 
 DNS Mode Setup
 ====================================
@@ -224,18 +261,21 @@ This template is equivalent but omits CoreDNS.
 Linux Install without Docker
 =============================
 
-Most systems support Docker, but some do not. In such environments, there are many options for installing Netmaker. Netmaker is available as a binary file, and there is a zip file of the Netmaker UI static HTML on GitHub. Beyond the UI and Server, you need to install MongoDB and CoreDNS (optional). 
-
-To start, we recommend following the Nginx instructions in the :doc:`Quick Install <./quick-start>` guide to enable SSL for your environment.
+Most systems support Docker, but some do not. In such environments, there are many options for installing Netmaker. Netmaker is available as a binary file, and there is a zip file of the Netmaker UI static HTML on GitHub. Beyond the UI and Server, you may want to optionally install a database (sqlite is embedded, rqlite or postgres are supported) and CoreDNS (also optional). 
 
 Once this is enabled and configured for a domain, you can continue with the below. The recommended server runs Ubuntu 20.04.
 
-rqlite Setup
-----------------
+Database Setup (optional)
+--------------------------
+
+You can run the netmaker binary standalone and it will run an embedded sqlite server. Data goes in the data/ directory. Optionally, you can run PostgreSQL or rqlite. Instructions for rqlite are below.
+
 1. Install rqlite on your server: https://github.com/rqlite/rqlite
 
 2. Run rqlite: rqlited -node-id 1 ~/node.1
 
+If using rqlite or postgres, you must change the DATABASE environment/config variable and enter connection details.
+
 Server Setup
 -------------
 1. **Run the install script:** 
@@ -265,8 +305,18 @@ The following uses Nginx as an http server. You may alternatively use Apache or
   sudo sh -c 'BACKEND_URL=http://<YOUR BACKEND API URL>:PORT /usr/share/nginx/html/generate_config_js.sh >/usr/share/nginx/html/config.js'
   sudo systemctl start nginx
 
-CoreDNS Setup
-----------------
+CoreDNS Setup (optional)
+----------------------------
+
+CoreDNS is only required if you want private DNS features. Once installed, you must set the CoreDNS variables in the env settings of the server.
+
+See https://coredns.io/manual/toc/#installation
+
+Proxy / Load Balancer
+------------------------
+
+You will need to proxy connections to your UI and Server. By default the ports are 8081, 8082, and 50051 (grpc). This proxy should handle SSL certificates. We recommend Caddy or Nginx (you can follow the Nginx guide in these docs). The proxy must be able to handle gRPC connections.
+
 
 .. _KubeInstall:
 

+ 27 - 0
docs/support.rst

@@ -39,6 +39,33 @@ We believe the SSPL lets most people run the project the way they want, for both
 
 If you believe the SSPL will negatively impact your ability to use the project, please do not hesitate to reach out.
 
+Telemetry
+==============
+
+As of v0.10.0, Netmaker collects "opt-out" telemetry data. To opt out, simply set "TELEMETRY=off" in your docker-compose file.
+
+Please consider participating in telemetry, as it helps us focus on the features and bug fixes which are most useful to users. Netmaker is a broad platform, and without this data, it is difficult to know where the team should spend its limited resources.
+
+The following is the full list of telemetry data we collect. Besides "Server Version" all data is simply an integer count:
+
+- Randomized server ID
+- Count of nodes
+- Count of "non-server" nodes
+- Count of external clients
+- Count of networks
+- Count of users
+- Count of linux nodes
+- Count of freebsd nodes
+- Count of macos nodes
+- Count of windows nodes
+- Count of docker nodes
+- Count of k8s nodes
+- Server version
+
+We use  `PostHog <https://https://posthog.com/>`_, an open source and trusted framework for telemetry data.
+
+To look at exactly we collect telemetry, you can view the source code under serverctl/telemetry.go: https://github.com/gravitl/netmaker/blob/master/serverctl/telemetry.go
+
 Contact
 ===========
 If you need help, try the discord or open a GitHub ticket.

+ 24 - 0
docs/troubleshoot.rst

@@ -29,6 +29,13 @@ Common Issues
 Server
 -------
 
+**How do I use a private address from the Netmaker Server? How do I contact nodes using their private addresses from the server?**
+  Default nodes appear in each network with the "netmaker" name. These nodes are created by, and attached to, the server. The server is contained in docker, meaning these clients are also contained in docker. Their networking stack is also contained in docker. The "netmaker" nodes are meant to function as network utilities. They assist with UDP Hole Punching and can run Relays, Egress, and Ingress. However, they are meant to stay contained in the server. They do not touch the host networking stack.
+
+  If you want to give the physical server / VM a private IP in the netmaker network, you must deploy an **additional** node using the standard netclient. The only note here is that the server consumes ports 51821-51831, so you will need to give it a port outside this range, e.x. `./netclient join <token> --port 51835`.
+
+  One a netclient is deployed to the underlying server/VM, you will be able to use the private address to reach other nodes from the host, or to reach the server over the private network.
+
 **I upgraded from 0.7 to 0.8 and now I dont have any data in my server!**
   In 0.8, sqlite becomes the default database. If you were running with rqlite, you must set the DATABASE environment variable to rqlite in order to continue using rqlite.
 
@@ -105,6 +112,23 @@ Netclient
 **I have a hard to reach machine behind a firewall or a corporate NAT, what can I do?**
   In this situation you can use the Relay Server functionality introduced in Netmaker v0.8 to designate a node as a relay to your "stuck" machine. Simply click the button to make a node into a relay and tell it to relay traffic to this hard-to-reach peer. 
 
+**I am unable to run the netclient on my OpenWRT machine, what's wrong?**
+  Deploying on OpenWRT depends a lot on the version of OpenWRT and the hardware being used. If the primary installer does not work, there are two things you can try:
+
+  1. This community-run package for OpenWRT: https://github.com/sbilly/netmaker-openwrt
+
+  2. Manual installation:
+
+  - download (wget) the netclient package for your hardware from the netclient releases: https://github.com/gravitl/netmaker/releases
+  - rename to "netclient"
+  - Run as root from a bash shell on OpenWRT
+
+  3. You may experience an issue with the length of the token, which has limits on some OpenWRT shells. If you run into this problem, you can use the following script to convert your token into a "netclient join" command:
+
+  - `wget https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/token-convert.sh`
+  - ./token-convert <token value>
+  - Run the output on your OpenWRT machine
+
 
 CoreDNS
 --------

+ 0 - 25
functions/helpers.go

@@ -31,20 +31,6 @@ func ParseIntClient(value string) (models.IntClient, error) {
 	return intClient, err
 }
 
-//Takes in an arbitrary field and value for field and checks to see if any other
-//node has that value for the same field within the network
-
-// SliceContains - sees if a slice contains something
-func SliceContains(slice []string, item string) bool {
-	set := make(map[string]struct{}, len(slice))
-	for _, s := range slice {
-		set[s] = struct{}{}
-	}
-
-	_, ok := set[item]
-	return ok
-}
-
 // GetPeersList - gets peers for given network
 func GetPeersList(networkName string) ([]models.PeersResponse, error) {
 
@@ -141,17 +127,6 @@ func IsNetworkDisplayNameUnique(name string) (bool, error) {
 	return isunique, nil
 }
 
-// IsMacAddressUnique - checks if mac is unique
-func IsMacAddressUnique(macaddress string, networkName string) (bool, error) {
-
-	_, err := database.FetchRecord(database.NODES_TABLE_NAME, macaddress+"###"+networkName)
-	if err != nil {
-		return database.IsEmptyRecord(err), err
-	}
-
-	return true, nil
-}
-
 // IsKeyValidGlobal - checks if a key is valid globally
 func IsKeyValidGlobal(keyvalue string) bool {
 

+ 6 - 2
functions/local.go

@@ -22,13 +22,16 @@ func SetDNSDir() error {
 	if err != nil {
 		return err
 	}
+
 	_, err = os.Stat(dir + "/config/dnsconfig")
 	if os.IsNotExist(err) {
-		os.Mkdir(dir+"/config/dnsconfig", 0744)
-	} else if err != nil {
+		err = os.MkdirAll(dir+"/config/dnsconfig", 0744)
+	}
+	if err != nil {
 		logger.Log(0, "couldnt find or create /config/dnsconfig")
 		return err
 	}
+
 	_, err = os.Stat(dir + "/config/dnsconfig/Corefile")
 	if os.IsNotExist(err) {
 		err = logic.SetCorefile(".")
@@ -36,6 +39,7 @@ func SetDNSDir() error {
 			logger.Log(0, err.Error())
 		}
 	}
+
 	_, err = os.Stat(dir + "/config/dnsconfig/netmaker.hosts")
 	if os.IsNotExist(err) {
 		_, err = os.Create(dir + "/config/dnsconfig/netmaker.hosts")

+ 12 - 1
go.mod

@@ -3,9 +3,11 @@ module github.com/gravitl/netmaker
 go 1.17
 
 require (
+	github.com/eclipse/paho.mqtt.golang v1.3.5
 	github.com/go-playground/validator/v10 v10.10.0
 	github.com/golang-jwt/jwt/v4 v4.2.0
 	github.com/golang/protobuf v1.5.2 // indirect
+	github.com/google/uuid v1.3.0
 	github.com/gorilla/handlers v1.5.1
 	github.com/gorilla/mux v1.8.0
 	github.com/lib/pq v1.10.4
@@ -23,11 +25,17 @@ require (
 	golang.zx2c4.com/wireguard v0.0.0-20210805125648-3957e9b9dd19 // indirect
 	golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210913210325-91d1988e44de
 	google.golang.org/genproto v0.0.0-20210201151548-94839c025ad4 // indirect
-	google.golang.org/grpc v1.43.0
+	google.golang.org/grpc v1.44.0
 	google.golang.org/protobuf v1.27.1
+	gopkg.in/ini.v1 v1.66.3
 	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
 )
 
+require (
+	github.com/go-ping/ping v0.0.0-20211130115550-779d1e919534
+	github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0
+)
+
 require (
 	cloud.google.com/go v0.34.0 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
@@ -36,6 +44,7 @@ require (
 	github.com/go-playground/locales v0.14.0 // indirect
 	github.com/go-playground/universal-translator v0.18.0 // indirect
 	github.com/google/go-cmp v0.5.5 // indirect
+	github.com/gorilla/websocket v1.4.2 // indirect
 	github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect
 	github.com/leodido/go-urn v1.2.1 // indirect
 	github.com/mdlayher/genetlink v1.0.0 // indirect
@@ -43,5 +52,7 @@ require (
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/russross/blackfriday/v2 v2.0.1 // indirect
 	github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
+	github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
+	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
 	google.golang.org/appengine v1.4.0 // indirect
 )

+ 24 - 2
go.sum

@@ -24,6 +24,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/eclipse/paho.mqtt.golang v1.3.5 h1:sWtmgNxYM9P2sP+xEItMozsR3w0cqZFlqnNN1bdl41Y=
+github.com/eclipse/paho.mqtt.golang v1.3.5/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -34,6 +36,8 @@ github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8S
 github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-ping/ping v0.0.0-20211130115550-779d1e919534 h1:dhy9OQKGBh4zVXbjwbxxHjRxMJtLXj3zfgpBYQaR4Q4=
+github.com/go-ping/ping v0.0.0-20211130115550-779d1e919534/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/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
@@ -70,10 +74,15 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
 github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
 github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
 github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
@@ -124,6 +133,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
 github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 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/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0 h1:Y2hUrkfuM0on62KZOci/VLijlkdF/yeWU262BQgvcjE=
+github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
@@ -154,9 +165,13 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/txn2/txeh v1.3.0 h1:vnbv63htVMZCaQgLqVBxKvj2+HHHFUzNW7I183zjg3E=
 github.com/txn2/txeh v1.3.0/go.mod h1:O7M6gUTPeMF+vsa4c4Ipx3JDkOYrruB1Wry8QRsMcw8=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
+github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
 github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+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=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 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=
@@ -178,6 +193,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@@ -185,6 +201,7 @@ golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
 golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8=
 golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -195,6 +212,8 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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=
@@ -216,6 +235,7 @@ golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/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-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -258,8 +278,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
 google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=
-google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
+google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg=
+google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -279,6 +299,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/ini.v1 v1.66.3 h1:jRskFVxYaMGAMUbN0UZ7niA9gzL9B49DOqE78vg0k3w=
+gopkg.in/ini.v1 v1.66.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 10 - 0
logger/logger.go

@@ -6,6 +6,7 @@ import (
 	"sort"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 )
 
@@ -36,6 +37,9 @@ func ResetLogs() {
 
 // Log - handles adding logs
 func Log(verbosity int, message ...string) {
+	var mu sync.Mutex
+	mu.Lock()
+	defer mu.Unlock()
 	var currentTime = time.Now()
 	var currentMessage = makeString(message...)
 	if int32(verbosity) <= getVerbose() && getVerbose() >= 0 {
@@ -51,6 +55,9 @@ func Dump() string {
 		Key   string
 		Value time.Time
 	}
+	var mu sync.Mutex
+	mu.Lock()
+	defer mu.Unlock()
 	var dumpLogs = make([]keyVal, 0, len(currentLogs))
 	for key, value := range currentLogs {
 		parsedTime, err := time.Parse(TimeFormat, value)
@@ -98,6 +105,9 @@ func Retrieve(filePath string) string {
 
 // FatalLog - exits os after logging
 func FatalLog(message ...string) {
+	var mu sync.Mutex
+	mu.Lock()
+	defer mu.Unlock()
 	fmt.Printf("[netmaker] Fatal: %s \n", makeString(message...))
 	os.Exit(2)
 }

+ 4 - 2
logic/dns.go

@@ -115,10 +115,12 @@ func SetCorefile(domains string) error {
 	if err != nil {
 		return err
 	}
+
 	_, err = os.Stat(dir + "/config/dnsconfig")
 	if os.IsNotExist(err) {
-		os.Mkdir(dir+"/config/dnsconfig", 744)
-	} else if err != nil {
+		err = os.MkdirAll(dir+"/config/dnsconfig", 744)
+	}
+	if err != nil {
 		logger.Log(0, "couldnt find or create /config/dnsconfig")
 		return err
 	}

+ 2 - 2
logic/extpeers.go

@@ -11,7 +11,7 @@ import (
 )
 
 // GetExtPeersList - gets the ext peers lists
-func GetExtPeersList(macaddress string, networkName string) ([]models.ExtPeersResponse, error) {
+func GetExtPeersList(node *models.Node) ([]models.ExtPeersResponse, error) {
 
 	var peers []models.ExtPeersResponse
 	records, err := database.FetchRecords(database.EXT_CLIENT_TABLE_NAME)
@@ -33,7 +33,7 @@ func GetExtPeersList(macaddress string, networkName string) ([]models.ExtPeersRe
 			logger.Log(2, "failed to unmarshal ext client")
 			continue
 		}
-		if extClient.Network == networkName && extClient.IngressGatewayID == macaddress {
+		if extClient.Network == node.Network && extClient.IngressGatewayID == node.ID {
 			peers = append(peers, peer)
 		}
 	}

+ 20 - 32
logic/gateway.go

@@ -13,13 +13,13 @@ import (
 
 // CreateEgressGateway - creates an egress gateway
 func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, error) {
-	node, err := GetNodeByMacAddress(gateway.NetID, gateway.NodeID)
-	if node.OS == "windows" || node.OS == "macos" { // add in darwin later
-		return models.Node{}, errors.New(node.OS + " is unsupported for egress gateways")
-	}
+	node, err := GetNodeByID(gateway.NodeID)
 	if err != nil {
 		return models.Node{}, err
 	}
+	if node.OS != "linux" { // add in darwin later
+		return models.Node{}, errors.New(node.OS + " is unsupported for egress gateways")
+	}
 	err = ValidateEgressGateway(gateway)
 	if err != nil {
 		return models.Node{}, err
@@ -44,10 +44,7 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
 			postDownCmd = node.PostDown + "; " + postDownCmd
 		}
 	}
-	key, err := GetRecordKey(gateway.NodeID, gateway.NetID)
-	if err != nil {
-		return node, err
-	}
+
 	node.PostUp = postUpCmd
 	node.PostDown = postDownCmd
 	node.SetLastModified()
@@ -56,7 +53,7 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
 	if err != nil {
 		return node, err
 	}
-	if err = database.Insert(key, string(nodeData), database.NODES_TABLE_NAME); err != nil {
+	if err = database.Insert(node.ID, string(nodeData), database.NODES_TABLE_NAME); err != nil {
 		return models.Node{}, err
 	}
 	if err = NetworkNodesUpdatePullChanges(node.Network); err != nil {
@@ -65,6 +62,7 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
 	return node, nil
 }
 
+// ValidateEgressGateway - validates the egress gateway model
 func ValidateEgressGateway(gateway models.EgressGatewayRequest) error {
 	var err error
 
@@ -80,9 +78,9 @@ func ValidateEgressGateway(gateway models.EgressGatewayRequest) error {
 }
 
 // DeleteEgressGateway - deletes egress from node
-func DeleteEgressGateway(network, macaddress string) (models.Node, error) {
+func DeleteEgressGateway(network, nodeid string) (models.Node, error) {
 
-	node, err := GetNodeByMacAddress(network, macaddress)
+	node, err := GetNodeByID(nodeid)
 	if err != nil {
 		return models.Node{}, err
 	}
@@ -97,15 +95,12 @@ func DeleteEgressGateway(network, macaddress string) (models.Node, error) {
 	}
 	node.SetLastModified()
 	node.PullChanges = "yes"
-	key, err := GetRecordKey(node.MacAddress, node.Network)
-	if err != nil {
-		return models.Node{}, err
-	}
+
 	data, err := json.Marshal(&node)
 	if err != nil {
 		return models.Node{}, err
 	}
-	if err = database.Insert(key, string(data), database.NODES_TABLE_NAME); err != nil {
+	if err = database.Insert(node.ID, string(data), database.NODES_TABLE_NAME); err != nil {
 		return models.Node{}, err
 	}
 	if err = NetworkNodesUpdatePullChanges(network); err != nil {
@@ -115,10 +110,10 @@ func DeleteEgressGateway(network, macaddress string) (models.Node, error) {
 }
 
 // CreateIngressGateway - creates an ingress gateway
-func CreateIngressGateway(netid string, macaddress string) (models.Node, error) {
+func CreateIngressGateway(netid string, nodeid string) (models.Node, error) {
 
-	node, err := GetNodeByMacAddress(netid, macaddress)
-	if node.OS == "windows" || node.OS == "macos" { // add in darwin later
+	node, err := GetNodeByID(nodeid)
+	if node.OS != "linux" { // add in darwin later
 		return models.Node{}, errors.New(node.OS + " is unsupported for ingress gateways")
 	}
 
@@ -149,15 +144,12 @@ func CreateIngressGateway(netid string, macaddress string) (models.Node, error)
 	node.PostDown = postDownCmd
 	node.PullChanges = "yes"
 	node.UDPHolePunch = "no"
-	key, err := GetRecordKey(node.MacAddress, node.Network)
-	if err != nil {
-		return models.Node{}, err
-	}
+
 	data, err := json.Marshal(&node)
 	if err != nil {
 		return models.Node{}, err
 	}
-	err = database.Insert(key, string(data), database.NODES_TABLE_NAME)
+	err = database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
 	if err != nil {
 		return models.Node{}, err
 	}
@@ -166,9 +158,9 @@ func CreateIngressGateway(netid string, macaddress string) (models.Node, error)
 }
 
 // DeleteIngressGateway - deletes an ingress gateway
-func DeleteIngressGateway(networkName string, macaddress string) (models.Node, error) {
+func DeleteIngressGateway(networkName string, nodeid string) (models.Node, error) {
 
-	node, err := GetNodeByMacAddress(networkName, macaddress)
+	node, err := GetNodeByID(nodeid)
 	if err != nil {
 		return models.Node{}, err
 	}
@@ -177,7 +169,7 @@ func DeleteIngressGateway(networkName string, macaddress string) (models.Node, e
 		return models.Node{}, err
 	}
 	// delete ext clients belonging to ingress gateway
-	if err = DeleteGatewayExtClients(macaddress, networkName); err != nil {
+	if err = DeleteGatewayExtClients(node.ID, networkName); err != nil {
 		return models.Node{}, err
 	}
 
@@ -187,15 +179,11 @@ func DeleteIngressGateway(networkName string, macaddress string) (models.Node, e
 	node.IngressGatewayRange = ""
 	node.PullChanges = "yes"
 
-	key, err := GetRecordKey(node.MacAddress, node.Network)
-	if err != nil {
-		return models.Node{}, err
-	}
 	data, err := json.Marshal(&node)
 	if err != nil {
 		return models.Node{}, err
 	}
-	err = database.Insert(key, string(data), database.NODES_TABLE_NAME)
+	err = database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
 	if err != nil {
 		return models.Node{}, err
 	}

+ 7 - 6
logic/jwts.go

@@ -12,11 +12,12 @@ import (
 var jwtSecretKey = []byte("(BytesOverTheWire)")
 
 // CreateJWT func will used to create the JWT while signing in and signing out
-func CreateJWT(macaddress string, network string) (response string, err error) {
+func CreateJWT(uuid string, macAddress string, network string) (response string, err error) {
 	expirationTime := time.Now().Add(5 * time.Minute)
 	claims := &models.Claims{
-		MacAddress: macaddress,
+		ID:         uuid,
 		Network:    network,
+		MacAddress: macAddress,
 		StandardClaims: jwt.StandardClaims{
 			ExpiresAt: expirationTime.Unix(),
 		},
@@ -73,13 +74,13 @@ func VerifyUserToken(tokenString string) (username string, networks []string, is
 }
 
 // VerifyToken - gRPC [nodes] Only
-func VerifyToken(tokenString string) (macaddress string, network string, err error) {
+func VerifyToken(tokenString string) (nodeID string, mac string, network string, err error) {
 	claims := &models.Claims{}
 
 	//this may be a stupid way of serving up a master key
 	//TODO: look into a different method. Encryption?
 	if tokenString == servercfg.GetMasterKey() {
-		return "mastermac", "", nil
+		return "mastermac", "", "", nil
 	}
 
 	token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
@@ -87,7 +88,7 @@ func VerifyToken(tokenString string) (macaddress string, network string, err err
 	})
 
 	if token != nil {
-		return claims.MacAddress, claims.Network, nil
+		return claims.ID, claims.MacAddress, claims.Network, nil
 	}
-	return "", "", err
+	return "", "", "", err
 }

+ 82 - 14
logic/networks.go

@@ -1,6 +1,7 @@
 package logic
 
 import (
+	"encoding/binary"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -47,7 +48,7 @@ func DeleteNetwork(network string) error {
 		servers, err := GetSortedNetworkServerNodes(network)
 		if err == nil {
 			for _, s := range servers {
-				if err = DeleteNode(&s, true); err != nil {
+				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)
@@ -110,7 +111,6 @@ func NetworkNodesUpdatePullChanges(networkName string) error {
 			if err != nil {
 				return err
 			}
-			node.SetID()
 			database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
 		}
 	}
@@ -206,6 +206,43 @@ func UniqueAddress(networkName string) (string, error) {
 	return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", err1
 }
 
+// UniqueAddressServer - get unique address starting from last available
+func UniqueAddressServer(networkName string) (string, error) {
+
+	var network models.Network
+	network, err := GetParentNetwork(networkName)
+	if err != nil {
+		logger.Log(0, "UniqueAddressServer encountered  an error")
+		return "666", err
+	}
+
+	_, ipv4Net, err := net.ParseCIDR(network.AddressRange)
+	if err != nil {
+		logger.Log(0, "UniqueAddressServer encountered  an error")
+		return "666", err
+	}
+
+	// convert IPNet struct mask and address to uint32
+	// network is BigEndian
+	mask := binary.BigEndian.Uint32(ipv4Net.Mask)
+	start := binary.BigEndian.Uint32(ipv4Net.IP)
+
+	// find the final address
+	finish := (start & mask) | (mask ^ 0xffffffff)
+
+	// loop through addresses as uint32
+	for i := finish - 1; i > start; i-- {
+		// convert back to net.IP
+		ip := make(net.IP, 4)
+		binary.BigEndian.PutUint32(ip, i)
+		if IsIPUnique(networkName, ip.String(), database.NODES_TABLE_NAME, false) && IsIPUnique(networkName, ip.String(), database.EXT_CLIENT_TABLE_NAME, false) {
+			return ip.String(), err
+		}
+	}
+
+	return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", fmt.Errorf("no unique server addresses found")
+}
+
 // IsIPUnique - checks if an IP is unique
 func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool {
 
@@ -352,10 +389,9 @@ func UpdateNetworkLocalAddresses(networkName string) error {
 			node.Address = ipaddr
 			newNodeData, err := json.Marshal(&node)
 			if err != nil {
-				fmt.Println("error in node  address assignment!")
+				logger.Log(1, "error in node  address assignment!")
 				return err
 			}
-			node.SetID()
 			database.Insert(node.ID, string(newNodeData), database.NODES_TABLE_NAME)
 		}
 	}
@@ -363,6 +399,28 @@ func UpdateNetworkLocalAddresses(networkName string) error {
 	return nil
 }
 
+// UpdateNetworkLocalAddresses - updates network localaddresses
+func UpdateNetworkHolePunching(networkName string, holepunch string) error {
+
+	nodes, err := GetNetworkNodes(networkName)
+	if err != nil {
+		return err
+	}
+
+	for _, node := range nodes {
+		if node.IsServer != "yes" {
+			node.UDPHolePunch = holepunch
+			newNodeData, err := json.Marshal(&node)
+			if err != nil {
+				logger.Log(1, "error in node hole punch assignment")
+				return err
+			}
+			database.Insert(node.ID, string(newNodeData), database.NODES_TABLE_NAME)
+		}
+	}
+	return nil
+}
+
 // RemoveNetworkNodeIPv6Addresses - removes network node IPv6 addresses
 func RemoveNetworkNodeIPv6Addresses(networkName string) error {
 
@@ -387,7 +445,6 @@ func RemoveNetworkNodeIPv6Addresses(networkName string) error {
 			if err != nil {
 				return err
 			}
-			node.SetID()
 			database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
 		}
 	}
@@ -424,7 +481,6 @@ func UpdateNetworkNodeAddresses(networkName string) error {
 			if err != nil {
 				return err
 			}
-			node.SetID()
 			database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
 		}
 	}
@@ -475,23 +531,24 @@ func IsNetworkNameUnique(network *models.Network) (bool, error) {
 }
 
 // UpdateNetwork - updates a network with another network's fields
-func UpdateNetwork(currentNetwork *models.Network, newNetwork *models.Network) (bool, bool, error) {
+func UpdateNetwork(currentNetwork *models.Network, newNetwork *models.Network) (bool, bool, bool, error) {
 	if err := ValidateNetwork(newNetwork, true); err != nil {
-		return false, false, err
+		return false, false, false, err
 	}
 	if newNetwork.NetID == currentNetwork.NetID {
 		hasrangeupdate := newNetwork.AddressRange != currentNetwork.AddressRange
 		localrangeupdate := newNetwork.LocalRange != currentNetwork.LocalRange
+		hasholepunchupdate := newNetwork.DefaultUDPHolePunch != currentNetwork.DefaultUDPHolePunch
 		data, err := json.Marshal(newNetwork)
 		if err != nil {
-			return false, false, err
+			return false, false, false, err
 		}
 		newNetwork.SetNetworkLastModified()
 		err = database.Insert(newNetwork.NetID, string(data), database.NETWORKS_TABLE_NAME)
-		return hasrangeupdate, localrangeupdate, err
+		return hasrangeupdate, localrangeupdate, hasholepunchupdate, err
 	}
 	// copy values
-	return false, false, errors.New("failed to update network " + newNetwork.NetID + ", cannot change netid.")
+	return false, false, false, errors.New("failed to update network " + newNetwork.NetID + ", cannot change netid.")
 }
 
 // Inc - increments an IP
@@ -602,6 +659,18 @@ func KeyUpdate(netname string) (models.Network, error) {
 	return models.Network{}, nil
 }
 
+//SaveNetwork - save network struct to database
+func SaveNetwork(network *models.Network) error {
+	data, err := json.Marshal(network)
+	if err != nil {
+		return err
+	}
+	if err := database.Insert(network.NetID, string(data), database.NETWORKS_TABLE_NAME); err != nil {
+		return err
+	}
+	return nil
+}
+
 // == Private ==
 
 func networkNodesUpdateAction(networkName string, action string) error {
@@ -630,7 +699,6 @@ func networkNodesUpdateAction(networkName string, action string) error {
 			if err != nil {
 				return err
 			}
-			node.SetID()
 			database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
 		}
 	}
@@ -684,7 +752,7 @@ func isInterfacePresent(iface string, address string) (string, bool) {
 		}
 		for _, addr := range currAddrs {
 			if strings.Contains(addr.String(), address) && currIface.Name != iface {
-				logger.Log(2, "found iface", addr.String(), currIface.Name)
+				// logger.Log(2, "found iface", addr.String(), currIface.Name)
 				interfaces = nil
 				currAddrs = nil
 				return currIface.Name, false
@@ -693,6 +761,6 @@ func isInterfacePresent(iface string, address string) (string, bool) {
 		currAddrs = nil
 	}
 	interfaces = nil
-	logger.Log(2, "failed to find iface", iface)
+	// logger.Log(2, "failed to find iface", iface)
 	return "", true
 }

+ 264 - 25
logic/nodes.go

@@ -8,29 +8,24 @@ import (
 	"time"
 
 	"github.com/go-playground/validator/v10"
+	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/netclient/ncutils"
+	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/validation"
+	"golang.org/x/crypto/bcrypt"
 )
 
 // GetNetworkNodes - gets the nodes of a network
 func GetNetworkNodes(network string) ([]models.Node, error) {
 	var nodes []models.Node
-	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
+	allnodes, err := GetAllNodes()
 	if err != nil {
-		if database.IsEmptyRecord(err) {
-			return []models.Node{}, nil
-		}
-		return nodes, err
+		return []models.Node{}, err
 	}
-	for _, value := range collection {
-
-		var node models.Node
-		err := json.Unmarshal([]byte(value), &node)
-		if err != nil {
-			continue
-		}
+	for _, node := range allnodes {
 		if node.Network == network {
 			nodes = append(nodes, node)
 		}
@@ -63,9 +58,24 @@ func GetSortedNetworkServerNodes(network string) ([]models.Node, error) {
 	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(network, macaddress string) (models.Node, error) {
-	node, err := GetNodeByMacAddress(network, macaddress)
+func UncordonNode(nodeid string) (models.Node, error) {
+	node, err := GetNodeByID(nodeid)
 	if err != nil {
 		return models.Node{}, err
 	}
@@ -76,19 +86,15 @@ func UncordonNode(network, macaddress string) (models.Node, error) {
 	if err != nil {
 		return node, err
 	}
-	key, err := GetRecordKey(node.MacAddress, node.Network)
-	if err != nil {
-		return node, err
-	}
 
-	err = database.Insert(key, string(data), database.NODES_TABLE_NAME)
+	err = database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
 	return node, err
 }
 
-// GetPeers - gets the peers of a given node
+// GetPeers - gets the peers of a given server node
 func GetPeers(node *models.Node) ([]models.Node, error) {
 	if IsLeader(node) {
-		SetNetworkServerPeers(node)
+		setNetworkServerPeers(node)
 	}
 	excludeIsRelayed := node.IsRelay != "yes"
 	var relayedNode string
@@ -102,6 +108,13 @@ func GetPeers(node *models.Node) ([]models.Node, error) {
 	return peers, nil
 }
 
+// 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)
@@ -121,11 +134,23 @@ func IsLeader(node *models.Node) bool {
 
 // UpdateNode - takes a node and updates another node with it's values
 func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
+	if newNode.Address != currentNode.Address {
+		if network, err := GetParentNetwork(newNode.Network); err == nil {
+			if !IsAddressInCIDR(newNode.Address, network.AddressRange) {
+				return fmt.Errorf("invalid address provided; out of network range for node %s", newNode.ID)
+			}
+		}
+	}
 	newNode.Fill(currentNode)
+
+	if currentNode.IsServer == "yes" && !validateServer(currentNode, newNode) {
+		return fmt.Errorf("this operation is not supported on server nodes")
+	}
+
+	// check for un-settable server values
 	if err := ValidateNode(newNode, true); err != nil {
 		return err
 	}
-	newNode.SetID()
 	if newNode.ID == currentNode.ID {
 		newNode.SetLastModified()
 		if data, err := json.Marshal(newNode); err != nil {
@@ -134,7 +159,7 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
 			return database.Insert(newNode.ID, string(data), database.NODES_TABLE_NAME)
 		}
 	}
-	return fmt.Errorf("failed to update node " + newNode.MacAddress + ", cannot change macaddress.")
+	return fmt.Errorf("failed to update node " + currentNode.ID + ", cannot change ID.")
 }
 
 // IsNodeIDUnique - checks if node id is unique
@@ -150,8 +175,12 @@ func ValidateNode(node *models.Node, isUpdate bool) error {
 		if isUpdate {
 			return true
 		}
+		var unique = true
+		if !(node.MacAddress == "") {
+			unique, _ = isMacAddressUnique(node.MacAddress, node.Network)
+		}
 		isFieldUnique, _ := IsNodeIDUnique(node)
-		return isFieldUnique
+		return isFieldUnique && unique
 	})
 	_ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
 		_, err := GetNetworkByNode(node)
@@ -169,6 +198,80 @@ func ValidateNode(node *models.Node, isUpdate bool) error {
 	return err
 }
 
+// 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)
+
+	if node.IsServer == "yes" {
+		if node.Address, err = UniqueAddressServer(node.Network); err != nil {
+			return err
+		}
+	} else if node.Address == "" {
+		if node.Address, err = UniqueAddress(node.Network); 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 node.Address6, err = UniqueAddress6(node.Network); 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 == "" {
+		//returnErrorResponse(w, r, errorResponse)
+		return err
+	}
+	err = ValidateNode(node, false)
+	if err != nil {
+		return err
+	}
+
+	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
+	}
+	if node.IsPending != "yes" {
+		DecrimentKey(node.Network, node.AccessKey)
+	}
+	SetNetworkNodesLastModified(node.Network)
+	if servercfg.IsDNSMode() {
+		err = SetDNS()
+	}
+	return err
+}
+
 // GetAllNodes - returns all nodes in the DB
 func GetAllNodes() ([]models.Node, error) {
 	var nodes []models.Node
@@ -285,7 +388,6 @@ func SetNodeDefaults(node *models.Node) {
 	node.SetRoamingDefault()
 	node.SetPullChangesDefault()
 	node.SetDefaultAction()
-	node.SetID()
 	node.SetIsServerDefault()
 	node.SetIsStaticDefault()
 	node.SetDefaultEgressGateway()
@@ -294,10 +396,13 @@ func SetNodeDefaults(node *models.Node) {
 	node.SetDefaultMTU()
 	node.SetDefaultIsRelayed()
 	node.SetDefaultIsRelay()
+	node.SetDefaultIsDocker()
+	node.SetDefaultIsK8S()
 	node.KeyUpdateTimeStamp = time.Now().Unix()
 }
 
 // GetRecordKey - get record key
+// depricated
 func GetRecordKey(id string, network string) (string, error) {
 	if id == "" || network == "" {
 		return "", errors.New("unable to get record key")
@@ -329,6 +434,21 @@ func GetNodeByMacAddress(network string, macaddress string) (models.Node, error)
 	return node, nil
 }
 
+// GetNodesByAddress - gets a node by mac address
+func GetNodesByAddress(network string, addresses []string) ([]models.Node, error) {
+	var nodes []models.Node
+	allnodes, err := GetAllNodes()
+	if err != nil {
+		return []models.Node{}, err
+	}
+	for _, node := range allnodes {
+		if node.Network == network && ncutils.StringSliceContains(addresses, node.Address) {
+			nodes = append(nodes, node)
+		}
+	}
+	return nodes, nil
+}
+
 // GetDeletedNodeByMacAddress - get a deleted node
 func GetDeletedNodeByMacAddress(network string, macaddress string) (models.Node, error) {
 
@@ -380,3 +500,122 @@ func GetNodeRelay(network string, relayedNodeAddr string) (models.Node, error) {
 	}
 	return relay, errors.New("could not find relay for node " + relayedNodeAddr)
 }
+
+// GetNodeByIDorMacAddress - gets the node, if a mac address exists, but not id, then it should delete it and recreate in DB with new ID
+/*
+func GetNodeByIDorMacAddress(uuid string, macaddress string, network string) (models.Node, error) {
+	var node models.Node
+	var err error
+	node, err = GetNodeByID(uuid)
+	if err != nil && macaddress != "" && network != "" {
+		node, err = GetNodeByMacAddress(network, macaddress)
+		if err != nil {
+			return models.Node{}, err
+		}
+		err = DeleteNodeByMacAddress(&node, true) // remove node
+		if err != nil {
+			return models.Node{}, err
+		}
+		err = CreateNode(&node)
+		if err != nil {
+			return models.Node{}, err
+		}
+		logger.Log(2, "rewriting legacy node data; node now has id,", node.ID)
+		node.PullChanges = "yes"
+	}
+	return node, err
+}
+*/
+// GetNodeByID - get node by uuid, should have been set by create
+func GetNodeByID(uuid string) (models.Node, error) {
+	var record, err = database.FetchRecord(database.NODES_TABLE_NAME, uuid)
+	if err != nil {
+		return models.Node{}, err
+	}
+	var node models.Node
+	if err = json.Unmarshal([]byte(record), &node); err != nil {
+		return models.Node{}, err
+	}
+	return node, nil
+}
+
+// GetDeletedNodeByID - get a deleted node
+func GetDeletedNodeByID(uuid string) (models.Node, error) {
+
+	var node models.Node
+
+	record, err := database.FetchRecord(database.DELETED_NODES_TABLE_NAME, uuid)
+	if err != nil {
+		return models.Node{}, err
+	}
+
+	if err = json.Unmarshal([]byte(record), &node); err != nil {
+		return models.Node{}, err
+	}
+
+	SetNodeDefaults(&node)
+
+	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")
+}
+
+// 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")
+}
+
+// isMacAddressUnique - checks if mac is unique
+func isMacAddressUnique(macaddress string, networkName string) (bool, error) {
+
+	isunique := true
+
+	nodes, err := GetNetworkNodes(networkName)
+	if err != nil {
+		if database.IsEmptyRecord(err) {
+			return true, nil
+		}
+		return false, err
+	}
+
+	for _, node := range nodes {
+
+		if node.MacAddress == macaddress {
+			isunique = false
+		}
+	}
+
+	return isunique, nil
+}

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