Browse Source

Merge branch 'develop' into NET-39/nm-quick-refactor

# Conflicts:
#	docker/Caddyfile
#	scripts/nm-quick.sh
Tobias Cudnik 2 years ago
parent
commit
a97e27cd6c

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

@@ -31,6 +31,7 @@ body:
       label: Version
       label: Version
       description: What version are you running?
       description: What version are you running?
       options:
       options:
+        - v0.19.0
         - v0.18.7
         - v0.18.7
         - v0.18.6
         - v0.18.6
         - v0.18.5
         - v0.18.5

+ 8 - 6
.github/workflows/deletedroplets.yml

@@ -23,7 +23,7 @@ jobs:
           webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
           webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
           color: "#42f545"
           color: "#42f545"
           username: "GitHub Bot"
           username: "GitHub Bot"
-          message: " ${{ github.event.workflow_run.name }} was successful"
+          message: "${{ github.respository }}: ${{ github.event.workflow_run.name }} was successful"
           file: ./results/results.log
           file: ./results/results.log
       - name: discord server message
       - name: discord server message
         uses: appleboy/discord-action@master
         uses: appleboy/discord-action@master
@@ -32,9 +32,10 @@ jobs:
           webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
           webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
           color: "#42f545"
           color: "#42f545"
           username: "GitHub Bot"
           username: "GitHub Bot"
-          message: "droplets from this workflow will be deleted in 15 min"
+          message: "droplets from this workflow (tag ${{ github.event.workflow_run.id }}-{{ $github.event.workflow_run.run_number }}) will be deleted in 15 min"
           file: ./server/serverinfo.txt
           file: ./server/serverinfo.txt
       - name: delete droplets
       - name: delete droplets
+        if: success() || failure()
         run: |
         run: |
           sleep 15m
           sleep 15m
           curl -X GET \
           curl -X GET \
@@ -59,18 +60,18 @@ jobs:
         with:
         with:
           webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }}
           webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }}
           webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
           webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
-          color: "#42f545"
+          color: "#990000"
           username: "GitHub Bot"
           username: "GitHub Bot"
-          message: " ${{ github.event.workflow_run.name }} failed"
+          message: "${{ github.respository }}: ${{ github.event.workflow_run.name }} failed"
           file: ./results/results.log
           file: ./results/results.log
       - name: discord server message
       - name: discord server message
         uses: appleboy/discord-action@master
         uses: appleboy/discord-action@master
         with:
         with:
           webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }}
           webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }}
           webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
           webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
-          color: "#42f545"
+          color: "#990000"
           username: "GitHub Bot"
           username: "GitHub Bot"
-          message: "droplets from this workflow will be deleted in 6 hours"
+          message: "droplets from this workflow (tag ${{ github.event.workflow_run.id }}-{{ $github.event.workflow_run.run_number }}) will be deleted in 6 hours"
           file: ./server/serverinfo.txt
           file: ./server/serverinfo.txt
       - name: discord error message
       - name: discord error message
         uses: appleboy/discord-action@master
         uses: appleboy/discord-action@master
@@ -82,6 +83,7 @@ jobs:
           message: "errors from ${{ github.event.workflow_run.name }}"
           message: "errors from ${{ github.event.workflow_run.name }}"
           file: ./results/errors.log
           file: ./results/errors.log
       - name: delete droplets
       - name: delete droplets
+        if: success() || failure()
         run: |
         run: |
           sleep 6h
           sleep 6h
           curl -X GET \
           curl -X GET \

+ 1 - 1
Dockerfile

@@ -6,7 +6,7 @@ COPY . .
 
 
 RUN GOOS=linux CGO_ENABLED=1 go build -ldflags="-s -w " -tags ${tags} .
 RUN GOOS=linux CGO_ENABLED=1 go build -ldflags="-s -w " -tags ${tags} .
 # RUN go build -tags=ee . -o netmaker main.go
 # RUN go build -tags=ee . -o netmaker main.go
-FROM alpine:3.16.2
+FROM alpine:3.17.2
 
 
 # add a c lib
 # add a c lib
 # set the working directory
 # set the working directory

+ 1 - 1
Dockerfile-quick

@@ -1,5 +1,5 @@
 #first stage - builder
 #first stage - builder
-FROM alpine:3.15.2
+FROM alpine:3.17.2
 ARG version 
 ARG version 
 WORKDIR /app
 WORKDIR /app
 COPY ./netmaker /root/netmaker
 COPY ./netmaker /root/netmaker

+ 1 - 1
README.md

@@ -17,7 +17,7 @@
 
 
 <p align="center">
 <p align="center">
   <a href="https://github.com/gravitl/netmaker/releases">
   <a href="https://github.com/gravitl/netmaker/releases">
-    <img src="https://img.shields.io/badge/Version-0.18.7-informational?style=flat-square" />
+    <img src="https://img.shields.io/badge/Version-0.19.0-informational?style=flat-square" />
   </a>
   </a>
   <a href="https://hub.docker.com/r/gravitl/netmaker/tags">
   <a href="https://hub.docker.com/r/gravitl/netmaker/tags">
     <img src="https://img.shields.io/docker/pulls/gravitl/netmaker?label=downloads" />
     <img src="https://img.shields.io/docker/pulls/gravitl/netmaker?label=downloads" />

+ 2 - 2
compose/docker-compose-emqx.yml

@@ -3,7 +3,7 @@ version: "3.4"
 services:
 services:
   netmaker:
   netmaker:
     container_name: netmaker
     container_name: netmaker
-    image: gravitl/netmaker:v0.18.7
+    image: gravitl/netmaker:v0.19.0
     restart: on-failure
     restart: on-failure
     volumes:
     volumes:
       - dnsconfig:/root/config/dnsconfig
       - dnsconfig:/root/config/dnsconfig
@@ -36,7 +36,7 @@ services:
       - "3478:3478/udp"
       - "3478:3478/udp"
   netmaker-ui:
   netmaker-ui:
     container_name: netmaker-ui
     container_name: netmaker-ui
-    image: gravitl/netmaker-ui:v0.18.7
+    image: gravitl/netmaker-ui:v0.19.0
     depends_on:
     depends_on:
       - netmaker
       - netmaker
     links:
     links:

+ 22 - 0
compose/docker-compose.ee.yml

@@ -33,6 +33,12 @@ services:
       LICENSE_KEY: "YOUR_LICENSE_KEY"
       LICENSE_KEY: "YOUR_LICENSE_KEY"
       NETMAKER_ACCOUNT_ID: "YOUR_ACCOUNT_ID"
       NETMAKER_ACCOUNT_ID: "YOUR_ACCOUNT_ID"
       DEFAULT_PROXY_MODE: "off"
       DEFAULT_PROXY_MODE: "off"
+      TURN_SERVER_HOST: "turn.NETMAKER_BASE_DOMAIN"
+      TURN_SERVER_API_HOST: "https://turnapi.NETMAKER_BASE_DOMAIN"
+      TURN_PORT: "3479"
+      TURN_USERNAME: "REPLACE_TURN_USERNAME"
+      TURN_PASSWORD: "REPLACE_TURN_PASSWORD"
+      USE_TURN: "true"
     ports:
     ports:
       - "3478:3478/udp"
       - "3478:3478/udp"
   netmaker-ui:
   netmaker-ui:
@@ -119,6 +125,21 @@ services:
       API_PORT: "8085"
       API_PORT: "8085"
       LICENSE_KEY: "YOUR_LICENSE_KEY"
       LICENSE_KEY: "YOUR_LICENSE_KEY"
       PROMETHEUS_HOST: https://prometheus.NETMAKER_BASE_DOMAIN
       PROMETHEUS_HOST: https://prometheus.NETMAKER_BASE_DOMAIN
+  turn:
+    container_name: turn
+    image: gravitl/turnserver:v1.0.0
+    network_mode: "host"
+    volumes:
+      - turn_server:/etc/config
+    environment:
+      DEBUG_MODE: "off"
+      VERBOSITY: "1"
+      TURN_PORT: "3479"
+      TURN_API_PORT: "8089"
+      CORS_ALLOWED_ORIGIN: "*"
+      TURN_SERVER_HOST: "turn.NETMAKER_BASE_DOMAIN"
+      USERNAME: "REPLACE_TURN_USERNAME"
+      PASSWORD: "REPLACE_TURN_PASSWORD"
 volumes:
 volumes:
   caddy_data: {}
   caddy_data: {}
   caddy_conf: {}
   caddy_conf: {}
@@ -127,3 +148,4 @@ volumes:
   mosquitto_logs: {}
   mosquitto_logs: {}
   prometheus_data: {}
   prometheus_data: {}
   grafana_data: {}
   grafana_data: {}
+  turn_server: {}

+ 1 - 1
compose/docker-compose.netclient.yml

@@ -3,7 +3,7 @@ version: "3.4"
 services:
 services:
   netclient:
   netclient:
     container_name: netclient
     container_name: netclient
-    image: 'gravitl/netclient:v0.18.7'
+    image: 'gravitl/netclient:v0.19.0'
     hostname: netmaker-1
     hostname: netmaker-1
     network_mode: host
     network_mode: host
     restart: on-failure
     restart: on-failure

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

@@ -40,6 +40,12 @@ services:
       AZURE_TENANT: "" # "<only for azure, you may optionally specify the tenant for the OAuth>"
       AZURE_TENANT: "" # "<only for azure, you may optionally specify the tenant for the OAuth>"
       OIDC_ISSUER: "" # https://oidc.yourprovider.com - URL of oidc provider
       OIDC_ISSUER: "" # https://oidc.yourprovider.com - URL of oidc provider
       DEFAULT_PROXY_MODE: "off" # if ON, all new clients will enable proxy by default if OFF, all new clients will disable proxy by default, if AUTO, stick with the existing logic for NAT detection
       DEFAULT_PROXY_MODE: "off" # if ON, all new clients will enable proxy by default if OFF, all new clients will disable proxy by default, if AUTO, stick with the existing logic for NAT detection
+      TURN_SERVER_HOST: "turn.NETMAKER_BASE_DOMAIN" # domain for your turn server
+      TURN_SERVER_API_HOST: "https://turnapi.NETMAKER_BASE_DOMAIN" # domain of the turn api server
+      TURN_PORT: "3479" #  port to access turn server
+      TURN_USERNAME: "REPLACE_TURN_USERNAME"  # the username to set for turn api access
+      TURN_PASSWORD: "REPLACE_TURN_PASSWORD" #  the password to set for turn api access
+      USE_TURN: "true" #config for using turn, accepts either true/false
     ports:
     ports:
       - "3478:3478/udp" # the stun port
       - "3478:3478/udp" # the stun port
   netmaker-ui:  # The Netmaker UI Component
   netmaker-ui:  # The Netmaker UI Component
@@ -89,6 +95,22 @@ services:
     ports:
     ports:
       - "1883:1883"
       - "1883:1883"
       - "8883:8883"
       - "8883:8883"
+  turn:
+    container_name: turn
+    image: gravitl/turnserver:v1.0.0
+    network_mode: "host"
+    volumes:
+      - turn_server:/etc/config
+    environment:
+      DEBUG_MODE: "off"
+      VERBOSITY: "1"
+      TURN_PORT: "3479"
+      TURN_API_PORT: "8089"
+      CORS_ALLOWED_ORIGIN: "*"
+      TURN_SERVER_HOST: "turn.NETMAKER_BASE_DOMAIN"
+      USERNAME: "REPLACE_TURN_USERNAME"
+      PASSWORD: "REPLACE_TURN_PASSWORD"
+      USE_TURN: "true"
 volumes:
 volumes:
   caddy_data: {} # runtime data for caddy
   caddy_data: {} # runtime data for caddy
   caddy_conf: {} # configuration file for Caddy
   caddy_conf: {} # configuration file for Caddy
@@ -96,3 +118,4 @@ volumes:
   sqldata: {} # storage for embedded sqlite
   sqldata: {} # storage for embedded sqlite
   dnsconfig: {} # storage for coredns
   dnsconfig: {} # storage for coredns
   mosquitto_logs: {} # storage for mqtt logs
   mosquitto_logs: {} # storage for mqtt logs
+  turn_server: {}

+ 24 - 0
compose/docker-compose.yml

@@ -30,6 +30,12 @@ services:
       MQ_USERNAME: "REPLACE_MQ_USERNAME"
       MQ_USERNAME: "REPLACE_MQ_USERNAME"
       STUN_PORT: "3478"
       STUN_PORT: "3478"
       DEFAULT_PROXY_MODE: "off"
       DEFAULT_PROXY_MODE: "off"
+      TURN_SERVER_HOST: "turn.NETMAKER_BASE_DOMAIN"
+      TURN_SERVER_API_HOST: "https://turnapi.NETMAKER_BASE_DOMAIN"
+      TURN_PORT: "3479"
+      TURN_USERNAME: "REPLACE_TURN_USERNAME"
+      TURN_PASSWORD: "REPLACE_TURN_PASSWORD"
+      USE_TURN: "true"
     ports:
     ports:
       - "3478:3478/udp"
       - "3478:3478/udp"
   netmaker-ui:
   netmaker-ui:
@@ -46,6 +52,8 @@ services:
     image: caddy:2.6.2
     image: caddy:2.6.2
     container_name: caddy
     container_name: caddy
     restart: unless-stopped
     restart: unless-stopped
+    extra_hosts:
+      - "host.docker.internal:host-gateway"
     volumes:
     volumes:
       - /root/Caddyfile:/etc/caddy/Caddyfile
       - /root/Caddyfile:/etc/caddy/Caddyfile
       - /root/fullchain.pem:/root/fullchain.pem
       - /root/fullchain.pem:/root/fullchain.pem
@@ -78,9 +86,25 @@ services:
       - /root/mosquitto.conf:/mosquitto/config/mosquitto.conf
       - /root/mosquitto.conf:/mosquitto/config/mosquitto.conf
       - /root/wait.sh:/mosquitto/config/wait.sh
       - /root/wait.sh:/mosquitto/config/wait.sh
       - mosquitto_logs:/mosquitto/log
       - mosquitto_logs:/mosquitto/log
+  turn:
+    container_name: turn
+    image: gravitl/turnserver:v1.0.0
+    network_mode: "host"
+    volumes:
+      - turn_server:/etc/config
+    environment:
+      DEBUG_MODE: "off"
+      VERBOSITY: "1"
+      TURN_PORT: "3479"
+      TURN_API_PORT: "8089"
+      CORS_ALLOWED_ORIGIN: "*"
+      TURN_SERVER_HOST: "turn.NETMAKER_BASE_DOMAIN"
+      USERNAME: "REPLACE_TURN_USERNAME"
+      PASSWORD: "REPLACE_TURN_PASSWORD"
 volumes:
 volumes:
   caddy_data: {}
   caddy_data: {}
   caddy_conf: {}
   caddy_conf: {}
   sqldata: {}
   sqldata: {}
   dnsconfig: {}
   dnsconfig: {}
   mosquitto_logs: {}
   mosquitto_logs: {}
+  turn_server: {}

+ 6 - 0
config/config.go

@@ -76,6 +76,12 @@ type ServerConfig struct {
 	StunList             string    `yaml:"stun_list"`
 	StunList             string    `yaml:"stun_list"`
 	Proxy                string    `yaml:"proxy"`
 	Proxy                string    `yaml:"proxy"`
 	DefaultProxyMode     ProxyMode `yaml:"defaultproxymode"`
 	DefaultProxyMode     ProxyMode `yaml:"defaultproxymode"`
+	TurnServer           string    `yaml:"turn_server"`
+	TurnApiServer        string    `yaml:"turn_api_server"`
+	TurnPort             int       `yaml:"turn_port"`
+	TurnUserName         string    `yaml:"turn_username"`
+	TurnPassword         string    `yaml:"turn_password"`
+	UseTurn              bool      `yaml:"use_turn"`
 }
 }
 
 
 // ProxyMode - default proxy mode for server
 // ProxyMode - default proxy mode for server

+ 1 - 1
controllers/docs.go

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

+ 7 - 0
controllers/enrollmentkeys.go

@@ -153,6 +153,13 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 	hostExists := false
 	hostExists := false
+	// re-register host with turn just in case.
+	if servercfg.IsUsingTurn() {
+		err = logic.RegisterHostWithTurn(newHost.ID.String(), newHost.HostPass)
+		if err != nil {
+			logger.Log(0, "failed to register host with turn server: ", err.Error())
+		}
+	}
 	// check if host already exists
 	// check if host already exists
 	if hostExists = logic.HostExists(&newHost); hostExists && len(enrollmentKey.Networks) == 0 {
 	if hostExists = logic.HostExists(&newHost); hostExists && len(enrollmentKey.Networks) == 0 {
 		logger.Log(0, "host", newHost.ID.String(), newHost.Name, "attempted to re-register with no networks")
 		logger.Log(0, "host", newHost.ID.String(), newHost.Name, "attempted to re-register with no networks")

+ 74 - 0
controllers/hosts.go

@@ -30,6 +30,7 @@ func hostHandlers(r *mux.Router) {
 	r.HandleFunc("/api/hosts/{hostid}/relay", logic.SecurityCheck(false, http.HandlerFunc(deleteHostRelay))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/hosts/{hostid}/relay", logic.SecurityCheck(false, http.HandlerFunc(deleteHostRelay))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/hosts/adm/authenticate", authenticateHost).Methods(http.MethodPost)
 	r.HandleFunc("/api/hosts/adm/authenticate", authenticateHost).Methods(http.MethodPost)
 	r.HandleFunc("/api/v1/host", authorize(true, false, "host", http.HandlerFunc(pull))).Methods(http.MethodGet)
 	r.HandleFunc("/api/v1/host", authorize(true, false, "host", http.HandlerFunc(pull))).Methods(http.MethodGet)
+	r.HandleFunc("/api/v1/host/{hostid}/signalpeer", authorize(true, false, "host", http.HandlerFunc(signalPeer))).Methods(http.MethodPost)
 	r.HandleFunc("/api/v1/auth-register/host", socketHandler)
 	r.HandleFunc("/api/v1/auth-register/host", socketHandler)
 }
 }
 
 
@@ -94,6 +95,13 @@ func pull(w http.ResponseWriter, r *http.Request) {
 	if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
 	if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
 		serverConf.MQUserName = hostID
 		serverConf.MQUserName = hostID
 	}
 	}
+	key, keyErr := logic.RetrievePublicTrafficKey()
+	if keyErr != nil {
+		logger.Log(0, "error retrieving key:", keyErr.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	serverConf.TrafficKey = key
 	response := models.HostPull{
 	response := models.HostPull{
 		Host:         *host,
 		Host:         *host,
 		ServerConfig: serverConf,
 		ServerConfig: serverConf,
@@ -450,6 +458,72 @@ func authenticateHost(response http.ResponseWriter, request *http.Request) {
 	response.Write(successJSONResponse)
 	response.Write(successJSONResponse)
 }
 }
 
 
+// swagger:route POST /api/hosts/{hostid}/signalpeer signalPeer
+//
+// send signal to peer.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: signal
+func signalPeer(w http.ResponseWriter, r *http.Request) {
+	var params = mux.Vars(r)
+	hostid := params["hostid"]
+	// confirm host exists
+	_, err := logic.GetHost(hostid)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to get host:", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	var signal models.Signal
+	w.Header().Set("Content-Type", "application/json")
+	err = json.NewDecoder(r.Body).Decode(&signal)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	if signal.ToHostPubKey == "" || signal.TurnRelayEndpoint == "" {
+		msg := "insufficient data to signal peer"
+		logger.Log(0, r.Header.Get("user"), msg)
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New(msg), "badrequest"))
+		return
+	}
+	hosts, err := logic.GetAllHosts()
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	// push the signal to host through mq
+	found := false
+	for _, hostI := range hosts {
+		if hostI.PublicKey.String() == signal.ToHostPubKey {
+			// found host publish message and break
+			found = true
+			err = mq.HostUpdate(&models.HostUpdate{
+				Action: models.SignalHost,
+				Host:   hostI,
+				Signal: signal,
+			})
+			if err != nil {
+				logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to publish signal to peer: "+err.Error()), "badrequest"))
+				return
+			}
+			break
+		}
+	}
+	if !found {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to signal, peer not found"), "badrequest"))
+		return
+	}
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(signal)
+}
+
 // swagger:route POST /api/hosts/keys host updateAllKeys
 // swagger:route POST /api/hosts/keys host updateAllKeys
 //
 //
 // Update keys for a network.
 // Update keys for a network.

+ 12 - 0
docker/Caddyfile

@@ -32,6 +32,18 @@ https://stun.NETMAKER_BASE_DOMAIN {
 	reverse_proxy netmaker:3478
 	reverse_proxy netmaker:3478
 }
 }
 
 
+# TURN
+https://turn.NETMAKER_BASE_DOMAIN {
+	tls /root/fullchain.pem /root/privkey.pem
+	reverse_proxy host.docker.internal:3479
+}
+
+#TURN API
+https://turnapi.NETMAKER_BASE_DOMAIN {
+	tls /root/fullchain.pem /root/privkey.pem
+    reverse_proxy http://host.docker.internal:8089
+}
+
 # MQ
 # MQ
 wss://broker.NETMAKER_BASE_DOMAIN {
 wss://broker.NETMAKER_BASE_DOMAIN {
 	tls /root/fullchain.pem /root/privkey.pem
 	tls /root/fullchain.pem /root/privkey.pem

+ 10 - 0
docker/Caddyfile-EE

@@ -50,6 +50,16 @@ https://stun.NETMAKER_BASE_DOMAIN {
 	reverse_proxy netmaker:3478
 	reverse_proxy netmaker:3478
 }
 }
 
 
+# TURN
+https://turn.NETMAKER_BASE_DOMAIN {
+	reverse_proxy host.docker.internal:3479
+}
+
+#TURN API
+https://turnapi.NETMAKER_BASE_DOMAIN {
+        reverse_proxy http://host.docker.internal:8089
+}
+
 # MQ
 # MQ
 wss://broker.NETMAKER_BASE_DOMAIN {
 wss://broker.NETMAKER_BASE_DOMAIN {
         reverse_proxy ws://mq:8883
         reverse_proxy ws://mq:8883

+ 1 - 1
docker/Dockerfile-go-builder

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

+ 4 - 3
go.mod

@@ -4,12 +4,12 @@ go 1.19
 
 
 require (
 require (
 	github.com/eclipse/paho.mqtt.golang v1.4.2
 	github.com/eclipse/paho.mqtt.golang v1.4.2
-	github.com/go-playground/validator/v10 v10.12.0
+	github.com/go-playground/validator/v10 v10.13.0
 	github.com/golang-jwt/jwt/v4 v4.5.0
 	github.com/golang-jwt/jwt/v4 v4.5.0
 	github.com/google/uuid v1.3.0
 	github.com/google/uuid v1.3.0
 	github.com/gorilla/handlers v1.5.1
 	github.com/gorilla/handlers v1.5.1
 	github.com/gorilla/mux v1.8.0
 	github.com/gorilla/mux v1.8.0
-	github.com/lib/pq v1.10.8
+	github.com/lib/pq v1.10.9
 	github.com/mattn/go-sqlite3 v1.14.16
 	github.com/mattn/go-sqlite3 v1.14.16
 	github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f
 	github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
@@ -41,6 +41,7 @@ require (
 )
 )
 
 
 require (
 require (
+	github.com/devilcove/httpclient v0.6.0
 	github.com/guumaster/tablewriter v0.0.10
 	github.com/guumaster/tablewriter v0.0.10
 	github.com/matryer/is v1.4.1
 	github.com/matryer/is v1.4.1
 	github.com/olekukonko/tablewriter v0.0.5
 	github.com/olekukonko/tablewriter v0.0.5
@@ -66,7 +67,7 @@ require (
 	github.com/google/go-cmp v0.5.9 // indirect
 	github.com/google/go-cmp v0.5.9 // indirect
 	github.com/hashicorp/go-version v1.6.0
 	github.com/hashicorp/go-version v1.6.0
 	github.com/josharian/native v1.0.0 // indirect
 	github.com/josharian/native v1.0.0 // indirect
-	github.com/leodido/go-urn v1.2.2 // indirect
+	github.com/leodido/go-urn v1.2.3 // indirect
 	github.com/mattn/go-runewidth v0.0.13 // indirect
 	github.com/mattn/go-runewidth v0.0.13 // indirect
 	github.com/mdlayher/genetlink v1.2.0 // indirect
 	github.com/mdlayher/genetlink v1.2.0 // indirect
 	github.com/mdlayher/netlink v1.6.0 // indirect
 	github.com/mdlayher/netlink v1.6.0 // indirect

+ 8 - 7
go.sum

@@ -21,6 +21,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/devilcove/httpclient v0.6.0 h1:M5YAfHeNbu+0QxCiOCo/fKN+Hf0BtF/6aovu3NNgcKk=
+github.com/devilcove/httpclient v0.6.0/go.mod h1:ctrAO2gRgTT+GxtRdWBp2SMQ+vacuxXlbhmlM4oWhs8=
 github.com/eclipse/paho.mqtt.golang v1.4.2 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6rNA5PM12m4=
 github.com/eclipse/paho.mqtt.golang v1.4.2 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6rNA5PM12m4=
 github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
 github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
 github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
@@ -34,8 +36,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI=
-github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA=
+github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ=
+github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4=
 github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
 github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
 github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -75,10 +77,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4=
-github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ=
-github.com/lib/pq v1.10.8 h1:3fdt97i/cwSU83+E0hZTC/Xpc9mTZxc6UWSCRcSbxiE=
-github.com/lib/pq v1.10.8/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA=
+github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
+github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
+github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
 github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
 github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
 github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
@@ -118,7 +120,6 @@ github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f/go.mod h1:UW/gxgQw
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=

+ 1 - 1
k8s/client/netclient-daemonset.yaml

@@ -16,7 +16,7 @@ spec:
       hostNetwork: true
       hostNetwork: true
       containers:
       containers:
       - name: netclient
       - name: netclient
-        image: gravitl/netclient:v0.18.7
+        image: gravitl/netclient:v0.19.0
         env:
         env:
         - name: TOKEN
         - name: TOKEN
           value: "TOKEN_VALUE"
           value: "TOKEN_VALUE"

+ 1 - 1
k8s/client/netclient.yaml

@@ -28,7 +28,7 @@ spec:
       #           - "<node label value>"
       #           - "<node label value>"
       containers:
       containers:
       - name: netclient
       - name: netclient
-        image: gravitl/netclient:v0.18.7
+        image: gravitl/netclient:v0.19.0
         env:
         env:
         - name: TOKEN
         - name: TOKEN
           value: "TOKEN_VALUE"
           value: "TOKEN_VALUE"

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

@@ -79,7 +79,7 @@ spec:
           value: "Kubernetes"
           value: "Kubernetes"
         - name: VERBOSITY
         - name: VERBOSITY
           value: "3"
           value: "3"
-        image: gravitl/netmaker:v0.18.7
+        image: gravitl/netmaker:v0.19.0
         imagePullPolicy: Always
         imagePullPolicy: Always
         name: netmaker
         name: netmaker
         ports:
         ports:

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

@@ -15,7 +15,7 @@ spec:
     spec:
     spec:
       containers:
       containers:
       - name: netmaker-ui
       - name: netmaker-ui
-        image: gravitl/netmaker-ui:v0.18.7
+        image: gravitl/netmaker-ui:v0.19.0
         ports:
         ports:
         - containerPort: 443
         - containerPort: 443
         env:
         env:

+ 2 - 3
logic/enrollmentkey.go

@@ -9,7 +9,6 @@ import (
 
 
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/netclient/ncutils"
 )
 )
 
 
 // EnrollmentErrors - struct for holding EnrollmentKey error messages
 // EnrollmentErrors - struct for holding EnrollmentKey error messages
@@ -190,9 +189,9 @@ func getUniqueEnrollmentID() (string, error) {
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
-	newID := ncutils.MakeRandomString(models.EnrollmentKeyLength)
+	newID := RandomString(models.EnrollmentKeyLength)
 	for _, ok := currentKeys[newID]; ok; {
 	for _, ok := currentKeys[newID]; ok; {
-		newID = ncutils.MakeRandomString(models.EnrollmentKeyLength)
+		newID = RandomString(models.EnrollmentKeyLength)
 	}
 	}
 	return newID, nil
 	return newID, nil
 }
 }

+ 3 - 0
logic/gateway.go

@@ -25,6 +25,9 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
 	if host.OS != "linux" { // support for other OS to be added
 	if host.OS != "linux" { // support for other OS to be added
 		return models.Node{}, errors.New(host.OS + " is unsupported for egress gateways")
 		return models.Node{}, errors.New(host.OS + " is unsupported for egress gateways")
 	}
 	}
+	if host.FirewallInUse == models.FIREWALL_NONE {
+		return models.Node{}, errors.New("firewall is not supported for egress gateways")
+	}
 	for i := len(gateway.Ranges) - 1; i >= 0; i-- {
 	for i := len(gateway.Ranges) - 1; i >= 0; i-- {
 		if gateway.Ranges[i] == "::/0" {
 		if gateway.Ranges[i] == "::/0" {
 			logger.Log(0, "currently IPv6 internet gateways are not supported", gateway.Ranges[i])
 			logger.Log(0, "currently IPv6 internet gateways are not supported", gateway.Ranges[i])

+ 70 - 0
logic/hosts.go

@@ -1,12 +1,17 @@
 package logic
 package logic
 
 
 import (
 import (
+	"crypto/md5"
+	"encoding/base64"
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"log"
 	"log"
+	"net/http"
 	"sort"
 	"sort"
+	"strconv"
 
 
+	"github.com/devilcove/httpclient"
 	"github.com/google/uuid"
 	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
@@ -92,6 +97,13 @@ func CreateHost(h *models.Host) error {
 	if (err != nil && !database.IsEmptyRecord(err)) || (err == nil) {
 	if (err != nil && !database.IsEmptyRecord(err)) || (err == nil) {
 		return ErrHostExists
 		return ErrHostExists
 	}
 	}
+	if servercfg.IsUsingTurn() {
+		err = RegisterHostWithTurn(h.ID.String(), h.HostPass)
+		if err != nil {
+			logger.Log(0, "failed to register host with turn server: ", err.Error())
+		}
+	}
+
 	// encrypt that password so we never see it
 	// encrypt that password so we never see it
 	hash, err := bcrypt.GenerateFromPassword([]byte(h.HostPass), 5)
 	hash, err := bcrypt.GenerateFromPassword([]byte(h.HostPass), 5)
 	if err != nil {
 	if err != nil {
@@ -207,11 +219,18 @@ func RemoveHost(h *models.Host) error {
 	if len(h.Nodes) > 0 {
 	if len(h.Nodes) > 0 {
 		return fmt.Errorf("host still has associated nodes")
 		return fmt.Errorf("host still has associated nodes")
 	}
 	}
+	if servercfg.IsUsingTurn() {
+		DeRegisterHostWithTurn(h.ID.String())
+	}
+
 	return database.DeleteRecord(database.HOSTS_TABLE_NAME, h.ID.String())
 	return database.DeleteRecord(database.HOSTS_TABLE_NAME, h.ID.String())
 }
 }
 
 
 // RemoveHostByID - removes a given host by id from server
 // RemoveHostByID - removes a given host by id from server
 func RemoveHostByID(hostID string) error {
 func RemoveHostByID(hostID string) error {
+	if servercfg.IsUsingTurn() {
+		DeRegisterHostWithTurn(hostID)
+	}
 	return database.DeleteRecord(database.HOSTS_TABLE_NAME, hostID)
 	return database.DeleteRecord(database.HOSTS_TABLE_NAME, hostID)
 }
 }
 
 
@@ -435,6 +454,57 @@ func GetHostByNodeID(id string) *models.Host {
 	return nil
 	return nil
 }
 }
 
 
+// ConvHostPassToHash - converts password to md5 hash
+func ConvHostPassToHash(hostPass string) string {
+	return fmt.Sprintf("%x", md5.Sum([]byte(hostPass)))
+}
+
+// RegisterHostWithTurn - registers the host with the given turn server
+func RegisterHostWithTurn(hostID, hostPass string) error {
+	auth := servercfg.GetTurnUserName() + ":" + servercfg.GetTurnPassword()
+	api := httpclient.JSONEndpoint[models.SuccessResponse, models.ErrorResponse]{
+		URL:           servercfg.GetTurnApiHost(),
+		Route:         "/api/v1/host/register",
+		Method:        http.MethodPost,
+		Authorization: fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(auth))),
+		Data: models.HostTurnRegister{
+			HostID:       hostID,
+			HostPassHash: ConvHostPassToHash(hostPass),
+		},
+		Response:      models.SuccessResponse{},
+		ErrorResponse: models.ErrorResponse{},
+	}
+	_, errData, err := api.GetJSON(models.SuccessResponse{}, models.ErrorResponse{})
+	if err != nil {
+		if errors.Is(err, httpclient.ErrStatus) {
+			logger.Log(1, "error server status", strconv.Itoa(errData.Code), errData.Message)
+		}
+		return err
+	}
+	return nil
+}
+
+// DeRegisterHostWithTurn - to be called when host need to be deregistered from a turn server
+func DeRegisterHostWithTurn(hostID string) error {
+	auth := servercfg.GetTurnUserName() + ":" + servercfg.GetTurnPassword()
+	api := httpclient.JSONEndpoint[models.SuccessResponse, models.ErrorResponse]{
+		URL:           servercfg.GetTurnApiHost(),
+		Route:         fmt.Sprintf("/api/v1/host/deregister?host_id=%s", hostID),
+		Method:        http.MethodPost,
+		Authorization: fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(auth))),
+		Response:      models.SuccessResponse{},
+		ErrorResponse: models.ErrorResponse{},
+	}
+	_, errData, err := api.GetJSON(models.SuccessResponse{}, models.ErrorResponse{})
+	if err != nil {
+		if errors.Is(err, httpclient.ErrStatus) {
+			logger.Log(1, "error server status", strconv.Itoa(errData.Code), errData.Message)
+		}
+		return err
+	}
+	return nil
+}
+
 // SortApiHosts - Sorts slice of ApiHosts by their ID alphabetically with numbers first
 // SortApiHosts - Sorts slice of ApiHosts by their ID alphabetically with numbers first
 func SortApiHosts(unsortedHosts []models.ApiHost) {
 func SortApiHosts(unsortedHosts []models.ApiHost) {
 	sort.Slice(unsortedHosts, func(i, j int) bool {
 	sort.Slice(unsortedHosts, func(i, j int) bool {

+ 1 - 4
logic/jwts.go

@@ -19,10 +19,7 @@ var jwtSecretKey []byte
 func SetJWTSecret() {
 func SetJWTSecret() {
 	currentSecret, jwtErr := FetchJWTSecret()
 	currentSecret, jwtErr := FetchJWTSecret()
 	if jwtErr != nil {
 	if jwtErr != nil {
-		newValue, err := GenerateCryptoString(64)
-		if err != nil {
-			logger.FatalLog("something went wrong when generating JWT signature")
-		}
+		newValue := RandomString(64)
 		jwtSecretKey = []byte(newValue) // 512 bit random password
 		jwtSecretKey = []byte(newValue) // 512 bit random password
 		if err := StoreJWTSecret(string(jwtSecretKey)); err != nil {
 		if err := StoreJWTSecret(string(jwtSecretKey)); err != nil {
 			logger.FatalLog("something went wrong when configuring JWT authentication")
 			logger.FatalLog("something went wrong when configuring JWT authentication")

+ 1 - 0
logic/peers.go

@@ -97,6 +97,7 @@ func GetProxyUpdateForHost(ctx context.Context, host *models.Host) (models.Proxy
 					Proxy:            peerHost.ProxyEnabled,
 					Proxy:            peerHost.ProxyEnabled,
 					PublicListenPort: int32(GetPeerListenPort(peerHost)),
 					PublicListenPort: int32(GetPeerListenPort(peerHost)),
 					ProxyListenPort:  GetProxyListenPort(peerHost),
 					ProxyListenPort:  GetProxyListenPort(peerHost),
+					NatType:          peerHost.NatType,
 				}
 				}
 			}
 			}
 
 

+ 9 - 26
logic/util.go

@@ -2,11 +2,10 @@
 package logic
 package logic
 
 
 import (
 import (
-	crand "crypto/rand"
+	"crypto/rand"
+	"encoding/base32"
 	"encoding/base64"
 	"encoding/base64"
 	"encoding/json"
 	"encoding/json"
-	"math/big"
-	"math/rand"
 	"net"
 	"net"
 	"os"
 	"os"
 	"strings"
 	"strings"
@@ -14,6 +13,7 @@ import (
 
 
 	"github.com/c-robinson/iplib"
 	"github.com/c-robinson/iplib"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/logger"
 )
 )
 
 
 // IsBase64 - checks if a string is in base64 format
 // IsBase64 - checks if a string is in base64 format
@@ -68,32 +68,15 @@ func SetNetworkNodesLastModified(networkName string) error {
 	return nil
 	return nil
 }
 }
 
 
-// GenerateCryptoString - generates random string of n length
-func GenerateCryptoString(n int) (string, error) {
-	const chars = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
-	ret := make([]byte, n)
-	for i := range ret {
-		num, err := crand.Int(crand.Reader, big.NewInt(int64(len(chars))))
-		if err != nil {
-			return "", err
-		}
-		ret[i] = chars[num.Int64()]
-	}
-
-	return string(ret), nil
-}
-
 // RandomString - returns a random string in a charset
 // RandomString - returns a random string in a charset
 func RandomString(length int) string {
 func RandomString(length int) string {
-	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
-
-	var seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano()))
-
-	b := make([]byte, length)
-	for i := range b {
-		b[i] = charset[seededRand.Intn(len(charset))]
+	randombytes := make([]byte, length)
+	_, err := rand.Read(randombytes)
+	if err != nil {
+		logger.Log(0, "random string", err.Error())
+		return ""
 	}
 	}
-	return string(b)
+	return base32.StdEncoding.EncodeToString(randombytes)[:length]
 }
 }
 
 
 // StringSliceContains - sees if a string slice contains a string element
 // StringSliceContains - sees if a string slice contains a string element

+ 5 - 5
netclient/ncutils/netclientutils_test.go → logic/util_test.go

@@ -1,4 +1,4 @@
-package ncutils
+package logic
 
 
 import (
 import (
 	"strings"
 	"strings"
@@ -7,10 +7,10 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 )
 )
 
 
-func TestMakeRandomString(t *testing.T) {
+func TestRandomString(t *testing.T) {
 	for testCase := 0; testCase < 100; testCase++ {
 	for testCase := 0; testCase < 100; testCase++ {
 		for size := 2; size < 2058; size++ {
 		for size := 2; size < 2058; size++ {
-			if length := len(MakeRandomString(size)); length != size {
+			if length := len(RandomString(size)); length != size {
 				t.Fatalf("expected random string of size %d, got %d instead", size, length)
 				t.Fatalf("expected random string of size %d, got %d instead", size, length)
 			}
 			}
 		}
 		}
@@ -18,9 +18,9 @@ func TestMakeRandomString(t *testing.T) {
 }
 }
 
 
 func TestMakeRandomStringValid(t *testing.T) {
 func TestMakeRandomStringValid(t *testing.T) {
-	lengthStr := MakeRandomString(10)
+	lengthStr := RandomString(10)
 	assert.Equal(t, len(lengthStr), 10)
 	assert.Equal(t, len(lengthStr), 10)
-	validMqID := MakeRandomString(23)
+	validMqID := RandomString(23)
 	assert.False(t, strings.Contains(validMqID, "#"))
 	assert.False(t, strings.Contains(validMqID, "#"))
 	assert.False(t, strings.Contains(validMqID, "!"))
 	assert.False(t, strings.Contains(validMqID, "!"))
 	assert.False(t, strings.Contains(validMqID, "\""))
 	assert.False(t, strings.Contains(validMqID, "\""))

+ 1 - 1
main.go

@@ -27,7 +27,7 @@ import (
 	stunserver "github.com/gravitl/netmaker/stun-server"
 	stunserver "github.com/gravitl/netmaker/stun-server"
 )
 )
 
 
-var version = "v0.18.7"
+var version = "v0.19.0"
 
 
 // Start DB Connection and start API Request Handler
 // Start DB Connection and start API Request Handler
 func main() {
 func main() {

+ 20 - 0
models/host.go

@@ -101,6 +101,8 @@ func ParseBool(s string) bool {
 type HostMqAction string
 type HostMqAction string
 
 
 const (
 const (
+	// SignalHost - const for host signal action
+	SignalHost = "SIGNAL_HOST"
 	// UpdateHost - constant for host update action
 	// UpdateHost - constant for host update action
 	UpdateHost = "UPDATE_HOST"
 	UpdateHost = "UPDATE_HOST"
 	// DeleteHost - constant for host delete action
 	// DeleteHost - constant for host delete action
@@ -113,6 +115,8 @@ const (
 	RequestAck = "REQ_ACK"
 	RequestAck = "REQ_ACK"
 	// CheckIn - update last check in times and public address and interfaces
 	// CheckIn - update last check in times and public address and interfaces
 	CheckIn = "CHECK_IN"
 	CheckIn = "CHECK_IN"
+	// REGISTER_WITH_TURN - registers host with turn server if configured
+	RegisterWithTurn = "REGISTER_WITH_TURN"
 	// UpdateKeys - update wireguard private/public keys
 	// UpdateKeys - update wireguard private/public keys
 	UpdateKeys = "UPDATE_KEYS"
 	UpdateKeys = "UPDATE_KEYS"
 )
 )
@@ -122,6 +126,22 @@ type HostUpdate struct {
 	Action HostMqAction
 	Action HostMqAction
 	Host   Host
 	Host   Host
 	Node   Node
 	Node   Node
+	Signal Signal
+}
+
+// HostTurnRegister - struct for host turn registration
+type HostTurnRegister struct {
+	HostID       string `json:"host_id"`
+	HostPassHash string `json:"host_pass_hash"`
+}
+
+// Signal - struct for signalling peer
+type Signal struct {
+	Server            string `json:"server"`
+	FromHostPubKey    string `json:"from_host_pubkey"`
+	TurnRelayEndpoint string `json:"turn_relay_addr"`
+	ToHostPubKey      string `json:"to_host_pubkey"`
+	Reply             bool   `json:"reply"`
 }
 }
 
 
 // RegisterMsg - login message struct for hosts to join via SSO login
 // RegisterMsg - login message struct for hosts to join via SSO login

+ 4 - 5
models/proxy.go

@@ -40,17 +40,16 @@ type PeerConf struct {
 	ProxyListenPort  int          `json:"proxy_listen_port"`
 	ProxyListenPort  int          `json:"proxy_listen_port"`
 	IsExtClient      bool         `json:"is_ext_client"`
 	IsExtClient      bool         `json:"is_ext_client"`
 	Address          net.IP       `json:"address"`
 	Address          net.IP       `json:"address"`
-	ExtInternalIp    net.IP       `json:"ext_internal_ip"`
 	IsRelayed        bool         `json:"is_relayed"`
 	IsRelayed        bool         `json:"is_relayed"`
 	RelayedTo        *net.UDPAddr `json:"relayed_to"`
 	RelayedTo        *net.UDPAddr `json:"relayed_to"`
+	NatType          string       `json:"nat_type"`
 }
 }
 
 
 // ProxyManagerPayload - struct for proxy manager payload
 // ProxyManagerPayload - struct for proxy manager payload
 type ProxyManagerPayload struct {
 type ProxyManagerPayload struct {
-	Action        ProxyAction `json:"action"`
-	InterfaceName string      `json:"interface_name"`
-	Server        string      `json:"server"`
-	//WgAddr          string                 `json:"wg_addr"`
+	Action          ProxyAction            `json:"action"`
+	InterfaceName   string                 `json:"interface_name"`
+	Server          string                 `json:"server"`
 	Peers           []wgtypes.PeerConfig   `json:"peers"`
 	Peers           []wgtypes.PeerConfig   `json:"peers"`
 	PeerMap         map[string]PeerConf    `json:"peer_map"`
 	PeerMap         map[string]PeerConf    `json:"peer_map"`
 	IsIngress       bool                   `json:"is_ingress"`
 	IsIngress       bool                   `json:"is_ingress"`

+ 3 - 0
models/structs.go

@@ -240,6 +240,9 @@ type ServerConfig struct {
 	StunPort    int          `yaml:"stun_port"`
 	StunPort    int          `yaml:"stun_port"`
 	StunList    []StunServer `yaml:"stun_list"`
 	StunList    []StunServer `yaml:"stun_list"`
 	TrafficKey  []byte       `yaml:"traffickey"`
 	TrafficKey  []byte       `yaml:"traffickey"`
+	TurnDomain  string       `yaml:"turn_domain"`
+	TurnPort    int          `yaml:"turn_port"`
+	UseTurn     bool         `yaml:"use_turn"`
 }
 }
 
 
 // User.NameInCharset - returns if name is in charset below or not
 // User.NameInCharset - returns if name is in charset below or not

+ 9 - 0
mq/handlers.go

@@ -163,6 +163,15 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
 			return
 			return
 		}
 		}
 		sendPeerUpdate = true
 		sendPeerUpdate = true
+	case models.RegisterWithTurn:
+		if servercfg.IsUsingTurn() {
+			err = logic.RegisterHostWithTurn(hostUpdate.Host.ID.String(), hostUpdate.Host.HostPass)
+			if err != nil {
+				logger.Log(0, "failed to register host with turn server: ", err.Error())
+				return
+			}
+		}
+
 	}
 	}
 
 
 	if sendPeerUpdate {
 	if sendPeerUpdate {

+ 2 - 2
mq/mq.go

@@ -8,7 +8,7 @@ import (
 
 
 	mqtt "github.com/eclipse/paho.mqtt.golang"
 	mqtt "github.com/eclipse/paho.mqtt.golang"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/netclient/ncutils"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
 )
 )
 
 
@@ -27,7 +27,7 @@ var mqclient mqtt.Client
 func setMqOptions(user, password string, opts *mqtt.ClientOptions) {
 func setMqOptions(user, password string, opts *mqtt.ClientOptions) {
 	broker, _ := servercfg.GetMessageQueueEndpoint()
 	broker, _ := servercfg.GetMessageQueueEndpoint()
 	opts.AddBroker(broker)
 	opts.AddBroker(broker)
-	id := ncutils.MakeRandomString(23)
+	id := logic.RandomString(23)
 	opts.ClientID = id
 	opts.ClientID = id
 	opts.SetUsername(user)
 	opts.SetUsername(user)
 	opts.SetPassword(password)
 	opts.SetPassword(password)

+ 0 - 14
netclient/ncutils/netclientutils.go

@@ -2,7 +2,6 @@ package ncutils
 
 
 import (
 import (
 	"bytes"
 	"bytes"
-	"crypto/rand"
 	"encoding/gob"
 	"encoding/gob"
 )
 )
 
 
@@ -32,16 +31,3 @@ func ConvertBytesToKey(data []byte) (*[32]byte, error) {
 	}
 	}
 	return result, err
 	return result, err
 }
 }
-
-// MakeRandomString - generates a random string of len n
-func MakeRandomString(n int) string {
-	const validChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
-	result := make([]byte, n)
-	if _, err := rand.Reader.Read(result); err != nil {
-		return ""
-	}
-	for i, b := range result {
-		result[i] = validChars[b%byte(len(validChars))]
-	}
-	return string(result)
-}

+ 10 - 4
release.md

@@ -1,14 +1,20 @@
-# Netmaker v0.18.7
+# Netmaker v0.19.0
 
 
 ## whats new
 ## whats new
+- TURN
+- dependency updates
 - internet gateways (0.0.0.0/0) for egress
 - internet gateways (0.0.0.0/0) for egress
 - deprecated editing of network parameters
 - deprecated editing of network parameters
 - allow extra ips for extclient (not enabled in UI)
 - allow extra ips for extclient (not enabled in UI)
     
     
 ## whats fixed
 ## whats fixed
-- nm-quick - determine lastest version from releases
-- wireguard public/private key rotation
-- ee-license checks
+- unbiased random string
+- get traffic keys on pull
+- CI updates
+- install/update script updates
+- firewall checks
+-  
+- 
 
 
 ## known issues
 ## known issues
 - Caddy does not handle netmaker exporter well for EE
 - Caddy does not handle netmaker exporter well for EE

+ 51 - 1
scripts/nm-quick.sh

@@ -453,6 +453,8 @@ set_install_vars() {
 	echo "                api.$NETMAKER_BASE_DOMAIN"
 	echo "                api.$NETMAKER_BASE_DOMAIN"
 	echo "             broker.$NETMAKER_BASE_DOMAIN"
 	echo "             broker.$NETMAKER_BASE_DOMAIN"
 	echo "               stun.$NETMAKER_BASE_DOMAIN"
 	echo "               stun.$NETMAKER_BASE_DOMAIN"
+	echo "               turn.$NETMAKER_BASE_DOMAIN"
+	echo "               turnapi.$NETMAKER_BASE_DOMAIN"
 
 
 	if [ "$INSTALL_TYPE" = "ee" ]; then
 	if [ "$INSTALL_TYPE" = "ee" ]; then
 		echo "         prometheus.$NETMAKER_BASE_DOMAIN"
 		echo "         prometheus.$NETMAKER_BASE_DOMAIN"
@@ -566,6 +568,51 @@ set_install_vars() {
 		done
 		done
 	fi
 	fi
 
 
+	unset GET_TURN_USERNAME
+	unset GET_TURN_PASSWORD
+	unset CONFIRM_TURN_PASSWORD
+	echo "Enter Credentials For TURN..."
+	if [ -z $AUTO_BUILD ]; then
+		read -p "TURN Username (click 'enter' to use 'netmaker'): " GET_TURN_USERNAME
+	fi
+	if [ -z "$GET_TURN_USERNAME" ]; then
+	echo "using default username for mq"
+	TURN_USERNAME="netmaker"
+	else
+	TURN_USERNAME="$GET_TURN_USERNAME"
+	fi
+
+	TURN_PASSWORD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 30 ; echo '')
+
+	if [ -z $AUTO_BUILD ]; then
+		select domain_option in "Auto Generated Password" "Input Your Own Password"; do
+			case $REPLY in
+			1)
+			echo "using random password for turn"
+			break
+			;;
+			2)
+			while true
+			do
+				echo "Enter your Password For TURN: "
+				read -s GET_TURN_PASSWORD
+				echo "Enter your password again to confirm: "
+				read -s CONFIRM_TURN_PASSWORD
+				if [ ${GET_TURN_PASSWORD} != ${CONFIRM_TURN_PASSWORD} ]; then
+					echo "wrong password entered, try again..."
+					continue
+				fi
+				TURN_PASSWORD="$GET_TURN_PASSWORD"
+				echo "TURN Password Saved Successfully!!"
+				break
+			done
+			break
+			;;
+			*) echo "invalid option $REPLY";;
+		esac
+		done
+	fi
+
 	wait_seconds 2
 	wait_seconds 2
 
 
 	echo "-----------------------------------------------------------------"
 	echo "-----------------------------------------------------------------"
@@ -623,8 +670,11 @@ install_netmaker() {
 	sed -i "s/NETMAKER_BASE_DOMAIN/$NETMAKER_BASE_DOMAIN/g" /root/docker-compose.yml
 	sed -i "s/NETMAKER_BASE_DOMAIN/$NETMAKER_BASE_DOMAIN/g" /root/docker-compose.yml
 	sed -i "s/REPLACE_MASTER_KEY/$MASTER_KEY/g" /root/docker-compose.yml
 	sed -i "s/REPLACE_MASTER_KEY/$MASTER_KEY/g" /root/docker-compose.yml
 	sed -i "s/YOUR_EMAIL/$EMAIL/g" /root/Caddyfile
 	sed -i "s/YOUR_EMAIL/$EMAIL/g" /root/Caddyfile
-	sed -i "s/REPLACE_MQ_PASSWORD/$MQ_PASSWORD/g" /root/docker-compose.yml
 	sed -i "s/REPLACE_MQ_USERNAME/$MQ_USERNAME/g" /root/docker-compose.yml
 	sed -i "s/REPLACE_MQ_USERNAME/$MQ_USERNAME/g" /root/docker-compose.yml
+	sed -i "s/REPLACE_MQ_PASSWORD/$MQ_PASSWORD/g" /root/docker-compose.yml
+	sed -i "s/REPLACE_TURN_USERNAME/$TURN_USERNAME/g" /root/docker-compose.yml
+	sed -i "s/REPLACE_TURN_PASSWORD/$TURN_PASSWORD/g" /root/docker-compose.yml
+
 	if [ "$INSTALL_TYPE" = "ee" ]; then
 	if [ "$INSTALL_TYPE" = "ee" ]; then
 		sed -i "s~YOUR_LICENSE_KEY~$LICENSE_KEY~g" /root/docker-compose.yml
 		sed -i "s~YOUR_LICENSE_KEY~$LICENSE_KEY~g" /root/docker-compose.yml
 		sed -i "s/YOUR_ACCOUNT_ID/$ACCOUNT_ID/g" /root/docker-compose.yml
 		sed -i "s/YOUR_ACCOUNT_ID/$ACCOUNT_ID/g" /root/docker-compose.yml

+ 83 - 9
scripts/nm-upgrade.sh → scripts/nm-upgrade-0-17-1-to-0-19-0.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 #!/bin/bash
 
 
-LATEST="v0.18.6"
+LATEST="v0.19.0"
 INSTALL_PATH="/root"
 INSTALL_PATH="/root"
 
 
 trap restore_old_netmaker_instructions
 trap restore_old_netmaker_instructions
@@ -260,10 +260,12 @@ collect_server_settings() {
   done
   done
 
 
   STUN_DOMAIN="stun.$SERVER_NAME"
   STUN_DOMAIN="stun.$SERVER_NAME"
+  TURN_DOMAIN="turn.$SERVER_NAME"
+  TURNAPI_DOMAIN="turnapi.$SERVER_NAME"
   echo "-----------------------------------------------------"
   echo "-----------------------------------------------------"
-  echo "Netmaker v0.18 requires a new DNS entry for $STUN_DOMAIN."
-  echo "Please confirm this is added to your DNS provider before continuing"
-  echo "(note: this is not required if using an nip.io address)"
+  echo "Netmaker v0.19 requires new DNS entries for $STUN_DOMAIN, $TURN_DOMAIN, and $TURNAPI_DOMAIN."
+  echo "Please confirm this is added to your DNS provider before continuing."
+  echo "You can skip this step if using a wildcard DNS entry (e.g. *.$SERVER_NAME) or a nip.io address."
   echo "-----------------------------------------------------"
   echo "-----------------------------------------------------"
   confirm
   confirm
 }
 }
@@ -337,6 +339,16 @@ cat <<EOT >> $INSTALL_PATH/Caddyfile
 https://$STUN_DOMAIN {
 https://$STUN_DOMAIN {
   reverse_proxy netmaker:3478
   reverse_proxy netmaker:3478
 }
 }
+
+# TURN
+https://$TURN_DOMAIN {
+  reverse_proxy netmaker:3479
+}
+
+#TURN API
+https://turnapi.$TURNAPI_DOMAIN {
+        reverse_proxy http://host.docker.internal:8089
+}
 EOT
 EOT
 
 
 }
 }
@@ -385,10 +397,55 @@ set_mq_credentials() {
   done
   done
 }
 }
 
 
+# set_turn_credentials - sets mq credentials
+set_turn_credentials() {
+
+  unset GET_TURN_USERNAME
+  unset GET_TURN_PASSWORD
+  unset CONFIRM_TURN_PASSWORD
+  echo "Enter Credentials For TURN..."
+  read -p "TURN Username (click 'enter' to use 'netmaker'): " GET_TURN_USERNAME
+  if [ -z "$GET_TURN_USERNAME" ]; then
+    echo "using default username for turn"
+    TURN_USERNAME="netmaker"
+  else
+    TURN_USERNAME="$GET_TURN_USERNAME"
+  fi
+
+  select domain_option in "Auto Generated Password" "Input Your Own Password"; do
+    case $REPLY in
+    1)
+    echo "generating random password for TURN"
+    TURN_PASSWORD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 30 ; echo '')
+    break
+    ;;      
+      2)
+    while true
+      do
+          echo "Enter your Password For TURN: " 
+          read -s GET_TURN_PASSWORD
+          echo "Enter your password again to confirm: "
+          read -s CONFIRM_TURN_PASSWORD
+          if [ ${GET_TURN_PASSWORD} != ${CONFIRM_TURN_PASSWORD} ]; then
+              echo "wrong password entered, try again..."
+              continue
+          fi
+      TURN_PASSWORD="$GET_TURN_PASSWORD"
+          echo "TURN Password Saved Successfully!!"
+          break
+      done
+        break
+        ;;
+      *) echo "invalid option $REPLY";;
+    esac
+  done
+}
+
 # set_compose - set compose file with proper values
 # set_compose - set compose file with proper values
 set_compose() {
 set_compose() {
 
 
   set_mq_credentials
   set_mq_credentials
+  set_turn_credentials
 
 
   echo "retrieving updated wait script and mosquitto conf"  
   echo "retrieving updated wait script and mosquitto conf"  
   rm $INSTALL_PATH/wait.sh
   rm $INSTALL_PATH/wait.sh
@@ -407,21 +464,38 @@ set_compose() {
 
 
   STUN_PORT=3478
   STUN_PORT=3478
 
 
-  # RELEASE_REPLACE - Use this once release is ready
-
-  #sed -i "s/v0.17.1/v0.18.6/g" /root/docker-compose.yml
   yq ".services.netmaker.environment.SERVER_NAME = \"$SERVER_NAME\"" -i $INSTALL_PATH/docker-compose.yml
   yq ".services.netmaker.environment.SERVER_NAME = \"$SERVER_NAME\"" -i $INSTALL_PATH/docker-compose.yml
   yq ".services.netmaker.environment += {\"BROKER_ENDPOINT\": \"wss://$BROKER_NAME\"}" -i $INSTALL_PATH/docker-compose.yml  
   yq ".services.netmaker.environment += {\"BROKER_ENDPOINT\": \"wss://$BROKER_NAME\"}" -i $INSTALL_PATH/docker-compose.yml  
   yq ".services.netmaker.environment += {\"SERVER_BROKER_ENDPOINT\": \"ws://mq:1883\"}" -i $INSTALL_PATH/docker-compose.yml  
   yq ".services.netmaker.environment += {\"SERVER_BROKER_ENDPOINT\": \"ws://mq:1883\"}" -i $INSTALL_PATH/docker-compose.yml  
   yq ".services.netmaker.environment += {\"STUN_LIST\": \"$STUN_DOMAIN:$STUN_PORT,stun1.netmaker.io:3478,stun2.netmaker.io:3478,stun1.l.google.com:19302,stun2.l.google.com:19302\"}" -i $INSTALL_PATH/docker-compose.yml  
   yq ".services.netmaker.environment += {\"STUN_LIST\": \"$STUN_DOMAIN:$STUN_PORT,stun1.netmaker.io:3478,stun2.netmaker.io:3478,stun1.l.google.com:19302,stun2.l.google.com:19302\"}" -i $INSTALL_PATH/docker-compose.yml  
-  yq ".services.netmaker.environment += {\"MQ_PASSWORD\": \"$MQ_PASSWORD\"}" -i $INSTALL_PATH/docker-compose.yml  
   yq ".services.netmaker.environment += {\"MQ_USERNAME\": \"$MQ_USERNAME\"}" -i $INSTALL_PATH/docker-compose.yml  
   yq ".services.netmaker.environment += {\"MQ_USERNAME\": \"$MQ_USERNAME\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.netmaker.environment += {\"MQ_PASSWORD\": \"$MQ_PASSWORD\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.netmaker.environment += {\"TURN_SERVER_HOST\": \"turn.$SERVER_NAME\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.netmaker.environment += {\"TURN_SERVER_API_HOST\": \"turnapi.$SERVER_NAME\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.netmaker.environment += {\"TURN_USERNAME\": \"$TURN_USERNAME\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.netmaker.environment += {\"TURN_PASSWORD\": \"$TURN_PASSWORD\"}" -i $INSTALL_PATH/docker-compose.yml  
   yq ".services.netmaker.environment += {\"STUN_PORT\": \"$STUN_PORT\"}" -i $INSTALL_PATH/docker-compose.yml  
   yq ".services.netmaker.environment += {\"STUN_PORT\": \"$STUN_PORT\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.netmaker.environment += {\"TURN_PORT\": \"3479\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.netmaker.environment += {\"USE_TURN\": \"true\"}" -i $INSTALL_PATH/docker-compose.yml  
   yq ".services.netmaker.ports += \"3478:3478/udp\"" -i $INSTALL_PATH/docker-compose.yml
   yq ".services.netmaker.ports += \"3478:3478/udp\"" -i $INSTALL_PATH/docker-compose.yml
 
 
-  yq ".services.mq.environment += {\"MQ_PASSWORD\": \"$MQ_PASSWORD\"}" -i $INSTALL_PATH/docker-compose.yml  
   yq ".services.mq.environment += {\"MQ_USERNAME\": \"$MQ_USERNAME\"}" -i $INSTALL_PATH/docker-compose.yml  
   yq ".services.mq.environment += {\"MQ_USERNAME\": \"$MQ_USERNAME\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.mq.environment += {\"MQ_PASSWORD\": \"$MQ_PASSWORD\"}" -i $INSTALL_PATH/docker-compose.yml  
 
 
+  yq ".services.turn += {\"container_name\": \"turn\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.turn += {\"image\": \"gravitl/turnserver:v1.0.0\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.turn += {\"network_mode\": \"host\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.turn.volumes += {\"turn_server:/etc/config\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.turn.environment += {\"DEBUG_MODE\": \"off\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.turn.environment += {\"VERBOSITY\": \"1\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.turn.environment += {\"TURN_PORT\": \"3479\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.turn.environment += {\"TURN_API_PORT\": \"8089\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.turn.environment += {\"CORS_ALLOWED_ORIGIN\": \"*\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.turn.environment += {\"TURN_SERVER_HOST\": \"$TURN_DOMAIN\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.turn.environment += {\"TURN_USERNAME\": \"$TURN_USERNAME\"}" -i $INSTALL_PATH/docker-compose.yml  
+  yq ".services.turn.environment += {\"TURN_PASSWORD\": \"$TURN_PASSWORD\"}" -i $INSTALL_PATH/docker-compose.yml  
+
+  yq ".services.volumes += {\".turn_server\": \"{}\"}" -i $INSTALL_PATH/docker-compose.yml  
 
 
   #remove unnecessary ports
   #remove unnecessary ports
   yq eval 'del( .services.netmaker.ports[] | select(. == "51821*") )' -i $INSTALL_PATH/docker-compose.yml
   yq eval 'del( .services.netmaker.ports[] | select(. == "51821*") )' -i $INSTALL_PATH/docker-compose.yml

+ 75 - 3
servercfg/serverconf.go

@@ -107,10 +107,44 @@ func GetServerInfo() models.ServerConfig {
 	cfg.Is_EE = Is_EE
 	cfg.Is_EE = Is_EE
 	cfg.StunPort = GetStunPort()
 	cfg.StunPort = GetStunPort()
 	cfg.StunList = GetStunList()
 	cfg.StunList = GetStunList()
-
+	cfg.TurnDomain = GetTurnHost()
+	cfg.TurnPort = GetTurnPort()
+	cfg.UseTurn = IsUsingTurn()
 	return cfg
 	return cfg
 }
 }
 
 
+// GetTurnHost - fetches the turn host domain
+func GetTurnHost() string {
+	turnServer := ""
+	if os.Getenv("TURN_SERVER_HOST") != "" {
+		turnServer = os.Getenv("TURN_SERVER_HOST")
+	} else if config.Config.Server.TurnServer != "" {
+		turnServer = config.Config.Server.TurnServer
+	}
+	return turnServer
+}
+
+// IsUsingTurn - check if server has turn configured
+func IsUsingTurn() (b bool) {
+	if os.Getenv("USE_TURN") != "" {
+		b = os.Getenv("USE_TURN") == "true"
+	} else {
+		b = config.Config.Server.UseTurn
+	}
+	return
+}
+
+// GetTurnApiHost - fetches the turn api host domain
+func GetTurnApiHost() string {
+	turnApiServer := ""
+	if os.Getenv("TURN_SERVER_API_HOST") != "" {
+		turnApiServer = os.Getenv("TURN_SERVER_API_HOST")
+	} else if config.Config.Server.TurnApiServer != "" {
+		turnApiServer = config.Config.Server.TurnApiServer
+	}
+	return turnApiServer
+}
+
 // GetFrontendURL - gets the frontend url
 // GetFrontendURL - gets the frontend url
 func GetFrontendURL() string {
 func GetFrontendURL() string {
 	var frontend = ""
 	var frontend = ""
@@ -186,11 +220,11 @@ func GetAPIPort() string {
 // GetStunList - gets the stun servers
 // GetStunList - gets the stun servers
 func GetStunList() []models.StunServer {
 func GetStunList() []models.StunServer {
 	stunList := []models.StunServer{
 	stunList := []models.StunServer{
-		models.StunServer{
+		{
 			Domain: "stun1.netmaker.io",
 			Domain: "stun1.netmaker.io",
 			Port:   3478,
 			Port:   3478,
 		},
 		},
-		models.StunServer{
+		{
 			Domain: "stun2.netmaker.io",
 			Domain: "stun2.netmaker.io",
 			Port:   3478,
 			Port:   3478,
 		},
 		},
@@ -642,6 +676,44 @@ func GetStunPort() int {
 	return port
 	return port
 }
 }
 
 
+// GetTurnPort - Get the port to run the turn server on
+func GetTurnPort() int {
+	port := 3479 //default
+	if os.Getenv("TURN_PORT") != "" {
+		portInt, err := strconv.Atoi(os.Getenv("TURN_PORT"))
+		if err == nil {
+			port = portInt
+		}
+	} else if config.Config.Server.TurnPort != 0 {
+		port = config.Config.Server.TurnPort
+	}
+	return port
+}
+
+// GetTurnUserName - fetches the turn server username
+func GetTurnUserName() string {
+	userName := ""
+	if os.Getenv("TURN_USERNAME") != "" {
+		userName = os.Getenv("TURN_USERNAME")
+	} else {
+		userName = config.Config.Server.TurnUserName
+	}
+	return userName
+
+}
+
+// GetTurnPassword - fetches the turn server password
+func GetTurnPassword() string {
+	pass := ""
+	if os.Getenv("TURN_PASSWORD") != "" {
+		pass = os.Getenv("TURN_PASSWORD")
+	} else {
+		pass = config.Config.Server.TurnPassword
+	}
+	return pass
+
+}
+
 // IsProxyEnabled - is proxy on or off
 // IsProxyEnabled - is proxy on or off
 func IsProxyEnabled() bool {
 func IsProxyEnabled() bool {
 	var enabled = false //default
 	var enabled = false //default

+ 1 - 1
swagger.yaml

@@ -704,7 +704,7 @@ info:
 
 
         API calls must be authenticated via a header of the format -H “Authorization: Bearer <YOUR_SECRET_KEY>” There are two methods to obtain YOUR_SECRET_KEY: 1. Using the masterkey. By default, this value is “secret key,” but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [Netmaker](https://docs.netmaker.org/index.html) documentation for more details. 2. Using a JWT received for a node. This can be retrieved by calling the /api/nodes/<network>/authenticate endpoint, as documented below.
         API calls must be authenticated via a header of the format -H “Authorization: Bearer <YOUR_SECRET_KEY>” There are two methods to obtain YOUR_SECRET_KEY: 1. Using the masterkey. By default, this value is “secret key,” but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [Netmaker](https://docs.netmaker.org/index.html) documentation for more details. 2. Using a JWT received for a node. This can be retrieved by calling the /api/nodes/<network>/authenticate endpoint, as documented below.
     title: Netmaker
     title: Netmaker
-    version: 0.18.7
+    version: 0.19.0
 paths:
 paths:
     /api/dns:
     /api/dns:
         get:
         get: