浏览代码

Merge branch 'develop' of https://github.com/gravitl/netmaker into NET-655

Abhishek Kondur 1 年之前
父节点
当前提交
68a3e5b2b8

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

@@ -31,7 +31,7 @@ body:
       label: Version
       label: Version
       description: What version are you running?
       description: What version are you running?
       options:
       options:
-        - v0.21.3
+        - v0.22.0
         - v0.21.2
         - v0.21.2
         - v0.21.1
         - v0.21.1
         - v0.21.0
         - v0.21.0

+ 4 - 4
.github/workflows/test.yml

@@ -13,7 +13,7 @@ jobs:
       - name: Checkout
       - name: Checkout
         uses: actions/checkout@v4
         uses: actions/checkout@v4
       - name: Setup Go
       - name: Setup Go
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
         with:
           go-version: 1.19
           go-version: 1.19
       - name: Build
       - name: Build
@@ -27,7 +27,7 @@ jobs:
       - name: Checkout
       - name: Checkout
         uses: actions/checkout@v4
         uses: actions/checkout@v4
       - name: Setup go
       - name: Setup go
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
         with:
           go-version: 1.19
           go-version: 1.19
       - name: Build
       - name: Build
@@ -44,7 +44,7 @@ jobs:
       - name: Checkout
       - name: Checkout
         uses: actions/checkout@v4
         uses: actions/checkout@v4
       - name: Setup Go
       - name: Setup Go
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
         with:
           go-version: 1.19
           go-version: 1.19
       - name: run tests
       - name: run tests
@@ -64,7 +64,7 @@ jobs:
       - name: Checkout
       - name: Checkout
         uses: actions/checkout@v4
         uses: actions/checkout@v4
       - name: Setup Go
       - name: Setup Go
-        uses: actions/setup-go@v4
+        uses: actions/setup-go@v5
         with:
         with:
           go-version: 1.19
           go-version: 1.19
       - name: run static checks
       - name: run static checks

+ 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.18.5
+FROM alpine:3.19.0
 
 
 # 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.18.5
+FROM alpine:3.19.0
 ARG version 
 ARG version 
 WORKDIR /app
 WORKDIR /app
 COPY ./netmaker /root/netmaker
 COPY ./netmaker /root/netmaker

+ 1 - 1
README.md

@@ -16,7 +16,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.21.3-informational?style=flat-square" />
+    <img src="https://img.shields.io/badge/Version-0.22.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" />

+ 28 - 5
cli/cmd/context/set.go

@@ -1,9 +1,11 @@
 package context
 package context
 
 
 import (
 import (
+	"fmt"
 	"log"
 	"log"
 
 
 	"github.com/gravitl/netmaker/cli/config"
 	"github.com/gravitl/netmaker/cli/config"
+	"github.com/gravitl/netmaker/cli/functions"
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
 )
 )
 
 
@@ -13,6 +15,8 @@ var (
 	password  string
 	password  string
 	masterKey string
 	masterKey string
 	sso       bool
 	sso       bool
+	tenantId  string
+	saas      bool
 )
 )
 
 
 var contextSetCmd = &cobra.Command{
 var contextSetCmd = &cobra.Command{
@@ -27,10 +31,28 @@ var contextSetCmd = &cobra.Command{
 			Password:  password,
 			Password:  password,
 			MasterKey: masterKey,
 			MasterKey: masterKey,
 			SSO:       sso,
 			SSO:       sso,
+			TenantId:  tenantId,
+			Saas:      saas,
 		}
 		}
-		if ctx.Username == "" && ctx.MasterKey == "" && !ctx.SSO {
-			cmd.Usage()
-			log.Fatal("Either username/password or master key is required")
+		if !ctx.Saas {
+			if ctx.Username == "" && ctx.MasterKey == "" && !ctx.SSO {
+				log.Fatal("Either username/password or master key is required")
+				cmd.Usage()
+			}
+			if ctx.Endpoint == "" {
+				log.Fatal("Endpoint is required when for self-hosted tenants")
+				cmd.Usage()
+			}
+		} else {
+			if ctx.TenantId == "" {
+				log.Fatal("Tenant ID is required for SaaS tenants")
+				cmd.Usage()
+			}
+			ctx.Endpoint = fmt.Sprintf(functions.TenantUrlTemplate, tenantId)
+			if ctx.Username == "" && ctx.Password == "" && !ctx.SSO {
+				log.Fatal("Username/password is required for non-SSO SaaS contexts")
+				cmd.Usage()
+			}
 		}
 		}
 		config.SetContext(args[0], ctx)
 		config.SetContext(args[0], ctx)
 	},
 	},
@@ -38,11 +60,12 @@ var contextSetCmd = &cobra.Command{
 
 
 func init() {
 func init() {
 	contextSetCmd.Flags().StringVar(&endpoint, "endpoint", "", "Endpoint of the API Server")
 	contextSetCmd.Flags().StringVar(&endpoint, "endpoint", "", "Endpoint of the API Server")
-	contextSetCmd.MarkFlagRequired("endpoint")
 	contextSetCmd.Flags().StringVar(&username, "username", "", "Username")
 	contextSetCmd.Flags().StringVar(&username, "username", "", "Username")
 	contextSetCmd.Flags().StringVar(&password, "password", "", "Password")
 	contextSetCmd.Flags().StringVar(&password, "password", "", "Password")
 	contextSetCmd.MarkFlagsRequiredTogether("username", "password")
 	contextSetCmd.MarkFlagsRequiredTogether("username", "password")
-	contextSetCmd.Flags().BoolVar(&sso, "sso", false, "Login via Single Sign On (SSO) ?")
+	contextSetCmd.Flags().BoolVar(&sso, "sso", false, "Login via Single Sign On (SSO)?")
 	contextSetCmd.Flags().StringVar(&masterKey, "master_key", "", "Master Key")
 	contextSetCmd.Flags().StringVar(&masterKey, "master_key", "", "Master Key")
+	contextSetCmd.Flags().StringVar(&tenantId, "tenant_id", "", "Tenant ID")
+	contextSetCmd.Flags().BoolVar(&saas, "saas", false, "Is this context for a SaaS tenant?")
 	rootCmd.AddCommand(contextSetCmd)
 	rootCmd.AddCommand(contextSetCmd)
 }
 }

+ 2 - 0
cli/config/config.go

@@ -18,6 +18,8 @@ type Context struct {
 	Current   bool   `yaml:"current,omitempty"`
 	Current   bool   `yaml:"current,omitempty"`
 	AuthToken string `yaml:"auth_token,omitempty"`
 	AuthToken string `yaml:"auth_token,omitempty"`
 	SSO       bool   `yaml:"sso,omitempty"`
 	SSO       bool   `yaml:"sso,omitempty"`
+	TenantId  string `yaml:"tenant_id,omitempty"`
+	Saas      bool   `yaml:"saas,omitempty"`
 }
 }
 
 
 var (
 var (

+ 262 - 21
cli/functions/http_client.go

@@ -11,11 +11,19 @@ import (
 	"os"
 	"os"
 	"os/signal"
 	"os/signal"
 	"strings"
 	"strings"
+	"time"
 
 
 	"github.com/gorilla/websocket"
 	"github.com/gorilla/websocket"
 	"github.com/gravitl/netmaker/cli/config"
 	"github.com/gravitl/netmaker/cli/config"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
+	"golang.org/x/exp/slog"
+)
+
+const (
+	ambBaseUrl        = "https://api.accounts.netmaker.io"
+	TenantUrlTemplate = "https://api-%s.app.prod.netmaker.io"
+	ambOauthWssUrl    = "wss://api.accounts.netmaker.io/api/v1/auth/sso"
 )
 )
 
 
 func ssoLogin(endpoint string) string {
 func ssoLogin(endpoint string) string {
@@ -81,34 +89,57 @@ func getAuthToken(ctx config.Context, force bool) string {
 	if !force && ctx.AuthToken != "" {
 	if !force && ctx.AuthToken != "" {
 		return ctx.AuthToken
 		return ctx.AuthToken
 	}
 	}
-	if ctx.SSO {
-		authToken := ssoLogin(ctx.Endpoint)
+	if !ctx.Saas {
+		if ctx.SSO {
+			authToken := ssoLogin(ctx.Endpoint)
+			config.SetAuthToken(authToken)
+			return authToken
+		}
+		authParams := &models.UserAuthParams{UserName: ctx.Username, Password: ctx.Password}
+		payload, err := json.Marshal(authParams)
+		if err != nil {
+			log.Fatal(err)
+		}
+		res, err := http.Post(ctx.Endpoint+"/api/users/adm/authenticate", "application/json", bytes.NewReader(payload))
+		if err != nil {
+			log.Fatal(err)
+		}
+		defer res.Body.Close()
+		resBodyBytes, err := io.ReadAll(res.Body)
+		if err != nil {
+			log.Fatalf("Client could not read response body: %s", err)
+		}
+		if res.StatusCode != http.StatusOK {
+			log.Fatalf("Error Status: %d Response: %s", res.StatusCode, string(resBodyBytes))
+		}
+		body := new(models.SuccessResponse)
+		if err := json.Unmarshal(resBodyBytes, body); err != nil {
+			log.Fatalf("Error unmarshalling JSON: %s", err)
+		}
+		authToken := body.Response.(map[string]any)["AuthToken"].(string)
 		config.SetAuthToken(authToken)
 		config.SetAuthToken(authToken)
 		return authToken
 		return authToken
 	}
 	}
-	authParams := &models.UserAuthParams{UserName: ctx.Username, Password: ctx.Password}
-	payload, err := json.Marshal(authParams)
-	if err != nil {
-		log.Fatal(err)
+
+	if !ctx.SSO {
+		sToken, _, err := basicAuthSaasSignin(ctx.Username, ctx.Password)
+		if err != nil {
+			log.Fatal(err)
+		}
+		authToken, _, err := tenantLogin(ctx, sToken)
+		if err != nil {
+			log.Fatal(err)
+		}
+		config.SetAuthToken(authToken)
+		return authToken
 	}
 	}
-	res, err := http.Post(ctx.Endpoint+"/api/users/adm/authenticate", "application/json", bytes.NewReader(payload))
+
+	accessToken, err := loginSaaSOauth(&models.SsoLoginReqDto{OauthProvider: "oidc"}, ctx.TenantId)
 	if err != nil {
 	if err != nil {
 		log.Fatal(err)
 		log.Fatal(err)
 	}
 	}
-	resBodyBytes, err := io.ReadAll(res.Body)
-	if err != nil {
-		log.Fatalf("Client could not read response body: %s", err)
-	}
-	if res.StatusCode != http.StatusOK {
-		log.Fatalf("Error Status: %d Response: %s", res.StatusCode, string(resBodyBytes))
-	}
-	body := new(models.SuccessResponse)
-	if err := json.Unmarshal(resBodyBytes, body); err != nil {
-		log.Fatalf("Error unmarshalling JSON: %s", err)
-	}
-	authToken := body.Response.(map[string]any)["AuthToken"].(string)
-	config.SetAuthToken(authToken)
-	return authToken
+	config.SetAuthToken(accessToken)
+	return accessToken
 }
 }
 
 
 func request[T any](method, route string, payload any) *T {
 func request[T any](method, route string, payload any) *T {
@@ -188,3 +219,213 @@ func get(route string) string {
 	}
 	}
 	return string(bodyBytes)
 	return string(bodyBytes)
 }
 }
+
+func basicAuthSaasSignin(email, password string) (string, http.Header, error) {
+	payload := models.SignInReqDto{
+		FormFields: []models.FormField{
+			{
+				Id:    "email",
+				Value: email,
+			},
+			{
+				Id:    "password",
+				Value: password,
+			},
+		},
+	}
+
+	var res models.SignInResDto
+
+	// Create a new HTTP client with a timeout
+	client := &http.Client{
+		Timeout: 30 * time.Second,
+	}
+
+	// Create the request body
+	payloadBuf := new(bytes.Buffer)
+	json.NewEncoder(payloadBuf).Encode(payload)
+
+	// Create the request
+	req, err := http.NewRequest("POST", ambBaseUrl+"/auth/signin", payloadBuf)
+	if err != nil {
+		return "", http.Header{}, err
+	}
+	req.Header.Set("Content-Type", "application/json; charset=utf-8")
+	req.Header.Set("rid", "thirdpartyemailpassword")
+
+	// Send the request
+	resp, err := client.Do(req)
+	if err != nil {
+		return "", http.Header{}, err
+	}
+	defer resp.Body.Close()
+
+	// Check the response status code
+	if resp.StatusCode != http.StatusOK {
+		return "", http.Header{}, fmt.Errorf("error authenticating: %s", resp.Status)
+	}
+
+	// Copy the response headers
+	resHeaders := resp.Header
+
+	// Decode the response body
+	err = json.NewDecoder(resp.Body).Decode(&res)
+	if err != nil {
+		return "", http.Header{}, err
+	}
+
+	sToken := resHeaders.Get(models.ResHeaderKeyStAccessToken)
+	encodedAccessToken := url.QueryEscape(sToken)
+
+	return encodedAccessToken, resHeaders, nil
+}
+
+func tenantLogin(ctx config.Context, sToken string) (string, string, error) {
+	url := fmt.Sprintf("%s/api/v1/tenant/login?tenant_id=%s", ambBaseUrl, ctx.TenantId)
+
+	client := &http.Client{}
+	req, err := http.NewRequest(http.MethodPost, url, nil)
+
+	if err != nil {
+		return "", "", err
+	}
+	req.Header.Add("Cookie", fmt.Sprintf("sAccessToken=%s", sToken))
+
+	res, err := client.Do(req)
+	if err != nil {
+		return "", "", err
+	}
+	defer res.Body.Close()
+
+	body, err := io.ReadAll(res.Body)
+	if err != nil {
+		return "", "", err
+	}
+
+	data := models.TenantLoginResDto{}
+	json.Unmarshal(body, &data)
+
+	return data.Response.AuthToken, fmt.Sprintf(TenantUrlTemplate, ctx.TenantId), nil
+}
+
+func loginSaaSOauth(payload *models.SsoLoginReqDto, tenantId string) (string, error) {
+	socketUrl := ambOauthWssUrl
+	// Dial the netmaker server controller
+	conn, _, err := websocket.DefaultDialer.Dial(socketUrl, nil)
+	if err != nil {
+		slog.Error("error connecting to endpoint ", "url", socketUrl, "err", err)
+		return "", err
+	}
+
+	defer conn.Close()
+	return handleServerSSORegisterConn(payload, conn, tenantId)
+}
+
+func handleServerSSORegisterConn(payload *models.SsoLoginReqDto, conn *websocket.Conn, tenantId string) (string, error) {
+	reqData, err := json.Marshal(payload)
+	if err != nil {
+		return "", err
+	}
+	if err := conn.WriteMessage(websocket.TextMessage, reqData); err != nil {
+		return "", err
+	}
+	dataCh := make(chan string)
+	defer close(dataCh)
+	interrupt := make(chan os.Signal, 1)
+	signal.Notify(interrupt, os.Interrupt)
+
+	go func() {
+		for {
+			msgType, msg, err := conn.ReadMessage()
+			if err != nil {
+				if msgType < 0 {
+					slog.Info("received close message from server")
+					return
+				}
+				if !strings.Contains(err.Error(), "normal") { // Error reading a message from the server
+					slog.Error("error msg", "err", err)
+				}
+				return
+			}
+			if msgType == websocket.CloseMessage {
+				slog.Info("received close message from server")
+				return
+			}
+			if strings.Contains(string(msg), "auth/sso") {
+				fmt.Printf("Please visit:\n %s \nto authenticate\n", string(msg))
+			} else {
+				var res models.SsoLoginData
+				if err := json.Unmarshal(msg, &res); err != nil {
+					return
+				}
+				accessToken, _, err := tenantLoginV2(res.AmbAccessToken, tenantId, res.Username)
+				if err != nil {
+					slog.Error("error logging in tenant", "err", err)
+					dataCh <- ""
+					return
+				}
+				dataCh <- accessToken
+				return
+			}
+		}
+	}()
+
+	for {
+		select {
+		case accessToken := <-dataCh:
+			if accessToken == "" {
+				slog.Info("error getting access token")
+				return "", fmt.Errorf("error getting access token")
+			}
+			return accessToken, nil
+		case <-time.After(30 * time.Second):
+			slog.Error("authentiation timed out")
+			os.Exit(1)
+		case <-interrupt:
+			slog.Info("interrupt received, closing connection")
+			// Cleanly close the connection by sending a close message and then
+			// waiting (with timeout) for the server to close the connection.
+			err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
+			if err != nil {
+				log.Fatal(err)
+			}
+			os.Exit(1)
+		}
+	}
+}
+
+func tenantLoginV2(ambJwt, tenantId, email string) (string, string, error) {
+	url := fmt.Sprintf("%s/api/v1/tenant/login/custom", ambBaseUrl)
+	payload := models.LoginReqDto{
+		Email:    email,
+		TenantID: tenantId,
+	}
+	payloadBuf := new(bytes.Buffer)
+	json.NewEncoder(payloadBuf).Encode(payload)
+
+	client := &http.Client{}
+	req, err := http.NewRequest("POST", url, payloadBuf)
+	if err != nil {
+		slog.Error("error creating request", "err", err)
+		return "", "", err
+	}
+	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", ambJwt))
+
+	res, err := client.Do(req)
+	if err != nil {
+		slog.Error("error sending request", "err", err)
+		return "", "", err
+	}
+	defer res.Body.Close()
+
+	body, err := io.ReadAll(res.Body)
+	if err != nil {
+		slog.Error("error reading response body", "err", err)
+		return "", "", err
+	}
+
+	data := models.TenantLoginResDto{}
+	json.Unmarshal(body, &data)
+
+	return data.Response.AuthToken, fmt.Sprintf(TenantUrlTemplate, tenantId), nil
+}

+ 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.21.3'
+    image: 'gravitl/netclient:v0.22.0'
     hostname: netmaker-1
     hostname: netmaker-1
     network_mode: host
     network_mode: host
     restart: on-failure
     restart: on-failure

+ 1 - 21
compose/docker-compose.yml

@@ -25,10 +25,6 @@ services:
       - COREDNS_ADDR=${SERVER_HOST}
       - COREDNS_ADDR=${SERVER_HOST}
       # Overrides SERVER_HOST if set. Useful for making HTTP available via different interfaces/networks.
       # Overrides SERVER_HOST if set. Useful for making HTTP available via different interfaces/networks.
       - SERVER_HTTP_HOST=api.${NM_DOMAIN}
       - SERVER_HTTP_HOST=api.${NM_DOMAIN}
-      # domain for your turn server
-      - TURN_SERVER_HOST=turn.${NM_DOMAIN}
-      # domain of the turn api server
-      - TURN_SERVER_API_HOST=https://turnapi.${NM_DOMAIN}
 
 
   netmaker-ui:
   netmaker-ui:
     container_name: netmaker-ui
     container_name: netmaker-ui
@@ -82,22 +78,6 @@ services:
       - ./wait.sh:/mosquitto/config/wait.sh
       - ./wait.sh:/mosquitto/config/wait.sh
       - mosquitto_logs:/mosquitto/log
       - mosquitto_logs:/mosquitto/log
       - mosquitto_data:/mosquitto/data
       - mosquitto_data:/mosquitto/data
-
-  turn:
-    container_name: turn
-    image: gravitl/turnserver:v1.0.0
-    env_file: ./netmaker.env
-    environment:
-      # config-dependant vars
-      - USERNAME=${TURN_USERNAME}
-      - PASSWORD=${TURN_PASSWORD}
-      # domain for your turn server
-      - TURN_SERVER_HOST=turn.${NM_DOMAIN}
-    network_mode: "host"
-    volumes:
-      - turn_server:/etc/config
-    restart: always
-
 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
@@ -105,4 +85,4 @@ volumes:
   dnsconfig: { } # storage for coredns
   dnsconfig: { } # storage for coredns
   mosquitto_logs: { } # storage for mqtt logs
   mosquitto_logs: { } # storage for mqtt logs
   mosquitto_data: { } # storage for mqtt data
   mosquitto_data: { } # storage for mqtt data
-  turn_server: { }
+

+ 1 - 1
controllers/docs.go

@@ -10,7 +10,7 @@
 //
 //
 //	Schemes: https
 //	Schemes: https
 //	BasePath: /
 //	BasePath: /
-//	Version: 0.21.3
+//	Version: 0.22.0
 //	Host: api.demo.netmaker.io
 //	Host: api.demo.netmaker.io
 //
 //
 //	Consumes:
 //	Consumes:

+ 0 - 7
controllers/enrollmentkeys.go

@@ -231,13 +231,6 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
-	// 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
 	hostExists := false
 	hostExists := false
 	if hostExists = logic.HostExists(&newHost); hostExists && len(enrollmentKey.Networks) == 0 {
 	if hostExists = logic.HostExists(&newHost); hostExists && len(enrollmentKey.Networks) == 0 {

+ 20 - 10
controllers/ext_client.go

@@ -216,18 +216,28 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
 	} else {
 	} else {
 		gwendpoint = fmt.Sprintf("%s:%d", host.EndpointIP.String(), host.ListenPort)
 		gwendpoint = fmt.Sprintf("%s:%d", host.EndpointIP.String(), host.ListenPort)
 	}
 	}
-	newAllowedIPs := network.AddressRange
-	if newAllowedIPs != "" && network.AddressRange6 != "" {
-		newAllowedIPs += ","
-	}
-	if network.AddressRange6 != "" {
-		newAllowedIPs += network.AddressRange6
-	}
-	if egressGatewayRanges, err := logic.GetEgressRangesOnNetwork(&client); err == nil {
-		for _, egressGatewayRange := range egressGatewayRanges {
-			newAllowedIPs += "," + egressGatewayRange
+	var newAllowedIPs string
+	if logic.IsInternetGw(gwnode) {
+		egressrange := "0.0.0.0/0"
+		if gwnode.Address6.IP != nil && client.Address6 != "" {
+			egressrange += "," + "::/0"
+		}
+		newAllowedIPs = egressrange
+	} else {
+		newAllowedIPs = network.AddressRange
+		if newAllowedIPs != "" && network.AddressRange6 != "" {
+			newAllowedIPs += ","
+		}
+		if network.AddressRange6 != "" {
+			newAllowedIPs += network.AddressRange6
+		}
+		if egressGatewayRanges, err := logic.GetEgressRangesOnNetwork(&client); err == nil {
+			for _, egressGatewayRange := range egressGatewayRanges {
+				newAllowedIPs += "," + egressGatewayRange
+			}
 		}
 		}
 	}
 	}
+
 	defaultDNS := ""
 	defaultDNS := ""
 	if client.DNS != "" {
 	if client.DNS != "" {
 		defaultDNS = "DNS = " + client.DNS
 		defaultDNS = "DNS = " + client.DNS

+ 2 - 7
controllers/hosts.go

@@ -546,19 +546,14 @@ func signalPeer(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 		return
 	}
 	}
-	if signal.ToHostPubKey == "" || (!servercfg.IsPro && signal.TurnRelayEndpoint == "") {
+	if signal.ToHostPubKey == "" {
 		msg := "insufficient data to signal peer"
 		msg := "insufficient data to signal peer"
 		logger.Log(0, r.Header.Get("user"), msg)
 		logger.Log(0, r.Header.Get("user"), msg)
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New(msg), "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New(msg), "badrequest"))
 		return
 		return
 	}
 	}
 	signal.IsPro = servercfg.IsPro
 	signal.IsPro = servercfg.IsPro
-	var peerHost *models.Host
-	if signal.ToHostID == "" {
-		peerHost, err = logic.GetHostByPubKey(signal.ToHostPubKey)
-	} else {
-		peerHost, err = logic.GetHost(signal.ToHostID)
-	}
+	peerHost, err := logic.GetHost(signal.ToHostID)
 	if err != nil {
 	if err != nil {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to signal, peer not found"), "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to signal, peer not found"), "badrequest"))
 		return
 		return

+ 35 - 0
controllers/network.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"net"
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 
 
@@ -246,6 +247,40 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 
 
+	// validate address ranges: must be private
+	if network.AddressRange != "" {
+		_, ipNet, err := net.ParseCIDR(network.AddressRange)
+		if err != nil {
+			logger.Log(0, r.Header.Get("user"), "failed to create network: ",
+				err.Error())
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+			return
+		}
+		if !ipNet.IP.IsPrivate() {
+			err := errors.New("address range must be private")
+			logger.Log(0, r.Header.Get("user"), "failed to create network: ",
+				err.Error())
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+			return
+		}
+	}
+	if network.AddressRange6 != "" {
+		_, ipNet, err := net.ParseCIDR(network.AddressRange6)
+		if err != nil {
+			logger.Log(0, r.Header.Get("user"), "failed to create network: ",
+				err.Error())
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+			return
+		}
+		if !ipNet.IP.IsPrivate() {
+			err := errors.New("address range must be private")
+			logger.Log(0, r.Header.Get("user"), "failed to create network: ",
+				err.Error())
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+			return
+		}
+	}
+
 	network, err = logic.CreateNetwork(network)
 	network, err = logic.CreateNetwork(network)
 	if err != nil {
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"), "failed to create network: ",
 		logger.Log(0, r.Header.Get("user"), "failed to create network: ",

+ 0 - 10
docker/Caddyfile

@@ -24,16 +24,6 @@ https://api.{$NM_DOMAIN} {
 	reverse_proxy http://netmaker:8081
 	reverse_proxy http://netmaker:8081
 }
 }
 
 
-# TURN
-https://turn.{$NM_DOMAIN} {
-	reverse_proxy host.docker.internal:3479
-}
-
-# TURN API
-https://turnapi.{$NM_DOMAIN} {
-	reverse_proxy http://host.docker.internal:8089
-}
-
 # MQ
 # MQ
 wss://broker.{$NM_DOMAIN} {
 wss://broker.{$NM_DOMAIN} {
 	reverse_proxy ws://mq:8883 # For EMQX websockets use `reverse_proxy ws://mq:8083`
 	reverse_proxy ws://mq:8883 # For EMQX websockets use `reverse_proxy ws://mq:8083`

+ 0 - 10
docker/Caddyfile-pro

@@ -39,16 +39,6 @@ https://api.{$NM_DOMAIN} {
 	reverse_proxy http://netmaker:8081
 	reverse_proxy http://netmaker:8081
 }
 }
 
 
-# TURN
-https://turn.{$NM_DOMAIN} {
-	reverse_proxy host.docker.internal:3479
-}
-
-# TURN API
-https://turnapi.{$NM_DOMAIN} {
-	reverse_proxy http://host.docker.internal:8089
-}
-
 # MQ
 # MQ
 wss://broker.{$NM_DOMAIN} {
 wss://broker.{$NM_DOMAIN} {
 	reverse_proxy ws://mq:8883
 	reverse_proxy ws://mq:8883

+ 2 - 2
go.mod

@@ -26,13 +26,13 @@ require (
 )
 )
 
 
 require (
 require (
-	filippo.io/edwards25519 v1.0.0
+	filippo.io/edwards25519 v1.1.0
 	github.com/c-robinson/iplib v1.0.7
 	github.com/c-robinson/iplib v1.0.7
 	github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0
 	github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0
 )
 )
 
 
 require (
 require (
-	github.com/coreos/go-oidc/v3 v3.8.0
+	github.com/coreos/go-oidc/v3 v3.9.0
 	github.com/gorilla/websocket v1.5.1
 	github.com/gorilla/websocket v1.5.1
 	golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
 	golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
 )
 )

+ 4 - 4
go.sum

@@ -2,13 +2,13 @@ cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZN
 cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
 cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
 cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
 cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
 cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
 cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
-filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
-filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/c-robinson/iplib v1.0.7 h1:Dh9AINAlkc+NsNzZuFiVs+pi3AjN+0B7mu01KHdJKHU=
 github.com/c-robinson/iplib v1.0.7 h1:Dh9AINAlkc+NsNzZuFiVs+pi3AjN+0B7mu01KHdJKHU=
 github.com/c-robinson/iplib v1.0.7/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo=
 github.com/c-robinson/iplib v1.0.7/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo=
-github.com/coreos/go-oidc/v3 v3.8.0 h1:s3e30r6VEl3/M7DTSCEuImmrfu1/1WBgA0cXkdzkrAY=
-github.com/coreos/go-oidc/v3 v3.8.0/go.mod h1:yQzSCqBnK3e6Fs5l+f5i0F8Kwf0zpH9bPEsbY00KanM=
+github.com/coreos/go-oidc/v3 v3.9.0 h1:0J/ogVOd4y8P0f0xUh8l9t07xRP/d8tccvjHl2dcsSo=
+github.com/coreos/go-oidc/v3 v3.9.0/go.mod h1:rTKz2PYwftcrtoCzV5g5kvfJoWcm0Mk8AF8y1iAQro4=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 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=

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

+ 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.21.3
+        image: gravitl/netmaker-ui:v0.22.0
         ports:
         ports:
         - containerPort: 443
         - containerPort: 443
         env:
         env:

+ 32 - 0
logic/extpeers.go

@@ -438,3 +438,35 @@ func getExtpeersExtraRoutes(network string) (egressRoutes []models.EgressNetwork
 	}
 	}
 	return
 	return
 }
 }
+
+func GetExtclientAllowedIPs(client models.ExtClient) (allowedIPs []string) {
+	gwnode, err := GetNodeByID(client.IngressGatewayID)
+	if err != nil {
+		logger.Log(0,
+			fmt.Sprintf("failed to get ingress gateway node [%s] info: %v", client.IngressGatewayID, err))
+		return
+	}
+
+	network, err := GetParentNetwork(client.Network)
+	if err != nil {
+		logger.Log(1, "Could not retrieve Ingress Gateway Network", client.Network)
+		return
+	}
+	if IsInternetGw(gwnode) {
+		egressrange := "0.0.0.0/0"
+		if gwnode.Address6.IP != nil && client.Address6 != "" {
+			egressrange += "," + "::/0"
+		}
+		allowedIPs = []string{egressrange}
+	} else {
+		allowedIPs = []string{network.AddressRange}
+
+		if network.AddressRange6 != "" {
+			allowedIPs = append(allowedIPs, network.AddressRange6)
+		}
+		if egressGatewayRanges, err := GetEgressRangesOnNetwork(&client); err == nil {
+			allowedIPs = append(allowedIPs, egressGatewayRanges...)
+		}
+	}
+	return
+}

+ 14 - 6
logic/gateway.go

@@ -10,6 +10,16 @@ import (
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 )
 )
 
 
+var (
+	// SetInternetGw - sets the node as internet gw based on flag bool
+	SetInternetGw = func(node *models.Node, flag bool) {
+	}
+	// IsInternetGw - checks if node is acting as internet gw
+	IsInternetGw = func(node models.Node) bool {
+		return false
+	}
+)
+
 // GetInternetGateways - gets all the nodes that are internet gateways
 // GetInternetGateways - gets all the nodes that are internet gateways
 func GetInternetGateways() ([]models.Node, error) {
 func GetInternetGateways() ([]models.Node, error) {
 	nodes, err := GetAllNodes()
 	nodes, err := GetAllNodes()
@@ -78,12 +88,8 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
 	}
 	}
 	for i := len(gateway.Ranges) - 1; i >= 0; i-- {
 	for i := len(gateway.Ranges) - 1; i >= 0; i-- {
 		// check if internet gateway IPv4
 		// check if internet gateway IPv4
-		if gateway.Ranges[i] == "0.0.0.0/0" && FreeTier {
-			return models.Node{}, fmt.Errorf("currently IPv4 internet gateways are not supported on the free tier: %s", gateway.Ranges[i])
-		}
-		// check if internet gateway IPv6
-		if gateway.Ranges[i] == "::/0" {
-			return models.Node{}, fmt.Errorf("currently IPv6 internet gateways are not supported: %s", gateway.Ranges[i])
+		if gateway.Ranges[i] == "0.0.0.0/0" || gateway.Ranges[i] == "::/0" {
+			return models.Node{}, fmt.Errorf("create internet gateways on the remote client gateway")
 		}
 		}
 		normalized, err := NormalizeCIDR(gateway.Ranges[i])
 		normalized, err := NormalizeCIDR(gateway.Ranges[i])
 		if err != nil {
 		if err != nil {
@@ -163,6 +169,7 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq
 		return models.Node{}, err
 		return models.Node{}, err
 	}
 	}
 	node.IsIngressGateway = true
 	node.IsIngressGateway = true
+	SetInternetGw(&node, ingress.IsInternetGateway)
 	node.IngressGatewayRange = network.AddressRange
 	node.IngressGatewayRange = network.AddressRange
 	node.IngressGatewayRange6 = network.AddressRange6
 	node.IngressGatewayRange6 = network.AddressRange6
 	node.IngressDNS = ingress.ExtclientDNS
 	node.IngressDNS = ingress.ExtclientDNS
@@ -215,6 +222,7 @@ func DeleteIngressGateway(nodeid string) (models.Node, []models.ExtClient, error
 	logger.Log(3, "deleting ingress gateway")
 	logger.Log(3, "deleting ingress gateway")
 	node.LastModified = time.Now()
 	node.LastModified = time.Now()
 	node.IsIngressGateway = false
 	node.IsIngressGateway = false
+	node.IsInternetGateway = false
 	node.IngressGatewayRange = ""
 	node.IngressGatewayRange = ""
 	err = UpsertNode(&node)
 	err = UpsertNode(&node)
 	if err != nil {
 	if err != nil {

+ 0 - 63
logic/hosts.go

@@ -2,16 +2,12 @@ package logic
 
 
 import (
 import (
 	"crypto/md5"
 	"crypto/md5"
-	"encoding/base64"
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	"net/http"
 	"sort"
 	"sort"
-	"strconv"
 	"sync"
 	"sync"
 
 
-	"github.com/devilcove/httpclient"
 	"github.com/google/uuid"
 	"github.com/google/uuid"
 	"golang.org/x/crypto/bcrypt"
 	"golang.org/x/crypto/bcrypt"
 
 
@@ -197,12 +193,6 @@ 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)
@@ -306,10 +296,6 @@ func RemoveHost(h *models.Host, forceDelete bool) error {
 		return fmt.Errorf("host still has associated nodes")
 		return fmt.Errorf("host still has associated nodes")
 	}
 	}
 
 
-	if servercfg.IsUsingTurn() {
-		DeRegisterHostWithTurn(h.ID.String())
-	}
-
 	if len(h.Nodes) > 0 {
 	if len(h.Nodes) > 0 {
 		if err := DisassociateAllNodesFromHost(h.ID.String()); err != nil {
 		if err := DisassociateAllNodesFromHost(h.ID.String()); err != nil {
 			return err
 			return err
@@ -329,9 +315,6 @@ func RemoveHost(h *models.Host, forceDelete bool) error {
 
 
 // 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)
-	}
 
 
 	err := database.DeleteRecord(database.HOSTS_TABLE_NAME, hostID)
 	err := database.DeleteRecord(database.HOSTS_TABLE_NAME, hostID)
 	if err != nil {
 	if err != nil {
@@ -568,52 +551,6 @@ func ConvHostPassToHash(hostPass string) string {
 	return fmt.Sprintf("%x", md5.Sum([]byte(hostPass)))
 	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 {

+ 32 - 0
logic/peers.go

@@ -241,8 +241,18 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				logger.Log(1, "error retrieving external clients:", err.Error())
 				logger.Log(1, "error retrieving external clients:", err.Error())
 			}
 			}
 		}
 		}
+		addedInetGwRanges := false
 		if node.IsEgressGateway && node.EgressGatewayRequest.NatEnabled == "yes" && len(node.EgressGatewayRequest.Ranges) > 0 {
 		if node.IsEgressGateway && node.EgressGatewayRequest.NatEnabled == "yes" && len(node.EgressGatewayRequest.Ranges) > 0 {
 			hostPeerUpdate.FwUpdate.IsEgressGw = true
 			hostPeerUpdate.FwUpdate.IsEgressGw = true
+			if IsInternetGw(node) {
+				hostPeerUpdate.FwUpdate.IsEgressGw = true
+				egressrange := []string{"0.0.0.0/0"}
+				if node.Address6.IP != nil {
+					egressrange = append(egressrange, "::/0")
+				}
+				node.EgressGatewayRequest.Ranges = append(node.EgressGatewayRequest.Ranges, egressrange...)
+				addedInetGwRanges = true
+			}
 			hostPeerUpdate.FwUpdate.EgressInfo[node.ID.String()] = models.EgressInfo{
 			hostPeerUpdate.FwUpdate.EgressInfo[node.ID.String()] = models.EgressInfo{
 				EgressID: node.ID.String(),
 				EgressID: node.ID.String(),
 				Network:  node.PrimaryNetworkRange(),
 				Network:  node.PrimaryNetworkRange(),
@@ -252,6 +262,28 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				},
 				},
 				EgressGWCfg: node.EgressGatewayRequest,
 				EgressGWCfg: node.EgressGatewayRequest,
 			}
 			}
+
+		}
+		if IsInternetGw(node) && !addedInetGwRanges {
+			hostPeerUpdate.FwUpdate.IsEgressGw = true
+			egressrange := []string{"0.0.0.0/0"}
+			if node.Address6.IP != nil {
+				egressrange = append(egressrange, "::/0")
+			}
+			hostPeerUpdate.FwUpdate.EgressInfo[node.ID.String()] = models.EgressInfo{
+				EgressID: node.ID.String(),
+				Network:  node.PrimaryAddressIPNet(),
+				EgressGwAddr: net.IPNet{
+					IP:   net.ParseIP(node.PrimaryAddress()),
+					Mask: getCIDRMaskFromAddr(node.PrimaryAddress()),
+				},
+				EgressGWCfg: models.EgressGatewayRequest{
+					NodeID:     node.ID.String(),
+					NetID:      node.Network,
+					NatEnabled: "yes",
+					Ranges:     egressrange,
+				},
+			}
 		}
 		}
 	}
 	}
 	// == post peer calculations ==
 	// == post peer calculations ==

+ 1 - 1
main.go

@@ -28,7 +28,7 @@ import (
 	"golang.org/x/exp/slog"
 	"golang.org/x/exp/slog"
 )
 )
 
 
-var version = "v0.21.3"
+var version = "v0.22.0"
 
 
 // Start DB Connection and start API Request Handler
 // Start DB Connection and start API Request Handler
 func main() {
 func main() {

+ 30 - 0
migrate/migrate.go

@@ -18,6 +18,7 @@ func Run() {
 	updateEnrollmentKeys()
 	updateEnrollmentKeys()
 	assignSuperAdmin()
 	assignSuperAdmin()
 	updateHosts()
 	updateHosts()
+	updateNodes()
 }
 }
 
 
 func assignSuperAdmin() {
 func assignSuperAdmin() {
@@ -137,3 +138,32 @@ func updateHosts() {
 		}
 		}
 	}
 	}
 }
 }
+
+func updateNodes() {
+	nodes, err := logic.GetAllNodes()
+	if err != nil {
+		slog.Error("migration failed for nodes", "error", err)
+		return
+	}
+	for _, node := range nodes {
+		if node.IsEgressGateway {
+			egressRanges, update := removeInterGw(node.EgressGatewayRanges)
+			if update {
+				node.EgressGatewayRequest.Ranges = egressRanges
+				node.EgressGatewayRanges = egressRanges
+				logic.UpsertNode(&node)
+			}
+		}
+	}
+}
+
+func removeInterGw(egressRanges []string) ([]string, bool) {
+	update := false
+	for i := len(egressRanges) - 1; i >= 0; i-- {
+		if egressRanges[i] == "0.0.0.0/0" || egressRanges[i] == "::/0" {
+			update = true
+			egressRanges = append(egressRanges[:i], egressRanges[i+1:]...)
+		}
+	}
+	return egressRanges, update
+}

+ 3 - 5
models/api_node.go

@@ -28,6 +28,7 @@ type ApiNode struct {
 	RelayedNodes            []string `json:"relaynodes" yaml:"relayedNodes"`
 	RelayedNodes            []string `json:"relaynodes" yaml:"relayedNodes"`
 	IsEgressGateway         bool     `json:"isegressgateway"`
 	IsEgressGateway         bool     `json:"isegressgateway"`
 	IsIngressGateway        bool     `json:"isingressgateway"`
 	IsIngressGateway        bool     `json:"isingressgateway"`
+	IsInternetGateway       bool     `json:"isinternetgateway" yaml:"isinternetgateway"`
 	EgressGatewayRanges     []string `json:"egressgatewayranges"`
 	EgressGatewayRanges     []string `json:"egressgatewayranges"`
 	EgressGatewayNatEnabled bool     `json:"egressgatewaynatenabled"`
 	EgressGatewayNatEnabled bool     `json:"egressgatewaynatenabled"`
 	DNSOn                   bool     `json:"dnson"`
 	DNSOn                   bool     `json:"dnson"`
@@ -67,6 +68,7 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node {
 	convertedNode.IngressGatewayRange6 = currentNode.IngressGatewayRange6
 	convertedNode.IngressGatewayRange6 = currentNode.IngressGatewayRange6
 	convertedNode.DNSOn = a.DNSOn
 	convertedNode.DNSOn = a.DNSOn
 	convertedNode.IngressDNS = a.IngressDns
 	convertedNode.IngressDNS = a.IngressDns
+	convertedNode.IsInternetGateway = a.IsInternetGateway
 	convertedNode.EgressGatewayRequest = currentNode.EgressGatewayRequest
 	convertedNode.EgressGatewayRequest = currentNode.EgressGatewayRequest
 	convertedNode.EgressGatewayNatEnabled = currentNode.EgressGatewayNatEnabled
 	convertedNode.EgressGatewayNatEnabled = currentNode.EgressGatewayNatEnabled
 	convertedNode.RelayedNodes = a.RelayedNodes
 	convertedNode.RelayedNodes = a.RelayedNodes
@@ -88,10 +90,6 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node {
 	} else if !isEmptyAddr(currentNode.LocalAddress.String()) {
 	} else if !isEmptyAddr(currentNode.LocalAddress.String()) {
 		convertedNode.LocalAddress = currentNode.LocalAddress
 		convertedNode.LocalAddress = currentNode.LocalAddress
 	}
 	}
-	udpAddr, err := net.ResolveUDPAddr("udp", a.InternetGateway)
-	if err == nil {
-		convertedNode.InternetGateway = udpAddr
-	}
 	ip, addr, err := net.ParseCIDR(a.Address)
 	ip, addr, err := net.ParseCIDR(a.Address)
 	if err == nil {
 	if err == nil {
 		convertedNode.Address = *addr
 		convertedNode.Address = *addr
@@ -150,13 +148,13 @@ func (nm *Node) ConvertToAPINode() *ApiNode {
 	apiNode.DNSOn = nm.DNSOn
 	apiNode.DNSOn = nm.DNSOn
 	apiNode.IngressDns = nm.IngressDNS
 	apiNode.IngressDns = nm.IngressDNS
 	apiNode.Server = nm.Server
 	apiNode.Server = nm.Server
-	apiNode.InternetGateway = nm.InternetGateway.String()
 	if isEmptyAddr(apiNode.InternetGateway) {
 	if isEmptyAddr(apiNode.InternetGateway) {
 		apiNode.InternetGateway = ""
 		apiNode.InternetGateway = ""
 	}
 	}
 	apiNode.Connected = nm.Connected
 	apiNode.Connected = nm.Connected
 	apiNode.PendingDelete = nm.PendingDelete
 	apiNode.PendingDelete = nm.PendingDelete
 	apiNode.DefaultACL = nm.DefaultACL
 	apiNode.DefaultACL = nm.DefaultACL
+	apiNode.IsInternetGateway = nm.IsInternetGateway
 	apiNode.IsFailOver = nm.IsFailOver
 	apiNode.IsFailOver = nm.IsFailOver
 	apiNode.FailOverPeers = nm.FailOverPeers
 	apiNode.FailOverPeers = nm.FailOverPeers
 	apiNode.FailedOverBy = nm.FailedOverBy
 	apiNode.FailedOverBy = nm.FailedOverBy

+ 1 - 0
models/extclient.go

@@ -10,6 +10,7 @@ type ExtClient struct {
 	Address                string              `json:"address" bson:"address"`
 	Address                string              `json:"address" bson:"address"`
 	Address6               string              `json:"address6" bson:"address6"`
 	Address6               string              `json:"address6" bson:"address6"`
 	ExtraAllowedIPs        []string            `json:"extraallowedips" bson:"extraallowedips"`
 	ExtraAllowedIPs        []string            `json:"extraallowedips" bson:"extraallowedips"`
+	AllowedIPs             []string            `json:"allowed_ips"`
 	IngressGatewayID       string              `json:"ingressgatewayid" bson:"ingressgatewayid"`
 	IngressGatewayID       string              `json:"ingressgatewayid" bson:"ingressgatewayid"`
 	IngressGatewayEndpoint string              `json:"ingressgatewayendpoint" bson:"ingressgatewayendpoint"`
 	IngressGatewayEndpoint string              `json:"ingressgatewayendpoint" bson:"ingressgatewayendpoint"`
 	LastModified           int64               `json:"lastmodified" bson:"lastmodified"`
 	LastModified           int64               `json:"lastmodified" bson:"lastmodified"`

+ 11 - 16
models/host.go

@@ -110,8 +110,6 @@ const (
 	RequestAck HostMqAction = "REQ_ACK"
 	RequestAck HostMqAction = "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 HostMqAction = "CHECK_IN"
 	CheckIn HostMqAction = "CHECK_IN"
-	// RegisterWithTurn - registers host with turn server if configured
-	RegisterWithTurn HostMqAction = "REGISTER_WITH_TURN"
 	// UpdateKeys - update wireguard private/public keys
 	// UpdateKeys - update wireguard private/public keys
 	UpdateKeys HostMqAction = "UPDATE_KEYS"
 	UpdateKeys HostMqAction = "UPDATE_KEYS"
 	// RequestPull - request a pull from a host
 	// RequestPull - request a pull from a host
@@ -122,8 +120,6 @@ const (
 type SignalAction string
 type SignalAction string
 
 
 const (
 const (
-	// Disconnect - action to stop using turn connection
-	Disconnect SignalAction = "DISCONNECT"
 	// ConnNegotiation - action to negotiate connection between peers
 	// ConnNegotiation - action to negotiate connection between peers
 	ConnNegotiation SignalAction = "CONNECTION_NEGOTIATION"
 	ConnNegotiation SignalAction = "CONNECTION_NEGOTIATION"
 	// RelayME - action to relay the peer
 	// RelayME - action to relay the peer
@@ -146,18 +142,17 @@ type HostTurnRegister struct {
 
 
 // Signal - struct for signalling peer
 // Signal - struct for signalling peer
 type Signal struct {
 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"`
-	FromHostID        string       `json:"from_host_id"`
-	ToHostID          string       `json:"to_host_id"`
-	FromNodeID        string       `json:"from_node_id"`
-	ToNodeID          string       `json:"to_node_id"`
-	Reply             bool         `json:"reply"`
-	Action            SignalAction `json:"action"`
-	IsPro             bool         `json:"is_pro"`
-	TimeStamp         int64        `json:"timestamp"`
+	Server         string       `json:"server"`
+	FromHostPubKey string       `json:"from_host_pubkey"`
+	ToHostPubKey   string       `json:"to_host_pubkey"`
+	FromHostID     string       `json:"from_host_id"`
+	ToHostID       string       `json:"to_host_id"`
+	FromNodeID     string       `json:"from_node_id"`
+	ToNodeID       string       `json:"to_node_id"`
+	Reply          bool         `json:"reply"`
+	Action         SignalAction `json:"action"`
+	IsPro          bool         `json:"is_pro"`
+	TimeStamp      int64        `json:"timestamp"`
 }
 }
 
 
 // RegisterMsg - login message struct for hosts to join via SSO login
 // RegisterMsg - login message struct for hosts to join via SSO login

+ 21 - 21
models/node.go

@@ -54,27 +54,27 @@ type Iface struct {
 
 
 // CommonNode - represents a commonn node data elements shared by netmaker and netclient
 // CommonNode - represents a commonn node data elements shared by netmaker and netclient
 type CommonNode struct {
 type CommonNode struct {
-	ID                  uuid.UUID    `json:"id" yaml:"id"`
-	HostID              uuid.UUID    `json:"hostid" yaml:"hostid"`
-	Network             string       `json:"network" yaml:"network"`
-	NetworkRange        net.IPNet    `json:"networkrange" yaml:"networkrange"`
-	NetworkRange6       net.IPNet    `json:"networkrange6" yaml:"networkrange6"`
-	InternetGateway     *net.UDPAddr `json:"internetgateway" yaml:"internetgateway"`
-	Server              string       `json:"server" yaml:"server"`
-	Connected           bool         `json:"connected" yaml:"connected"`
-	Address             net.IPNet    `json:"address" yaml:"address"`
-	Address6            net.IPNet    `json:"address6" yaml:"address6"`
-	Action              string       `json:"action" yaml:"action"`
-	LocalAddress        net.IPNet    `json:"localaddress" yaml:"localaddress"`
-	IsEgressGateway     bool         `json:"isegressgateway" yaml:"isegressgateway"`
-	EgressGatewayRanges []string     `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
-	IsIngressGateway    bool         `json:"isingressgateway" yaml:"isingressgateway"`
-	IsRelayed           bool         `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
-	RelayedBy           string       `json:"relayedby" bson:"relayedby" yaml:"relayedby"`
-	IsRelay             bool         `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
-	RelayedNodes        []string     `json:"relaynodes" yaml:"relayedNodes"`
-	IngressDNS          string       `json:"ingressdns" yaml:"ingressdns"`
-	DNSOn               bool         `json:"dnson" yaml:"dnson"`
+	ID                  uuid.UUID `json:"id" yaml:"id"`
+	HostID              uuid.UUID `json:"hostid" yaml:"hostid"`
+	Network             string    `json:"network" yaml:"network"`
+	NetworkRange        net.IPNet `json:"networkrange" yaml:"networkrange"`
+	NetworkRange6       net.IPNet `json:"networkrange6" yaml:"networkrange6"`
+	Server              string    `json:"server" yaml:"server"`
+	Connected           bool      `json:"connected" yaml:"connected"`
+	Address             net.IPNet `json:"address" yaml:"address"`
+	Address6            net.IPNet `json:"address6" yaml:"address6"`
+	Action              string    `json:"action" yaml:"action"`
+	LocalAddress        net.IPNet `json:"localaddress" yaml:"localaddress"`
+	IsEgressGateway     bool      `json:"isegressgateway" yaml:"isegressgateway"`
+	EgressGatewayRanges []string  `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
+	IsIngressGateway    bool      `json:"isingressgateway" yaml:"isingressgateway"`
+	IsInternetGateway   bool      `json:"isinternetgateway" yaml:"isinternetgateway"`
+	IsRelayed           bool      `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
+	RelayedBy           string    `json:"relayedby" bson:"relayedby" yaml:"relayedby"`
+	IsRelay             bool      `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
+	RelayedNodes        []string  `json:"relaynodes" yaml:"relayedNodes"`
+	IngressDNS          string    `json:"ingressdns" yaml:"ingressdns"`
+	DNSOn               bool      `json:"dnson" yaml:"dnson"`
 }
 }
 
 
 // Node - a model of a network node
 // Node - a model of a network node

+ 60 - 11
models/structs.go

@@ -64,11 +64,13 @@ type IngressGwUsers struct {
 
 
 // UserRemoteGws - struct to hold user's remote gws
 // UserRemoteGws - struct to hold user's remote gws
 type UserRemoteGws struct {
 type UserRemoteGws struct {
-	GwID      string    `json:"remote_access_gw_id"`
-	GWName    string    `json:"gw_name"`
-	Network   string    `json:"network"`
-	Connected bool      `json:"connected"`
-	GwClient  ExtClient `json:"gw_client"`
+	GwID              string    `json:"remote_access_gw_id"`
+	GWName            string    `json:"gw_name"`
+	Network           string    `json:"network"`
+	Connected         bool      `json:"connected"`
+	IsInternetGateway bool      `json:"is_internet_gateway"`
+	GwClient          ExtClient `json:"gw_client"`
+	GwPeerPublicKey   string    `json:"gw_peer_public_key"`
 }
 }
 
 
 // UserRemoteGwsReq - struct to hold user remote acccess gws req
 // UserRemoteGwsReq - struct to hold user remote acccess gws req
@@ -189,8 +191,8 @@ type HostRelayRequest struct {
 
 
 // IngressRequest - ingress request struct
 // IngressRequest - ingress request struct
 type IngressRequest struct {
 type IngressRequest struct {
-	ExtclientDNS string `json:"extclientdns"`
-	Failover     bool   `json:"failover"`
+	ExtclientDNS      string `json:"extclientdns"`
+	IsInternetGateway bool   `json:"is_internet_gw"`
 }
 }
 
 
 // ServerUpdateData - contains data to configure server
 // ServerUpdateData - contains data to configure server
@@ -264,11 +266,7 @@ type ServerConfig struct {
 	Server      string `yaml:"server"`
 	Server      string `yaml:"server"`
 	Broker      string `yaml:"broker"`
 	Broker      string `yaml:"broker"`
 	IsPro       bool   `yaml:"isee" json:"Is_EE"`
 	IsPro       bool   `yaml:"isee" json:"Is_EE"`
-	StunPort    int    `yaml:"stun_port"`
 	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
@@ -308,3 +306,54 @@ type LicenseLimits struct {
 	Clients  int `json:"clients"`
 	Clients  int `json:"clients"`
 	Networks int `json:"networks"`
 	Networks int `json:"networks"`
 }
 }
+
+type SignInReqDto struct {
+	FormFields FormFields `json:"formFields"`
+}
+
+type FormField struct {
+	Id    string `json:"id"`
+	Value any    `json:"value"`
+}
+
+type FormFields []FormField
+
+type SignInResDto struct {
+	Status string `json:"status"`
+	User   User   `json:"user"`
+}
+
+type TenantLoginResDto struct {
+	Code     int    `json:"code"`
+	Message  string `json:"message"`
+	Response struct {
+		UserName  string `json:"UserName"`
+		AuthToken string `json:"AuthToken"`
+	} `json:"response"`
+}
+
+type SsoLoginReqDto struct {
+	OauthProvider string `json:"oauthprovider"`
+}
+
+type SsoLoginResDto struct {
+	User      string `json:"UserName"`
+	AuthToken string `json:"AuthToken"`
+}
+
+type SsoLoginData struct {
+	Expiration     time.Time `json:"expiration"`
+	OauthProvider  string    `json:"oauthprovider,omitempty"`
+	OauthCode      string    `json:"oauthcode,omitempty"`
+	Username       string    `json:"username,omitempty"`
+	AmbAccessToken string    `json:"ambaccesstoken,omitempty"`
+}
+
+type LoginReqDto struct {
+	Email    string `json:"email"`
+	TenantID string `json:"tenant_id"`
+}
+
+const (
+	ResHeaderKeyStAccessToken = "St-Access-Token"
+)

+ 26 - 8
mq/handlers.go

@@ -7,6 +7,7 @@ import (
 	mqtt "github.com/eclipse/paho.mqtt.golang"
 	mqtt "github.com/eclipse/paho.mqtt.golang"
 	"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/logic"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic/hostactions"
 	"github.com/gravitl/netmaker/logic/hostactions"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
@@ -193,14 +194,8 @@ 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 {
-				slog.Error("failed to register host with turn server", "id", currentHost.ID, "error", err)
-				return
-			}
-		}
+	case models.SignalHost:
+		signalPeer(hostUpdate.Signal)
 
 
 	}
 	}
 
 
@@ -212,6 +207,29 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
 	}
 	}
 }
 }
 
 
+func signalPeer(signal models.Signal) {
+
+	if signal.ToHostPubKey == "" {
+		msg := "insufficient data to signal peer"
+		logger.Log(0, msg)
+		return
+	}
+	signal.IsPro = servercfg.IsPro
+	peerHost, err := logic.GetHost(signal.ToHostID)
+	if err != nil {
+		slog.Error("failed to signal, peer not found", "error", err)
+		return
+	}
+	err = HostUpdate(&models.HostUpdate{
+		Action: models.SignalHost,
+		Host:   *peerHost,
+		Signal: signal,
+	})
+	if err != nil {
+		slog.Error("failed to publish signal to peer", "error", err)
+	}
+}
+
 // ClientPeerUpdate  message handler -- handles updating peers after signal from client nodes
 // ClientPeerUpdate  message handler -- handles updating peers after signal from client nodes
 func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
 func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
 	id, err := GetID(msg.Topic())
 	id, err := GetID(msg.Topic())

+ 13 - 9
pro/controllers/users.go

@@ -195,13 +195,15 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
 
 
 			if _, ok := user.RemoteGwIDs[node.ID.String()]; ok {
 			if _, ok := user.RemoteGwIDs[node.ID.String()]; ok {
 				gws := userGws[node.Network]
 				gws := userGws[node.Network]
-
+				extClient.AllowedIPs = logic.GetExtclientAllowedIPs(extClient)
 				gws = append(gws, models.UserRemoteGws{
 				gws = append(gws, models.UserRemoteGws{
-					GwID:      node.ID.String(),
-					GWName:    host.Name,
-					Network:   node.Network,
-					GwClient:  extClient,
-					Connected: true,
+					GwID:              node.ID.String(),
+					GWName:            host.Name,
+					Network:           node.Network,
+					GwClient:          extClient,
+					Connected:         true,
+					IsInternetGateway: node.IsInternetGateway,
+					GwPeerPublicKey:   host.PublicKey.String(),
 				})
 				})
 				userGws[node.Network] = gws
 				userGws[node.Network] = gws
 				delete(user.RemoteGwIDs, node.ID.String())
 				delete(user.RemoteGwIDs, node.ID.String())
@@ -230,9 +232,11 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
 		gws := userGws[node.Network]
 		gws := userGws[node.Network]
 
 
 		gws = append(gws, models.UserRemoteGws{
 		gws = append(gws, models.UserRemoteGws{
-			GwID:    node.ID.String(),
-			GWName:  host.Name,
-			Network: node.Network,
+			GwID:              node.ID.String(),
+			GWName:            host.Name,
+			Network:           node.Network,
+			IsInternetGateway: node.IsInternetGateway,
+			GwPeerPublicKey:   host.PublicKey.String(),
 		})
 		})
 		userGws[node.Network] = gws
 		userGws[node.Network] = gws
 	}
 	}

+ 2 - 0
pro/initialize.go

@@ -61,6 +61,8 @@ func InitPro() {
 	logic.UpdateRelayed = proLogic.UpdateRelayed
 	logic.UpdateRelayed = proLogic.UpdateRelayed
 	logic.SetRelayedNodes = proLogic.SetRelayedNodes
 	logic.SetRelayedNodes = proLogic.SetRelayedNodes
 	logic.RelayUpdates = proLogic.RelayUpdates
 	logic.RelayUpdates = proLogic.RelayUpdates
+	logic.IsInternetGw = proLogic.IsInternetGw
+	logic.SetInternetGw = proLogic.SetInternetGw
 	mq.UpdateMetrics = proLogic.MQUpdateMetrics
 	mq.UpdateMetrics = proLogic.MQUpdateMetrics
 }
 }
 
 

+ 10 - 0
pro/logic/nodes.go

@@ -5,6 +5,16 @@ import (
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 )
 )
 
 
+// IsInternetGw - checks if node is acting as internet gw
+func IsInternetGw(node models.Node) bool {
+	return node.IsInternetGateway
+}
+
+// SetInternetGw - sets the node as internet gw based on flag bool
+func SetInternetGw(node *models.Node, flag bool) {
+	node.IsInternetGateway = flag
+}
+
 // GetNetworkIngresses - gets the gateways of a network
 // GetNetworkIngresses - gets the gateways of a network
 func GetNetworkIngresses(network string) ([]models.Node, error) {
 func GetNetworkIngresses(network string) ([]models.Node, error) {
 	var ingresses []models.Node
 	var ingresses []models.Node

+ 1 - 1
release.md

@@ -1,5 +1,5 @@
 
 
-# Netmaker v0.21.3
+# Netmaker v0.22.0
 
 
 ## Whats New
 ## Whats New
 - Auto Relay via Enrollment key
 - Auto Relay via Enrollment key

+ 0 - 12
scripts/netmaker.default.env

@@ -6,10 +6,6 @@ NM_DOMAIN=
 SERVER_HOST=
 SERVER_HOST=
 # The admin master key for accessing the API. Change this in any production installation.
 # The admin master key for accessing the API. Change this in any production installation.
 MASTER_KEY=
 MASTER_KEY=
-# The username to set for turn api access
-TURN_USERNAME=
-# The password to set for turn api access
-TURN_PASSWORD=
 # The username to set for MQ access
 # The username to set for MQ access
 MQ_USERNAME=
 MQ_USERNAME=
 # The password to set for MQ access
 # The password to set for MQ access
@@ -42,18 +38,10 @@ DATABASE=sqlite
 # If using "host networking", it will find and detect the IP of the mq container.
 # If using "host networking", it will find and detect the IP of the mq container.
 # For EMQX websockets use `SERVER_BROKER_ENDPOINT=ws://mq:8083/mqtt`
 # For EMQX websockets use `SERVER_BROKER_ENDPOINT=ws://mq:8083/mqtt`
 SERVER_BROKER_ENDPOINT=ws://mq:1883 
 SERVER_BROKER_ENDPOINT=ws://mq:1883 
-# The reachable port of STUN on the server
-STUN_PORT=3478
 # Logging verbosity level - 1, 2, or 3
 # Logging verbosity level - 1, 2, or 3
 VERBOSITY=1
 VERBOSITY=1
-# Port to access turn server
-TURN_PORT=3479
-# Config for using turn, accepts either true/false
-USE_TURN=true
 DEBUG_MODE=off
 DEBUG_MODE=off
-TURN_API_PORT=8089
 # Enables the REST backend (API running on API_PORT at SERVER_HTTP_HOST).
 # Enables the REST backend (API running on API_PORT at SERVER_HTTP_HOST).
-# Change to "off" to turn off.
 REST_BACKEND=on
 REST_BACKEND=on
 # If turned "on", Server will not set Host based on remote IP check.
 # If turned "on", Server will not set Host based on remote IP check.
 # This is already overridden if SERVER_HOST is set. Turned "off" by default.
 # This is already overridden if SERVER_HOST is set. Turned "off" by default.

+ 3 - 55
scripts/nm-quick.sh

@@ -305,11 +305,10 @@ save_config() { (
 		save_config_item SERVER_IMAGE_TAG "$IMAGE_TAG"
 		save_config_item SERVER_IMAGE_TAG "$IMAGE_TAG"
 	fi
 	fi
 	# copy entries from the previous config
 	# copy entries from the previous config
-	local toCopy=("SERVER_HOST" "MASTER_KEY" "TURN_USERNAME" "TURN_PASSWORD" "MQ_USERNAME" "MQ_PASSWORD"
+	local toCopy=("SERVER_HOST" "MASTER_KEY" "MQ_USERNAME" "MQ_PASSWORD"
 		"INSTALL_TYPE" "NODE_ID" "DNS_MODE" "NETCLIENT_AUTO_UPDATE" "API_PORT"
 		"INSTALL_TYPE" "NODE_ID" "DNS_MODE" "NETCLIENT_AUTO_UPDATE" "API_PORT"
-		"CORS_ALLOWED_ORIGIN" "DISPLAY_KEYS" "DATABASE" "SERVER_BROKER_ENDPOINT" "STUN_PORT" "VERBOSITY"
-		"TURN_PORT" "USE_TURN" "DEBUG_MODE" "TURN_API_PORT" "REST_BACKEND"
-		"DISABLE_REMOTE_IP_CHECK" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET"
+		"CORS_ALLOWED_ORIGIN" "DISPLAY_KEYS" "DATABASE" "SERVER_BROKER_ENDPOINT" "VERBOSITY"
+		"DEBUG_MODE"  "REST_BACKEND" "DISABLE_REMOTE_IP_CHECK" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET"
 		"FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT" "JWT_VALIDITY_DURATION" "RAC_AUTO_DISABLE")
 		"FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT" "JWT_VALIDITY_DURATION" "RAC_AUTO_DISABLE")
 	for name in "${toCopy[@]}"; do
 	for name in "${toCopy[@]}"; do
 		save_config_item $name "${!name}"
 		save_config_item $name "${!name}"
@@ -550,8 +549,6 @@ set_install_vars() {
 	echo "          dashboard.$NETMAKER_BASE_DOMAIN"
 	echo "          dashboard.$NETMAKER_BASE_DOMAIN"
 	echo "                api.$NETMAKER_BASE_DOMAIN"
 	echo "                api.$NETMAKER_BASE_DOMAIN"
 	echo "             broker.$NETMAKER_BASE_DOMAIN"
 	echo "             broker.$NETMAKER_BASE_DOMAIN"
-	echo "               turn.$NETMAKER_BASE_DOMAIN"
-	echo "            turnapi.$NETMAKER_BASE_DOMAIN"
 
 
 	if [ "$INSTALL_TYPE" = "pro" ]; then
 	if [ "$INSTALL_TYPE" = "pro" ]; then
 		echo "         prometheus.$NETMAKER_BASE_DOMAIN"
 		echo "         prometheus.$NETMAKER_BASE_DOMAIN"
@@ -657,55 +654,6 @@ 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 TURN"
-		TURN_USERNAME="netmaker"
-	else
-		TURN_USERNAME="$GET_TURN_USERNAME"
-	fi
-
-	if test -z "$TURN_PASSWORD"; then
-		TURN_PASSWORD=$(
-			tr -dc A-Za-z0-9 </dev/urandom | head -c 30
-			echo ''
-		)
-	fi
-
-	if [ -z $AUTO_BUILD ]; then
-		select domain_option in "Auto Generated / Config 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 "-----------------------------------------------------------------"

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

@@ -1,6 +1,6 @@
 #!/bin/bash
 #!/bin/bash
 
 
-LATEST="v0.21.3"
+LATEST="v0.21.0"
 INSTALL_PATH="/root"
 INSTALL_PATH="/root"
 
 
 trap restore_old_netmaker_instructions
 trap restore_old_netmaker_instructions

+ 0 - 2
scripts/nm-upgrade.sh

@@ -356,8 +356,6 @@ set_install_vars() {
 	echo "          dashboard.$NETMAKER_BASE_DOMAIN"
 	echo "          dashboard.$NETMAKER_BASE_DOMAIN"
 	echo "                api.$NETMAKER_BASE_DOMAIN"
 	echo "                api.$NETMAKER_BASE_DOMAIN"
 	echo "             broker.$NETMAKER_BASE_DOMAIN"
 	echo "             broker.$NETMAKER_BASE_DOMAIN"
-	echo "               turn.$NETMAKER_BASE_DOMAIN"
-	echo "            turnapi.$NETMAKER_BASE_DOMAIN"
 
 
 	if [ "$INSTALL_TYPE" = "pro" ]; then
 	if [ "$INSTALL_TYPE" = "pro" ]; then
 		echo "         prometheus.$NETMAKER_BASE_DOMAIN"
 		echo "         prometheus.$NETMAKER_BASE_DOMAIN"

+ 0 - 89
servercfg/serverconf.go

@@ -45,7 +45,6 @@ func GetServerConfig() config.ServerConfig {
 	cfg.AllowedOrigin = GetAllowedOrigin()
 	cfg.AllowedOrigin = GetAllowedOrigin()
 	cfg.RestBackend = "off"
 	cfg.RestBackend = "off"
 	cfg.NodeID = GetNodeID()
 	cfg.NodeID = GetNodeID()
-	cfg.StunPort = GetStunPort()
 	cfg.BrokerType = GetBrokerType()
 	cfg.BrokerType = GetBrokerType()
 	cfg.EmqxRestEndpoint = GetEmqxRestEndpoint()
 	cfg.EmqxRestEndpoint = GetEmqxRestEndpoint()
 	if AutoUpdateEnabled() {
 	if AutoUpdateEnabled() {
@@ -125,45 +124,9 @@ func GetServerInfo() models.ServerConfig {
 	}
 	}
 	cfg.Version = GetVersion()
 	cfg.Version = GetVersion()
 	cfg.IsPro = IsPro
 	cfg.IsPro = IsPro
-	cfg.StunPort = GetStunPort()
-	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 = ""
@@ -646,58 +609,6 @@ func GetNetmakerTenantID() string {
 	return netmakerTenantID
 	return netmakerTenantID
 }
 }
 
 
-// GetStunPort - Get the port to run the stun server on
-func GetStunPort() int {
-	port := 3478 //default
-	if os.Getenv("STUN_PORT") != "" {
-		portInt, err := strconv.Atoi(os.Getenv("STUN_PORT"))
-		if err == nil {
-			port = portInt
-		}
-	} else if config.Config.Server.StunPort != 0 {
-		port = config.Config.Server.StunPort
-	}
-	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
-
-}
-
 // GetNetworkLimit - fetches free tier limits on users
 // GetNetworkLimit - fetches free tier limits on users
 func GetUserLimit() int {
 func GetUserLimit() int {
 	var userslimit int
 	var userslimit int

+ 1 - 1
swagger.yml

@@ -1149,7 +1149,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.21.3
+    version: 0.22.0
 paths:
 paths:
     /api/dns:
     /api/dns:
         get:
         get: