Browse Source

Merge branch 'develop' into patch-1

Alex 3 years ago
parent
commit
3fc2a729ad
44 changed files with 877 additions and 257 deletions
  1. 11 0
      .github/workflows/buildandrelease.yml
  2. 4 7
      .github/workflows/publish-docker.yml
  3. 1 1
      auth/auth.go
  4. 3 3
      auth/azure-ad.go
  5. 3 3
      auth/github.go
  6. 3 3
      auth/google.go
  7. 0 4
      compose/docker-compose.caddy.yml
  8. 2 0
      config/config.go
  9. 48 8
      controllers/dnsHttpController.go
  10. 21 3
      controllers/networkHttpController.go
  11. 19 4
      controllers/nodeGrpcController.go
  12. 1 1
      controllers/relay.go
  13. 1 0
      go.mod
  14. 10 0
      logic/accesskeys.go
  15. 15 0
      logic/networks.go
  16. 1 1
      logic/server.go
  17. 36 27
      logic/wireguard.go
  18. 3 1
      models/node.go
  19. 3 0
      models/structs.go
  20. 10 6
      netclient/config/config.go
  21. 3 2
      netclient/daemon/macos.go
  22. 1 1
      netclient/daemon/systemd.go
  23. 1 1
      netclient/functions/checkin.go
  24. 13 0
      netclient/functions/common.go
  25. 38 2
      netclient/functions/join.go
  26. 23 2
      netclient/local/local.go
  27. 1 1
      netclient/main.go
  28. 62 11
      netclient/ncutils/netclientutils.go
  29. 46 0
      netclient/ncutils/netclientutils_freebsd.go
  30. 95 0
      netclient/ncutils/peerhelper.go
  31. 3 0
      netclient/ncwindows/windows.go
  32. 1 1
      netclient/netclient.exe.manifest.xml
  33. 6 6
      netclient/versioninfo.json
  34. BIN
      netclient/windowsdata/resource/netclient.syso
  35. 64 29
      netclient/wireguard/common.go
  36. 32 6
      netclient/wireguard/unix.go
  37. 62 0
      scripts/netclient-install.ps1
  38. 104 13
      scripts/netclient-install.sh
  39. 17 0
      scripts/netclient-rc-freebsd
  40. 0 109
      scripts/netclient.sh
  41. 1 1
      scripts/nm-quick.sh
  42. 39 0
      scripts/openwrt-daemon-2.sh
  43. 39 0
      scripts/openwrt-daemon.sh
  44. 31 0
      servercfg/serverconf.go

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

@@ -39,6 +39,7 @@ jobs:
           env GOOS=linux GOARCH=arm GOARM=6 go build -o build/netclient-arm6/netclient main.go
           env GOOS=linux GOARCH=arm GOARM=7 go build -o build/netclient-arm7/netclient main.go
           env GOOS=linux GOARCH=arm64 go build -o build/netclient-arm64/netclient main.go
+          env GOOS=linux GOARCH=mipsle go build -ldflags "-s -w" -o build/netclient-mipsle/netclient main.go && upx build/netclient-mipsle/netclient
 
       - name: Upload x86 to Release
         uses: svenstaro/upload-release-action@v2
@@ -89,3 +90,13 @@ jobs:
           overwrite: true
           prerelease: true
           asset_name: netclient-arm64
+
+      - name: Upload mipsle to Release
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: netclient/build/netclient-mipsle/netclient
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient-mipsle

+ 4 - 7
.github/workflows/publish-docker.yml

@@ -6,10 +6,8 @@ on:
       tag:
         description: 'docker tag'
         required: true
-  pull_request:
-    branches:
-      - 'test'
-      - 'master'
+  release:
+    types: [published]
 
 jobs:
   docker:
@@ -31,8 +29,7 @@ jobs:
         uses: docker/setup-qemu-action@v1
       - name: Set up Docker Buildx
         uses: docker/setup-buildx-action@v1
-      - if: github.event_name != 'pull_request'
-        name: Login to DockerHub
+      - name: Login to DockerHub
         uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_USERNAME }}
@@ -42,5 +39,5 @@ jobs:
         with:
           context: .
           platforms: linux/amd64, linux/arm64
-          push: ${{ github.event_name != 'pull_request' }}
+          push: true
           tags: gravitl/netmaker:${{ env.TAG }}

+ 1 - 1
auth/auth.go

@@ -93,7 +93,7 @@ func HandleAuthLogin(w http.ResponseWriter, r *http.Request) {
 	if auth_provider == nil {
 		var referer = r.Header.Get("referer")
 		if referer != "" {
-			http.Redirect(w, r, referer+"?oauth=callback-error", http.StatusTemporaryRedirect)
+			http.Redirect(w, r, referer+"login?oauth=callback-error", http.StatusTemporaryRedirect)
 			return
 		}
 		w.Header().Set("Content-Type", "text/html; charset=utf-8")

+ 3 - 3
auth/azure-ad.go

@@ -42,7 +42,7 @@ func initAzureAD(redirectURL string, clientID string, clientSecret string) {
 func handleAzureLogin(w http.ResponseWriter, r *http.Request) {
 	oauth_state_string = logic.RandomString(16)
 	if auth_provider == nil && servercfg.GetFrontendURL() != "" {
-		http.Redirect(w, r, servercfg.GetFrontendURL()+"?oauth=callback-error", http.StatusTemporaryRedirect)
+		http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
 		return
 	} else if auth_provider == nil {
 		fmt.Fprintf(w, "%s", []byte("no frontend URL was provided and an OAuth login was attempted\nplease reconfigure server to use OAuth or use basic credentials"))
@@ -57,7 +57,7 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) {
 	var content, err = getAzureUserInfo(r.FormValue("state"), r.FormValue("code"))
 	if err != nil {
 		logic.Log("error when getting user info from azure: "+err.Error(), 1)
-		http.Redirect(w, r, servercfg.GetFrontendURL()+"?oauth=callback-error", http.StatusTemporaryRedirect)
+		http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
 		return
 	}
 	_, err = logic.GetUser(content.UserPrincipalName)
@@ -83,7 +83,7 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) {
 	}
 
 	logic.Log("completed azure OAuth sigin in for "+content.UserPrincipalName, 1)
-	http.Redirect(w, r, servercfg.GetFrontendURL()+"?login="+jwt+"&user="+content.UserPrincipalName, http.StatusPermanentRedirect)
+	http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?login="+jwt+"&user="+content.UserPrincipalName, http.StatusPermanentRedirect)
 }
 
 func getAzureUserInfo(state string, code string) (*azureOauthUser, error) {

+ 3 - 3
auth/github.go

@@ -41,7 +41,7 @@ func initGithub(redirectURL string, clientID string, clientSecret string) {
 func handleGithubLogin(w http.ResponseWriter, r *http.Request) {
 	oauth_state_string = logic.RandomString(16)
 	if auth_provider == nil && servercfg.GetFrontendURL() != "" {
-		http.Redirect(w, r, servercfg.GetFrontendURL()+"?error=callback-error", http.StatusTemporaryRedirect)
+		http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
 		return
 	} else if auth_provider == nil {
 		fmt.Fprintf(w, "%s", []byte("no frontend URL was provided and an OAuth login was attempted\nplease reconfigure server to use OAuth or use basic credentials"))
@@ -56,7 +56,7 @@ func handleGithubCallback(w http.ResponseWriter, r *http.Request) {
 	var content, err = getGithubUserInfo(r.URL.Query().Get("state"), r.URL.Query().Get("code"))
 	if err != nil {
 		logic.Log("error when getting user info from github: "+err.Error(), 1)
-		http.Redirect(w, r, servercfg.GetFrontendURL()+"?oauth=callback-error", http.StatusTemporaryRedirect)
+		http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
 		return
 	}
 	_, err = logic.GetUser(content.Login)
@@ -82,7 +82,7 @@ func handleGithubCallback(w http.ResponseWriter, r *http.Request) {
 	}
 
 	logic.Log("completed github OAuth sigin in for "+content.Login, 1)
-	http.Redirect(w, r, servercfg.GetFrontendURL()+"?login="+jwt+"&user="+content.Login, http.StatusPermanentRedirect)
+	http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?login="+jwt+"&user="+content.Login, http.StatusPermanentRedirect)
 }
 
 func getGithubUserInfo(state string, code string) (*githubOauthUser, error) {

+ 3 - 3
auth/google.go

@@ -41,7 +41,7 @@ func initGoogle(redirectURL string, clientID string, clientSecret string) {
 func handleGoogleLogin(w http.ResponseWriter, r *http.Request) {
 	oauth_state_string = logic.RandomString(16)
 	if auth_provider == nil && servercfg.GetFrontendURL() != "" {
-		http.Redirect(w, r, servercfg.GetFrontendURL()+"?oauth=callback-error", http.StatusTemporaryRedirect)
+		http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
 		return
 	} else if auth_provider == nil {
 		fmt.Fprintf(w, "%s", []byte("no frontend URL was provided and an OAuth login was attempted\nplease reconfigure server to use OAuth or use basic credentials"))
@@ -56,7 +56,7 @@ func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
 	var content, err = getGoogleUserInfo(r.FormValue("state"), r.FormValue("code"))
 	if err != nil {
 		logic.Log("error when getting user info from google: "+err.Error(), 1)
-		http.Redirect(w, r, servercfg.GetFrontendURL()+"?oauth=callback-error", http.StatusTemporaryRedirect)
+		http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
 		return
 	}
 	_, err = logic.GetUser(content.Email)
@@ -82,7 +82,7 @@ func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
 	}
 
 	logic.Log("completed google OAuth sigin in for "+content.Email, 1)
-	http.Redirect(w, r, servercfg.GetFrontendURL()+"?login="+jwt+"&user="+content.Email, http.StatusPermanentRedirect)
+	http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?login="+jwt+"&user="+content.Email, http.StatusPermanentRedirect)
 }
 
 func getGoogleUserInfo(state string, code string) (*googleOauthUser, error) {

+ 0 - 4
compose/docker-compose.caddy.yml

@@ -35,10 +35,6 @@ services:
       CORS_ALLOWED_ORIGIN: "*"
       DATABASE: "sqlite"
       NODE_ID: "netmaker-server-1"
-    ports:
-      - "51821-51830:51821-51830/udp"
-      - "8081:8081"
-      - "50051:50051"
   netmaker-ui:
     container_name: netmaker-ui
     depends_on:

+ 2 - 0
config/config.go

@@ -44,6 +44,7 @@ type ServerConfig struct {
 	GRPCPort              string `yaml:"grpcport"`
 	GRPCSecure            string `yaml:"grpcsecure"`
 	MasterKey             string `yaml:"masterkey"`
+	DNSKey                string `yaml:"dnskey"`
 	AllowedOrigin         string `yaml:"allowedorigin"`
 	NodeID                string `yaml:"nodeid"`
 	RestBackend           string `yaml:"restbackend"`
@@ -66,6 +67,7 @@ type ServerConfig struct {
 	ClientID              string `yaml:"clientid"`
 	ClientSecret          string `yaml:"clientsecret"`
 	FrontendURL           string `yaml:"frontendurl"`
+	DisplayKeys           string `yaml:"displaykeys"`
 }
 
 // Generic SQL Config

+ 48 - 8
controllers/dnsHttpController.go

@@ -3,6 +3,7 @@ package controller
 import (
 	"encoding/json"
 	"net/http"
+	"strings"
 
 	"github.com/go-playground/validator/v10"
 	"github.com/gorilla/mux"
@@ -14,14 +15,14 @@ import (
 
 func dnsHandlers(r *mux.Router) {
 
-	r.HandleFunc("/api/dns", securityCheck(true, http.HandlerFunc(getAllDNS))).Methods("GET")
-	r.HandleFunc("/api/dns/adm/{network}/nodes", securityCheck(false, http.HandlerFunc(getNodeDNS))).Methods("GET")
-	r.HandleFunc("/api/dns/adm/{network}/custom", securityCheck(false, http.HandlerFunc(getCustomDNS))).Methods("GET")
-	r.HandleFunc("/api/dns/adm/{network}", securityCheck(false, http.HandlerFunc(getDNS))).Methods("GET")
-	r.HandleFunc("/api/dns/{network}", securityCheck(false, http.HandlerFunc(createDNS))).Methods("POST")
-	r.HandleFunc("/api/dns/adm/pushdns", securityCheck(false, http.HandlerFunc(pushDNS))).Methods("POST")
-	r.HandleFunc("/api/dns/{network}/{domain}", securityCheck(false, http.HandlerFunc(deleteDNS))).Methods("DELETE")
-	r.HandleFunc("/api/dns/{network}/{domain}", securityCheck(false, http.HandlerFunc(updateDNS))).Methods("PUT")
+	r.HandleFunc("/api/dns", securityCheckDNS(true, true, http.HandlerFunc(getAllDNS))).Methods("GET")
+	r.HandleFunc("/api/dns/adm/{network}/nodes", securityCheckDNS(false, true, http.HandlerFunc(getNodeDNS))).Methods("GET")
+	r.HandleFunc("/api/dns/adm/{network}/custom", securityCheckDNS(false, true, http.HandlerFunc(getCustomDNS))).Methods("GET")
+	r.HandleFunc("/api/dns/adm/{network}", securityCheckDNS(false, true, http.HandlerFunc(getDNS))).Methods("GET")
+	r.HandleFunc("/api/dns/{network}", securityCheckDNS(false, false, http.HandlerFunc(createDNS))).Methods("POST")
+	r.HandleFunc("/api/dns/adm/pushdns", securityCheckDNS(false, false, http.HandlerFunc(pushDNS))).Methods("POST")
+	r.HandleFunc("/api/dns/{network}/{domain}", securityCheckDNS(false, false, http.HandlerFunc(deleteDNS))).Methods("DELETE")
+	r.HandleFunc("/api/dns/{network}/{domain}", securityCheckDNS(false, false, http.HandlerFunc(updateDNS))).Methods("PUT")
 }
 
 //Gets all nodes associated with network, including pending nodes
@@ -408,3 +409,42 @@ func ValidateDNSUpdate(change models.DNSEntry, entry models.DNSEntry) error {
 	}
 	return err
 }
+
+//Security check DNS is middleware for every DNS function and just checks to make sure that its the master or dns token calling
+//Only admin should have access to all these network-level actions
+//DNS token should have access to only read functions
+func securityCheckDNS(reqAdmin bool, allowDNSToken bool, next http.Handler) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var errorResponse = models.ErrorResponse{
+			Code: http.StatusUnauthorized, Message: "W1R3: It's not you it's me.",
+		}
+
+		var params = mux.Vars(r)
+		bearerToken := r.Header.Get("Authorization")
+		if allowDNSToken && authenticateDNSToken(bearerToken) {
+			r.Header.Set("user", "nameserver")
+			networks, _ := json.Marshal([]string{ALL_NETWORK_ACCESS})
+			r.Header.Set("networks", string(networks))
+			next.ServeHTTP(w, r)
+		} else {
+			err, networks, username := SecurityCheck(reqAdmin, params["networkname"], bearerToken)
+			if err != nil {
+				if strings.Contains(err.Error(), "does not exist") {
+					errorResponse.Code = http.StatusNotFound
+				}
+				errorResponse.Message = err.Error()
+				returnErrorResponse(w, r, errorResponse)
+				return
+			}
+			networksJson, err := json.Marshal(&networks)
+			if err != nil {
+				errorResponse.Message = err.Error()
+				returnErrorResponse(w, r, errorResponse)
+				return
+			}
+			r.Header.Set("user", username)
+			r.Header.Set("networks", string(networksJson))
+			next.ServeHTTP(w, r)
+		}
+	}
+}

+ 21 - 3
controllers/networkHttpController.go

@@ -114,10 +114,16 @@ func SecurityCheck(reqAdmin bool, netname string, token string) (error, []string
 
 //Consider a more secure way of setting master key
 func authenticateMaster(tokenString string) bool {
-	if tokenString == servercfg.GetMasterKey() {
-		return true
+	return tokenString == servercfg.GetMasterKey()
+}
+
+//Consider a more secure way of setting master key
+func authenticateDNSToken(tokenString string) bool {
+	tokens := strings.Split(tokenString, " ")
+	if len(tokens) < 2 {
+		return false
 	}
-	return false
+	return tokens[1] == servercfg.GetDNSKey()
 }
 
 //simple get all networks function
@@ -146,6 +152,12 @@ func getNetworks(w http.ResponseWriter, r *http.Request) {
 			}
 		}
 	}
+	if !servercfg.IsDisplayKeys() {
+		for i, net := range allnetworks {
+			net.AccessKeys = logic.RemoveKeySensitiveInfo(net.AccessKeys)
+			allnetworks[i] = net
+		}
+	}
 	functions.PrintUserLog(r.Header.Get("user"), "fetched networks.", 2)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(allnetworks)
@@ -183,6 +195,9 @@ func getNetwork(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+	if !servercfg.IsDisplayKeys() {
+		network.AccessKeys = logic.RemoveKeySensitiveInfo(network.AccessKeys)
+	}
 	functions.PrintUserLog(r.Header.Get("user"), "fetched network "+netname, 2)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(network)
@@ -572,6 +587,9 @@ func getAccessKeys(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
+	if !servercfg.IsDisplayKeys() {
+		keys = logic.RemoveKeySensitiveInfo(keys)
+	}
 	functions.PrintUserLog(r.Header.Get("user"), "fetched access keys on network "+network, 2)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(keys)

+ 19 - 4
controllers/nodeGrpcController.go

@@ -29,10 +29,14 @@ func (s *NodeServiceServer) ReadNode(ctx context.Context, req *nodepb.Object) (*
 	if err != nil {
 		return nil, err
 	}
+	node.NetworkSettings, err = logic.GetNetworkSettings(node.Network)
+	if err != nil {
+		return nil, err
+	}
 	node.SetLastCheckIn()
 	// Cast to ReadNodeRes type
-	nodeData, err := json.Marshal(&node)
-	if err != nil {
+	nodeData, errN := json.Marshal(&node)
+	if errN != nil {
 		return nil, err
 	}
 	logic.UpdateNode(&node, &node)
@@ -75,7 +79,14 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.Object)
 	if err != nil {
 		return nil, err
 	}
-	nodeData, err := json.Marshal(&node)
+	node.NetworkSettings, err = logic.GetNetworkSettings(node.Network)
+	if err != nil {
+		return nil, err
+	}
+	nodeData, errN := json.Marshal(&node)
+	if errN != nil {
+		return nil, err
+	}
 	// return the node in a CreateNodeRes type
 	response := &nodepb.Object{
 		Data: string(nodeData),
@@ -107,10 +118,14 @@ func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.Object)
 	if err != nil {
 		return nil, err
 	}
-	nodeData, err := json.Marshal(&newnode)
+	newnode.NetworkSettings, err = logic.GetNetworkSettings(node.Network)
 	if err != nil {
 		return nil, err
 	}
+	nodeData, errN := json.Marshal(&newnode)
+	if errN != nil {
+		return nil, err
+	}
 	return &nodepb.Object{
 		Data: string(nodeData),
 		Type: nodepb.NODE_TYPE,

+ 1 - 1
controllers/relay.go

@@ -37,7 +37,7 @@ func createRelay(w http.ResponseWriter, r *http.Request) {
 // CreateRelay - creates a relay
 func CreateRelay(relay models.RelayRequest) (models.Node, error) {
 	node, err := logic.GetNodeByMacAddress(relay.NetID, relay.NodeID)
-	if node.OS == "windows" || node.OS == "macos" { // add in darwin later
+	if node.OS == "macos" { // add in darwin later
 		return models.Node{}, errors.New(node.OS + " is unsupported for relay")
 	}
 	if err != nil {

+ 1 - 0
go.mod

@@ -3,6 +3,7 @@ module github.com/gravitl/netmaker
 go 1.15
 
 require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/go-playground/validator/v10 v10.9.0
 	github.com/golang-jwt/jwt/v4 v4.1.0
 	github.com/golang/protobuf v1.5.2 // indirect

+ 10 - 0
logic/accesskeys.go

@@ -60,3 +60,13 @@ func IsKeyValid(networkname string, keyvalue string) bool {
 	}
 	return isvalid
 }
+
+func RemoveKeySensitiveInfo(keys []models.AccessKey) []models.AccessKey {
+	var returnKeys []models.AccessKey
+	for _, key := range keys {
+		key.Value = models.PLACEHOLDER_KEY_TEXT
+		key.AccessString = models.PLACEHOLDER_TOKEN_TEXT
+		returnKeys = append(returnKeys, key)
+	}
+	return returnKeys
+}

+ 15 - 0
logic/networks.go

@@ -51,6 +51,21 @@ func GetParentNetwork(networkname string) (models.Network, error) {
 	return network, nil
 }
 
+// GetParentNetwork - get parent network
+func GetNetworkSettings(networkname string) (models.Network, error) {
+
+	var network models.Network
+	networkData, err := database.FetchRecord(database.NETWORKS_TABLE_NAME, networkname)
+	if err != nil {
+		return network, err
+	}
+	if err = json.Unmarshal([]byte(networkData), &network); err != nil {
+		return models.Network{}, err
+	}
+	network.AccessKeys = []models.AccessKey{}
+	return network, nil
+}
+
 // UniqueAddress - see if address is unique
 func UniqueAddress(networkName string) (string, error) {
 

+ 1 - 1
logic/server.go

@@ -136,7 +136,7 @@ func ServerJoin(network string, serverID string, privateKey string) error {
 		return err
 	}
 
-	err = initWireguard(node, privateKey, peers, hasGateway, gateways)
+	err = initWireguard(node, privateKey, peers, hasGateway, gateways, 0)
 	if err != nil {
 		return err
 	}

+ 36 - 27
logic/wireguard.go

@@ -45,32 +45,9 @@ func RemoveConf(iface string, printlog bool) error {
 	return err
 }
 
-// == Private Methods ==
+// Private Functions
 
-func setWGConfig(node models.Node, network string, peerupdate bool) error {
-
-	node.SetID()
-	peers, hasGateway, gateways, err := GetServerPeers(node.MacAddress, node.Network, node.IsDualStack == "yes", node.IsIngressGateway == "yes")
-	if err != nil {
-		return err
-	}
-	privkey, err := FetchPrivKey(node.ID)
-	if err != nil {
-		return err
-	}
-	if peerupdate {
-		var iface string
-		iface = node.Interface
-		err = setServerPeers(iface, node.PersistentKeepalive, peers)
-		Log("updated peers on server "+node.Name, 2)
-	} else {
-		err = initWireguard(&node, privkey, peers, hasGateway, gateways)
-		Log("finished setting wg config on server "+node.Name, 3)
-	}
-	return err
-}
-
-func initWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig, hasGateway bool, gateways []string) error {
+func initWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig, hasGateway bool, gateways []string, fwmark int32) error {
 
 	key, err := wgtypes.ParseKey(privkey)
 	if err != nil {
@@ -108,7 +85,7 @@ func initWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
 
 	if !ncutils.IsKernel() {
 		var newConf string
-		newConf, _ = ncutils.CreateUserSpaceConf(node.Address, key.String(), strconv.FormatInt(int64(node.ListenPort), 10), node.MTU, node.PersistentKeepalive, peers)
+		newConf, _ = ncutils.CreateUserSpaceConf(node.Address, key.String(), strconv.FormatInt(int64(node.ListenPort), 10), node.MTU, fwmark, node.PersistentKeepalive, peers)
 		confPath := ncutils.GetNetclientPathSpecific() + ifacename + ".conf"
 		Log("writing wg conf file to: "+confPath, 1)
 		err = ioutil.WriteFile(confPath, []byte(newConf), 0644)
@@ -116,6 +93,16 @@ func initWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
 			Log("error writing wg conf file to "+confPath+": "+err.Error(), 1)
 			return err
 		}
+		if ncutils.IsWindows() {
+			wgConfPath := ncutils.GetWGPathSpecific() + ifacename + ".conf"
+			Log("writing wg conf file to: "+confPath, 1)
+			err = ioutil.WriteFile(wgConfPath, []byte(newConf), 0644)
+			if err != nil {
+				Log("error writing wg conf file to "+wgConfPath+": "+err.Error(), 1)
+				return err
+			}
+			confPath = wgConfPath
+		}
 		// spin up userspace + apply the conf file
 		var deviceiface = ifacename
 		d, _ := wgclient.Device(deviceiface)
@@ -163,7 +150,7 @@ func initWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
 
 		if node.PostDown != "" {
 			runcmds := strings.Split(node.PostDown, "; ")
-			_ = ncutils.RunCmds(runcmds, true)
+			_ = ncutils.RunCmds(runcmds, false)
 		}
 		// set MTU of node interface
 		if _, err := ncutils.RunCmd(ipExec+" link set mtu "+strconv.Itoa(int(node.MTU))+" up dev "+ifacename, true); err != nil {
@@ -290,6 +277,28 @@ func setServerPeers(iface string, keepalive int32, peers []wgtypes.PeerConfig) e
 	return nil
 }
 
+func setWGConfig(node models.Node, network string, peerupdate bool) error {
+
+	node.SetID()
+	peers, hasGateway, gateways, err := GetServerPeers(node.MacAddress, node.Network, node.IsDualStack == "yes", node.IsIngressGateway == "yes")
+	if err != nil {
+		return err
+	}
+	privkey, err := FetchPrivKey(node.ID)
+	if err != nil {
+		return err
+	}
+	if peerupdate {
+		var iface string = node.Interface
+		err = setServerPeers(iface, node.PersistentKeepalive, peers)
+		Log("updated peers on server "+node.Name, 2)
+	} else {
+		err = initWireguard(&node, privkey, peers, hasGateway, gateways, 0)
+		Log("finished setting wg config on server "+node.Name, 3)
+	}
+	return err
+}
+
 func setWGKeyConfig(node models.Node) error {
 
 	node.SetID()

+ 3 - 1
models/node.go

@@ -13,6 +13,7 @@ import (
 
 const charset = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
 const TEN_YEARS_IN_SECONDS = 300000000
+const MAX_NAME_LENGTH = 62
 
 // == ACTIONS == (can only be set by GRPC)
 const NODE_UPDATE_KEY = "updatekey"
@@ -30,7 +31,8 @@ type Node struct {
 	Address             string   `json:"address" bson:"address" yaml:"address" validate:"omitempty,ipv4"`
 	Address6            string   `json:"address6" bson:"address6" yaml:"address6" validate:"omitempty,ipv6"`
 	LocalAddress        string   `json:"localaddress" bson:"localaddress" yaml:"localaddress" validate:"omitempty,ip"`
-	Name                string   `json:"name" bson:"name" yaml:"name" validate:"omitempty,max=32,in_charset"`
+	Name                string   `json:"name" bson:"name" yaml:"name" validate:"omitempty,max=62,in_charset"`
+	NetworkSettings     Network  `json:"networksettings" bson:"networksettings" yaml:"networksettings" validate:"-"`
 	ListenPort          int32    `json:"listenport" bson:"listenport" yaml:"listenport" validate:"omitempty,numeric,min=1024,max=65535"`
 	PublicKey           string   `json:"publickey" bson:"publickey" yaml:"publickey" validate:"required,base64"`
 	Endpoint            string   `json:"endpoint" bson:"endpoint" yaml:"endpoint" validate:"required,ip"`

+ 3 - 0
models/structs.go

@@ -2,6 +2,9 @@ package models
 
 import jwt "github.com/golang-jwt/jwt/v4"
 
+const PLACEHOLDER_KEY_TEXT = "ACCESS_KEY"
+const PLACEHOLDER_TOKEN_TEXT = "ACCESS_TOKEN"
+
 // AuthParams - struct for auth params
 type AuthParams struct {
 	MacAddress string `json:"macaddress"`

+ 10 - 6
netclient/config/config.go

@@ -24,12 +24,14 @@ type GlobalConfig struct {
 
 // ClientConfig - struct for dealing with client configuration
 type ClientConfig struct {
-	Server          ServerConfig `yaml:"server"`
-	Node            models.Node  `yaml:"node"`
-	Network         string       `yaml:"network"`
-	Daemon          string       `yaml:"daemon"`
-	OperatingSystem string       `yaml:"operatingsystem"`
-	DebugJoin       bool         `yaml:"debugjoin"`
+	Server          ServerConfig   `yaml:"server"`
+	Node            models.Node    `yaml:"node"`
+	NetworkSettings models.Network `yaml:"networksettings"`
+	Network         string         `yaml:"network"`
+	Daemon          string         `yaml:"daemon"`
+	OperatingSystem string         `yaml:"operatingsystem"`
+	DebugJoin       bool           `yaml:"debugjoin"`
+	FWMark          int32          `yaml:"fwmark"`
 }
 
 // ServerConfig - struct for dealing with the server information for a netclient
@@ -191,6 +193,7 @@ func (config *ClientConfig) ReadConfig() {
 // ModConfig - overwrites the node inside client config on disk
 func ModConfig(node *models.Node) error {
 	network := node.Network
+	networksettings := node.NetworkSettings
 	if network == "" {
 		return errors.New("no network provided")
 	}
@@ -205,6 +208,7 @@ func ModConfig(node *models.Node) error {
 	}
 
 	modconfig.Node = (*node)
+	modconfig.NetworkSettings = (networksettings)
 	err = Write(&modconfig, network)
 	return err
 }

+ 3 - 2
netclient/daemon/macos.go

@@ -2,11 +2,12 @@ package daemon
 
 import (
 	"fmt"
-	"github.com/gravitl/netmaker/netclient/ncutils"
 	"io/ioutil"
 	"log"
 	"os"
 	"path/filepath"
+
+	"github.com/gravitl/netmaker/netclient/ncutils"
 )
 
 const MAC_SERVICE_NAME = "com.gravitl.netclient"
@@ -20,7 +21,7 @@ func SetupMacDaemon(interval string) error {
 	binarypath := dir + "/netclient"
 
 	if !ncutils.FileExists("/etc/netclient/netclient") {
-		_, err = ncutils.Copy(binarypath, "/etc/netclient/netclient")
+		err = ncutils.Copy(binarypath, "/etc/netclient/netclient")
 		if err != nil {
 			log.Println(err)
 			return err

+ 1 - 1
netclient/daemon/systemd.go

@@ -35,7 +35,7 @@ func SetupSystemDDaemon(interval string) error {
 		os.Symlink("/etc/netclient/netclient", "/usr/local/bin/netclient")
 	}
 	if !ncutils.FileExists("/etc/netclient/netclient") {
-		_, err = ncutils.Copy(binarypath, "/etc/netclient/netclient")
+		err = ncutils.Copy(binarypath, "/etc/netclient/netclient")
 		if err != nil {
 			log.Println(err)
 			return err

+ 1 - 1
netclient/functions/checkin.go

@@ -234,7 +234,7 @@ func Pull(network string, manual bool) (*models.Node, error) {
 		}
 	} else {
 		if err = wireguard.SetWGConfig(network, true); err != nil {
-			if errors.Is(err, os.ErrNotExist) {
+			if errors.Is(err, os.ErrNotExist) && !ncutils.IsFreeBSD() {
 				return Pull(network, true)
 			} else {
 				return nil, err

+ 13 - 0
netclient/functions/common.go

@@ -206,6 +206,16 @@ func LeaveNetwork(network string) error {
 			}
 		}
 	}
+	//extra network route setting required for freebsd and windows
+	if ncutils.IsWindows() {
+		ip, mask, err := ncutils.GetNetworkIPMask(node.NetworkSettings.AddressRange)
+		if err != nil {
+			ncutils.PrintLog(err.Error(), 1)
+		}
+		_, _ = ncutils.RunCmd("route delete "+ip+" mask "+mask+" "+node.Address, true)
+	} else if ncutils.IsFreeBSD() {
+		_, _ = ncutils.RunCmd("route del -net "+node.NetworkSettings.AddressRange+" -interface "+node.Interface, true)
+	}
 	return RemoveLocalInstance(cfg, network)
 }
 
@@ -283,6 +293,9 @@ func WipeLocal(network string) error {
 	if ncutils.FileExists(home + "netconfig-" + network) {
 		_ = os.Remove(home + "netconfig-" + network)
 	}
+	if ncutils.FileExists(home + "backup.netconfig-" + network) {
+		_ = os.Remove(home + "backup.netconfig-" + network)
+	}
 	if ncutils.FileExists(home + "nettoken-" + network) {
 		_ = os.Remove(home + "nettoken-" + network)
 	}

+ 38 - 2
netclient/functions/join.go

@@ -6,7 +6,9 @@ import (
 	"errors"
 	"fmt"
 	"log"
+	"math/rand"
 	"os/exec"
+	"time"
 
 	nodepb "github.com/gravitl/netmaker/grpc"
 	"github.com/gravitl/netmaker/models"
@@ -23,7 +25,6 @@ import (
 
 // JoinNetwork - helps a client join a network
 func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
-
 	if cfg.Node.Network == "" {
 		return errors.New("no network provided")
 	}
@@ -34,6 +35,13 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
 			err := errors.New("ALREADY_INSTALLED. Netclient appears to already be installed for " + cfg.Network + ". To re-install, please remove by executing 'sudo netclient leave -n " + cfg.Network + "'. Then re-run the install command.")
 			return err
 		}
+		if cfg.FWMark == 0 {
+			rand.Seed(time.Now().UnixNano())
+			var min int32 = 1000
+			var max int32 = 9999
+			cfg.FWMark = rand.Int31n(max-min) + min
+		}
+
 		err = config.Write(&cfg, cfg.Network)
 		if err != nil {
 			return err
@@ -91,12 +99,18 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
 		}
 	}
 
+	if ncutils.IsFreeBSD() {
+		cfg.Node.UDPHolePunch = "no"
+	}
+	// make sure name is appropriate, if not, give blank name
+	cfg.Node.Name = formatName(cfg.Node)
 	// differentiate between client/server here
 	var node models.Node // fill this node with appropriate calls
 	postnode := &models.Node{
 		Password:            cfg.Node.Password,
 		MacAddress:          cfg.Node.MacAddress,
 		AccessKey:           cfg.Server.AccessKey,
+		IsStatic:            cfg.Node.IsStatic,
 		Network:             cfg.Network,
 		ListenPort:          cfg.Node.ListenPort,
 		PostUp:              cfg.Node.PostUp,
@@ -165,6 +179,11 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
 		}
 		node.Endpoint = node.LocalAddress
 	}
+	if ncutils.IsFreeBSD() {
+		node.UDPHolePunch = "no"
+		cfg.Node.IsStatic = "yes"
+	}
+
 	if node.IsServer != "yes" { // == handle client side ==
 		err = config.ModConfig(&node)
 		if err != nil {
@@ -200,7 +219,7 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
 	}
 
 	ncutils.Log("starting wireguard")
-	err = wireguard.InitWireguard(&node, privateKey, peers, hasGateway, gateways)
+	err = wireguard.InitWireguard(&node, privateKey, peers, hasGateway, gateways, false)
 	if err != nil {
 		return err
 	}
@@ -213,3 +232,20 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
 
 	return err
 }
+
+// format name appropriately. Set to blank on failure
+func formatName(node models.Node) string {
+	// Logic to properly format name
+	if !node.NameInNodeCharSet() {
+		node.Name = ncutils.DNSFormatString(node.Name)
+	}
+	if len(node.Name) > models.MAX_NAME_LENGTH {
+		node.Name = ncutils.ShortenString(node.Name, models.MAX_NAME_LENGTH)
+	}
+	if !node.NameInNodeCharSet() || len(node.Name) > models.MAX_NAME_LENGTH {
+		ncutils.PrintLog("could not properly format name: "+node.Name, 1)
+		ncutils.PrintLog("setting name to blank", 1)
+		node.Name = ""
+	}
+	return node.Name
+}

+ 23 - 2
netclient/local/local.go

@@ -19,7 +19,9 @@ func SetIPForwarding() error {
 	var err error
 	switch os {
 	case "linux":
-		err = SetIPForwardingLinux()
+		err = SetIPForwardingUnix()
+	case "freebsd":
+		err = SetIPForwardingFreeBSD()
 	case "darwin":
 		err = SetIPForwardingMac()
 	default:
@@ -29,7 +31,7 @@ func SetIPForwarding() error {
 }
 
 // SetIPForwardingLinux - sets the ipforwarding for linux
-func SetIPForwardingLinux() error {
+func SetIPForwardingUnix() error {
 	out, err := ncutils.RunCmd("sysctl net.ipv4.ip_forward", true)
 	if err != nil {
 		log.Println("WARNING: Error encountered setting ip forwarding. This can break functionality.")
@@ -47,6 +49,25 @@ func SetIPForwardingLinux() error {
 	return nil
 }
 
+// SetIPForwardingLinux - sets the ipforwarding for linux
+func SetIPForwardingFreeBSD() error {
+	out, err := ncutils.RunCmd("sysctl net.inet.ip.forwarding", true)
+	if err != nil {
+		log.Println("WARNING: Error encountered setting ip forwarding. This can break functionality.")
+		return err
+	} else {
+		s := strings.Fields(string(out))
+		if s[1] != "1" {
+			_, err = ncutils.RunCmd("sysctl -w net.inet.ip.forwarding=1", true)
+			if err != nil {
+				log.Println("WARNING: Error encountered setting ip forwarding. You may want to investigate this.")
+				return err
+			}
+		}
+	}
+	return nil
+}
+
 // SetIPForwardingMac - sets ip forwarding for mac
 func SetIPForwardingMac() error {
 	_, err := ncutils.RunCmd("sysctl -w net.inet.ip.forwarding=1", true)

+ 1 - 1
netclient/main.go

@@ -24,7 +24,7 @@ func main() {
 	app := cli.NewApp()
 	app.Name = "Netclient CLI"
 	app.Usage = "Netmaker's netclient agent and CLI. Used to perform interactions with Netmaker server and set local WireGuard config."
-	app.Version = "v0.8.5"
+	app.Version = "v0.8.6"
 
 	hostname, err := os.Hostname()
 	if err != nil {

+ 62 - 11
netclient/ncutils/netclientutils.go

@@ -12,6 +12,7 @@ import (
 	"net/http"
 	"os"
 	"os/exec"
+	"regexp"
 	"runtime"
 	"strconv"
 	"strings"
@@ -35,6 +36,9 @@ const LINUX_APP_DATA_PATH = "/etc/netclient"
 // WINDOWS_APP_DATA_PATH - windows path
 const WINDOWS_APP_DATA_PATH = "C:\\ProgramData\\Netclient"
 
+// WINDOWS_APP_DATA_PATH - windows path
+const WINDOWS_WG_DATA_PATH = "C:\\Program Files\\WireGuard\\Data\\Configurations"
+
 // WINDOWS_SVC_NAME - service name
 const WINDOWS_SVC_NAME = "netclient"
 
@@ -65,6 +69,11 @@ func IsLinux() bool {
 	return runtime.GOOS == "linux"
 }
 
+// IsLinux - checks if is linux
+func IsFreeBSD() bool {
+	return runtime.GOOS == "freebsd"
+}
+
 // GetWireGuard - checks if wg is installed
 func GetWireGuard() string {
 	userspace := os.Getenv("WG_QUICK_USERSPACE_IMPLEMENTATION")
@@ -182,15 +191,19 @@ PersistentKeepAlive = %s
 }
 
 // CreateUserSpaceConf - creates a user space WireGuard conf
-func CreateUserSpaceConf(address string, privatekey string, listenPort string, mtu int32, perskeepalive int32, peers []wgtypes.PeerConfig) (string, error) {
+func CreateUserSpaceConf(address string, privatekey string, listenPort string, mtu int32, fwmark int32, perskeepalive int32, peers []wgtypes.PeerConfig) (string, error) {
 	peersString, err := parsePeers(perskeepalive, peers)
-	listenPortString := ""
+	var listenPortString string
+	var fwmarkString string
 	if mtu <= 0 {
 		mtu = 1280
 	}
 	if listenPort != "" {
 		listenPortString += "ListenPort = " + listenPort
 	}
+	if fwmark != 0 {
+		fwmarkString += "FWMark = " + strconv.Itoa(int(fwmark))
+	}
 	if err != nil {
 		return "", err
 	}
@@ -199,6 +212,7 @@ Address = %s
 PrivateKey = %s
 MTU = %s
 %s
+%s
 
 %s
 
@@ -207,6 +221,7 @@ MTU = %s
 		privatekey,
 		strconv.Itoa(int(mtu)),
 		listenPortString,
+		fwmarkString,
 		peersString)
 	return config, nil
 }
@@ -258,6 +273,16 @@ func GetLocalIP(localrange string) (string, error) {
 	return local, nil
 }
 
+func GetNetworkIPMask(networkstring string) (string, string, error) {
+	ip, ipnet, err := net.ParseCIDR(networkstring)
+	if err != nil {
+		return "", "", err
+	}
+	ipstring := ip.String()
+	maskstring := ipnet.Mask.String()
+	return ipstring, maskstring, err
+}
+
 // GetFreePort - gets free port of machine
 func GetFreePort(rangestart int32) (int32, error) {
 	if rangestart == 0 {
@@ -324,6 +349,15 @@ func GetNetclientPathSpecific() string {
 	}
 }
 
+// GetNetclientPathSpecific - gets specific netclient config path
+func GetWGPathSpecific() string {
+	if IsWindows() {
+		return WINDOWS_WG_DATA_PATH + "\\"
+	} else {
+		return "/etc/wireguard/"
+	}
+}
+
 // GRPCRequestOpts - gets grps request opts
 func GRPCRequestOpts(isSecure string) grpc.DialOption {
 	var requestOpts grpc.DialOption
@@ -336,33 +370,33 @@ func GRPCRequestOpts(isSecure string) grpc.DialOption {
 }
 
 // Copy - copies a src file to dest
-func Copy(src, dst string) (int64, error) {
+func Copy(src, dst string) error {
 	sourceFileStat, err := os.Stat(src)
 	if err != nil {
-		return 0, err
+		return err
 	}
 
 	if !sourceFileStat.Mode().IsRegular() {
-		return 0, errors.New(src + " is not a regular file")
+		return errors.New(src + " is not a regular file")
 	}
 
 	source, err := os.Open(src)
 	if err != nil {
-		return 0, err
+		return err
 	}
 	defer source.Close()
 
 	destination, err := os.Create(dst)
 	if err != nil {
-		return 0, err
+		return err
 	}
 	defer destination.Close()
-	nBytes, err := io.Copy(destination, source)
-	err = os.Chmod(dst, 0755)
+	_, err = io.Copy(destination, source)
 	if err != nil {
-		log.Println(err)
+		return err
 	}
-	return nBytes, err
+	err = os.Chmod(dst, 0755)
+	return err
 }
 
 // RunCmd - runs a local command
@@ -443,3 +477,20 @@ func stringAfter(original string, substring string) string {
 	}
 	return original[adjustedPosition:]
 }
+
+func ShortenString(input string, length int) string {
+	output := input
+	if len(input) > length {
+		output = input[0:length]
+	}
+	return output
+}
+
+func DNSFormatString(input string) string {
+	reg, err := regexp.Compile("[^a-zA-Z0-9-]+")
+	if err != nil {
+		Log("error with regex: " + err.Error())
+		return ""
+	}
+	return reg.ReplaceAllString(input, "")
+}

+ 46 - 0
netclient/ncutils/netclientutils_freebsd.go

@@ -0,0 +1,46 @@
+package ncutils
+
+import (
+	"context"
+	"crypto/tls"
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"math/rand"
+	"net"
+	"net/http"
+	"os"
+	"os/exec"
+	"regexp"
+	"runtime"
+	"strconv"
+	"strings"
+	"syscall"
+	"time"
+
+	"golang.zx2c4.com/wireguard/wgctrl"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/credentials"
+)
+
+// Runs Commands for FreeBSD
+func RunCmd(command string, printerr bool) (string, error) {
+	args := strings.Fields(command)
+	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+	defer cancel()
+	cmd := exec.Command(args[0], args[1:]...)
+	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+	go func() {
+		<-ctx.Done()
+		_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
+	}()
+	out, err := cmd.CombinedOutput()
+	if err != nil && printerr {
+		log.Println("error running command:", command)
+		log.Println(strings.TrimSuffix(string(out), "\n"))
+	}
+	return string(out), err
+}

+ 95 - 0
netclient/ncutils/peerhelper.go

@@ -0,0 +1,95 @@
+package ncutils
+
+import (
+	"strconv"
+	"strings"
+	"net"
+	"time"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+)
+
+func GetPeers(iface string) ([]wgtypes.Peer, error) {
+
+	var peers []wgtypes.Peer
+	output, err := RunCmd("wg show "+iface+" dump",true)
+	if err != nil {
+		return peers, err
+	}
+	for i, line := range strings.Split(strings.TrimSuffix(output, "\n"), "\n") {
+		if i == 0 {
+			continue
+		}
+		var allowedIPs []net.IPNet
+		fields := strings.Fields(line)
+		if len(fields) < 4 {
+			Log("error parsing peer: "+line)
+			continue
+		}
+		pubkeystring := fields[0]
+		endpointstring := fields[2]
+		allowedipstring := fields[3]
+		var pkeepalivestring string
+		if len(fields) > 7 {
+			pkeepalivestring = fields[7]
+		}
+		// AllowedIPs = private IP + defined networks
+
+		pubkey, err := wgtypes.ParseKey(pubkeystring)
+		if err != nil {
+			Log("error parsing peer key "+pubkeystring)
+			continue
+		}
+		ipstrings := strings.Split(allowedipstring, ",")
+		for _, ipstring := range ipstrings {
+			var netip net.IP
+			if netip = net.ParseIP(strings.Split(ipstring,"/")[0]); netip != nil {
+				allowedIPs = append(
+					allowedIPs,
+					net.IPNet{
+						IP:   netip,
+						Mask: netip.DefaultMask(),
+					},
+				)
+			}
+		}
+		if len(allowedIPs) == 0 {
+			Log("error parsing peer "+pubkeystring+", no allowedips found")
+			continue
+		}
+		var endpointarr []string
+		var endpointip net.IP
+		if endpointarr = strings.Split(endpointstring,":"); len(endpointarr) != 2 {
+			Log("error parsing peer "+pubkeystring+", could not parse endpoint: "+endpointstring)
+			continue
+		}
+		if endpointip = net.ParseIP(endpointarr[0]); endpointip == nil {
+			Log("error parsing peer "+pubkeystring+", could not parse endpoint: "+endpointarr[0])
+			continue
+		}
+		var port int
+		if port, err = strconv.Atoi(endpointarr[1]); err != nil {
+			Log("error parsing peer "+pubkeystring+", could not parse port: "+err.Error())
+			continue
+		}
+		var endpoint = net.UDPAddr {
+			IP: endpointip,
+			Port: port,
+		}
+		var dur time.Duration
+		if pkeepalivestring != "" {
+			if dur, err = time.ParseDuration(pkeepalivestring+"s"); err != nil {
+				Log("error parsing peer "+pubkeystring+", could not parse keepalive: "+err.Error())
+			}
+		}
+
+
+		peers = append(peers, wgtypes.Peer{
+			PublicKey:         pubkey,
+			Endpoint:          &endpoint,
+			AllowedIPs:        allowedIPs,
+			PersistentKeepaliveInterval: dur,
+		})
+	}
+
+	return peers, err
+}

+ 3 - 0
netclient/ncwindows/windows.go

@@ -21,6 +21,7 @@ func InitWindows() {
 	}
 	_, dataNetclientErr := os.Stat(ncutils.GetNetclientPathSpecific() + "netclient.exe")
 	_, currentNetclientErr := os.Stat(wdPath + "\\netclient.exe")
+
 	if os.IsNotExist(dataNetclientErr) { // check and see if netclient.exe is in appdata
 		if currentNetclientErr == nil { // copy it if it exists locally
 			input, err := ioutil.ReadFile(wdPath + "\\netclient.exe")
@@ -32,6 +33,8 @@ func InitWindows() {
 				log.Println("failed to copy netclient.exe to", ncutils.GetNetclientPath())
 				return
 			}
+		} else {
+			log.Fatalf("[netclient] netclient.exe not found in current working directory: %s \nexiting.", wdPath)
 		}
 	}
 	log.Println("Gravitl Netclient on Windows started")

+ 1 - 1
netclient/netclient.exe.manifest.xml

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
     <assemblyIdentity
-            version="0.8.0"
+            version="0.8.5.0"
             processorArchitecture="*"
             name="netclient.exe"
             type="win32"

+ 6 - 6
netclient/versioninfo.json

@@ -2,14 +2,14 @@
     "FixedFileInfo": {
         "FileVersion": {
             "Major": 0,
-            "Minor": 7,
-            "Patch": 3,
+            "Minor": 8,
+            "Patch": 5,
             "Build": 0
         },
         "ProductVersion": {
             "Major": 0,
-            "Minor": 7,
-            "Patch": 3,
+            "Minor": 8,
+            "Patch": 5,
             "Build": 0
         },
         "FileFlagsMask": "3f",
@@ -29,7 +29,7 @@
         "OriginalFilename": "",
         "PrivateBuild": "",
         "ProductName": "Netclient",
-        "ProductVersion": "v0.7.3.0",
+        "ProductVersion": "v0.8.5.0",
         "SpecialBuild": ""
     },
     "VarFileInfo": {
@@ -38,6 +38,6 @@
             "CharsetID": "04B0"
         }
     },
-    "IconPath": "netclient.ico",
+    "IconPath": "windowsdata/resource/netclient.ico",
     "ManifestPath": "netclient.exe.manifest.xml"
 }

BIN
netclient/windowsdata/netclient.syso → netclient/windowsdata/resource/netclient.syso


+ 64 - 29
netclient/wireguard/common.go

@@ -23,23 +23,29 @@ import (
 // SetPeers - sets peers on a given WireGuard interface
 func SetPeers(iface string, keepalive int32, peers []wgtypes.PeerConfig) error {
 
-	client, err := wgctrl.New()
-	if err != nil {
-		ncutils.PrintLog("failed to start wgctrl", 0)
-		return err
-	}
-
-	device, err := client.Device(iface)
-	if err != nil {
-		ncutils.PrintLog("failed to parse interface", 0)
-		return err
+	var devicePeers []wgtypes.Peer
+	var err error
+	if ncutils.IsFreeBSD() {
+		if devicePeers, err = ncutils.GetPeers(iface); err != nil {
+			return err
+		}
+	} else {
+		client, err := wgctrl.New()
+		if err != nil {
+			ncutils.PrintLog("failed to start wgctrl", 0)
+			return err
+		}
+		device, err := client.Device(iface)
+		if err != nil {
+			ncutils.PrintLog("failed to parse interface", 0)
+			return err
+		}
+		devicePeers = device.Peers
 	}
-	devicePeers := device.Peers
 	if len(devicePeers) > 1 && len(peers) == 0 {
 		ncutils.PrintLog("no peers pulled", 1)
 		return err
 	}
-
 	for _, peer := range peers {
 
 		for _, currentPeer := range devicePeers {
@@ -60,7 +66,7 @@ func SetPeers(iface string, keepalive int32, peers []wgtypes.PeerConfig) error {
 		allowedips = strings.Join(iparr, ",")
 		keepAliveString := strconv.Itoa(int(keepalive))
 		if keepAliveString == "0" {
-			keepAliveString = "5"
+			keepAliveString = "15"
 		}
 		if peer.Endpoint != nil {
 			_, err = ncutils.RunCmd("wg set "+iface+" peer "+peer.PublicKey.String()+
@@ -96,7 +102,7 @@ func SetPeers(iface string, keepalive int32, peers []wgtypes.PeerConfig) error {
 }
 
 // Initializes a WireGuard interface
-func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig, hasGateway bool, gateways []string) error {
+func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig, hasGateway bool, gateways []string, syncconf bool) error {
 
 	key, err := wgtypes.ParseKey(privkey)
 	if err != nil {
@@ -112,7 +118,8 @@ func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
 	if err != nil {
 		return err
 	}
-
+	fwmarkint32 := modcfg.FWMark
+	fwmarkint := int(fwmarkint32)
 	nodecfg := modcfg.Node
 	servercfg := modcfg.Server
 
@@ -160,6 +167,7 @@ func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
 		conf = wgtypes.Config{
 			PrivateKey:   &key,
 			ListenPort:   &nodeport,
+			FirewallMark: &fwmarkint,
 			ReplacePeers: true,
 			Peers:        peers,
 		}
@@ -167,9 +175,9 @@ func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
 	if !ncutils.IsKernel() {
 		var newConf string
 		if node.UDPHolePunch != "yes" {
-			newConf, _ = ncutils.CreateUserSpaceConf(node.Address, key.String(), strconv.FormatInt(int64(node.ListenPort), 10), node.MTU, node.PersistentKeepalive, peers)
+			newConf, _ = ncutils.CreateUserSpaceConf(node.Address, key.String(), strconv.FormatInt(int64(node.ListenPort), 10), node.MTU, fwmarkint32, node.PersistentKeepalive, peers)
 		} else {
-			newConf, _ = ncutils.CreateUserSpaceConf(node.Address, key.String(), "", node.MTU, node.PersistentKeepalive, peers)
+			newConf, _ = ncutils.CreateUserSpaceConf(node.Address, key.String(), "", node.MTU, fwmarkint32, node.PersistentKeepalive, peers)
 		}
 		confPath := ncutils.GetNetclientPathSpecific() + ifacename + ".conf"
 		ncutils.PrintLog("writing wg conf file to: "+confPath, 1)
@@ -178,6 +186,16 @@ func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
 			ncutils.PrintLog("error writing wg conf file to "+confPath+": "+err.Error(), 1)
 			return err
 		}
+		if ncutils.IsWindows() {
+			wgConfPath := ncutils.GetWGPathSpecific() + ifacename + ".conf"
+			ncutils.PrintLog("error writing wg conf file to "+confPath+": "+err.Error(), 1)
+			err = ioutil.WriteFile(wgConfPath, []byte(newConf), 0644)
+			if err != nil {
+				ncutils.PrintLog("error writing wg conf file to "+confPath+": "+err.Error(), 1)
+				return err
+			}
+			confPath = wgConfPath
+		}
 		// spin up userspace / windows interface + apply the conf file
 		var deviceiface string
 		if ncutils.IsMac() {
@@ -186,16 +204,20 @@ func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
 				deviceiface = ifacename
 			}
 		}
-		d, _ := wgclient.Device(deviceiface)
-		for d != nil && d.Name == deviceiface {
-			_ = RemoveConf(ifacename, false) // remove interface first
-			time.Sleep(time.Second >> 2)
-			d, _ = wgclient.Device(deviceiface)
-		}
-		err = ApplyConf(confPath)
-		if err != nil {
-			ncutils.PrintLog("failed to create wireguard interface", 1)
-			return err
+		if syncconf {
+			err = SyncWGQuickConf(ifacename, confPath)
+		} else {
+			d, _ := wgclient.Device(deviceiface)
+			for d != nil && d.Name == deviceiface {
+				_ = RemoveConf(ifacename, false) // remove interface first
+				time.Sleep(time.Second >> 2)
+				d, _ = wgclient.Device(deviceiface)
+			}
+			err = ApplyConf(confPath)
+			if err != nil {
+				ncutils.PrintLog("failed to create wireguard interface", 1)
+				return err
+			}
 		}
 	} else {
 		ipExec, err := exec.LookPath("ip")
@@ -258,6 +280,17 @@ func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
 		}
 	}
 
+	//extra network route setting required for freebsd and windows
+	if ncutils.IsWindows() {
+		ip, mask, err := ncutils.GetNetworkIPMask(nodecfg.NetworkSettings.AddressRange)
+		if err != nil {
+			return err
+		}
+		_, _ = ncutils.RunCmd("route add "+ip+" mask "+mask+" "+node.Address, true)
+	} else if ncutils.IsFreeBSD() {
+		_, _ = ncutils.RunCmd("route add -net "+nodecfg.NetworkSettings.AddressRange+" -interface "+ifacename, true)
+	}
+
 	return err
 }
 
@@ -279,7 +312,7 @@ func SetWGConfig(network string, peerupdate bool) error {
 	if err != nil {
 		return err
 	}
-	if peerupdate {
+	if peerupdate && !ncutils.IsFreeBSD() {
 		var iface string
 		iface = nodecfg.Interface
 		if ncutils.IsMac() {
@@ -289,8 +322,10 @@ func SetWGConfig(network string, peerupdate bool) error {
 			}
 		}
 		err = SetPeers(iface, nodecfg.PersistentKeepalive, peers)
+	} else if peerupdate {
+		err = InitWireguard(&nodecfg, privkey, peers, hasGateway, gateways, true)
 	} else {
-		err = InitWireguard(&nodecfg, privkey, peers, hasGateway, gateways)
+		err = InitWireguard(&nodecfg, privkey, peers, hasGateway, gateways, false)
 	}
 	return err
 }

+ 32 - 6
netclient/wireguard/unix.go

@@ -2,6 +2,9 @@ package wireguard
 
 import (
 	"io/ioutil"
+	"log"
+	"os"
+	"regexp"
 
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/config"
@@ -50,18 +53,41 @@ func SetWGKeyConfig(network string, serveraddr string) error {
 
 // ApplyWGQuickConf - applies wg-quick commands if os supports
 func ApplyWGQuickConf(confPath string) error {
-	if _, err := ncutils.RunCmd("wg-quick up "+confPath, true); err != nil {
+	_, _ = ncutils.RunCmd("wg-quick down "+confPath, false)
+	_, err := ncutils.RunCmd("wg-quick up "+confPath, false)
+	return err
+}
+
+// SyncWGQuickConf - formats config file and runs sync command
+func SyncWGQuickConf(iface string, confPath string) error {
+	var tmpConf = confPath + ".sync.tmp"
+	confRaw, err := ncutils.RunCmd("wg-quick strip "+confPath, false)
+	if err != nil {
+		return err
+	}
+	regex := regexp.MustCompile(".*Warning.*\n")
+	conf := regex.ReplaceAllString(confRaw, "")
+	err = ioutil.WriteFile(tmpConf, []byte(conf), 0644)
+	if err != nil {
 		return err
 	}
-	return nil
+	_, err = ncutils.RunCmd("wg syncconf "+iface+" "+tmpConf, true)
+	if err != nil {
+		log.Println(err.Error())
+		ncutils.Log("error syncing conf, resetting")
+		err = ApplyWGQuickConf(confPath)
+	}
+	errN := os.Remove(tmpConf)
+	if errN != nil {
+		ncutils.Log(errN.Error())
+	}
+	return err
 }
 
 // RemoveWGQuickConf - calls wg-quick down
 func RemoveWGQuickConf(confPath string, printlog bool) error {
-	if _, err := ncutils.RunCmd("wg-quick down "+confPath, printlog); err != nil {
-		return err
-	}
-	return nil
+	_, err := ncutils.RunCmd("wg-quick down "+confPath, printlog)
+	return err
 }
 
 // StorePrivKey - stores wg priv key on disk locally

+ 62 - 0
scripts/netclient-install.ps1

@@ -0,0 +1,62 @@
+param ($version='latest', $token)
+
+function Quit {
+    param(
+        $Text
+    )
+    Write-Host "Exiting: " $Text
+    Break Script
+}
+
+if($token -eq $null -or $token -eq ""){
+    Quit "-token required"
+}
+
+$software = "WireGuard";
+$installed = (Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Where { $_.DisplayName -eq $software }) -ne $null
+
+If(-Not $installed) {
+    Write-Host "'$software' is NOT installed. installing...";
+    $url = "https://download.wireguard.com/windows-client/wireguard-installer.exe"
+    $outpath = "$PSScriptRoot/wireguard-installer.exe"
+    Invoke-WebRequest -Uri $url -OutFile $outpath
+    $args = @("Comma","Separated","Arguments")
+    Start-Process -Filepath "$PSScriptRoot/wireguard-installer.exe" -ArgumentList $args
+    $software = "WireGuard";
+    $installed = (Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Where { $_.DisplayName -eq $software }) -ne $null
+    If(-Not $installed) {
+        Quit "Could not install WireGuard"
+    } else {
+        Write-Host "'$software' is installed."
+    }
+} else {
+	Write-Host "'$software' is installed."
+}
+$outpath = "";
+if (Test-Path -Path "C:\ProgramData\Netclient\bin\netclient.exe") {
+    $outpath = "C:\ProgramData\Netclient\bin\netclient.exe";
+} else {
+    $outpath = "$PSScriptRoot/netclient.exe"
+    Write-Host "'netclient.exe' is NOT installed. installing...";
+    Write-Host "https://github.com/gravitl/netmaker/releases/download/$version/netclient.exe";
+    $url = "https://github.com/gravitl/netmaker/releases/download/$version/netclient.exe"
+    Invoke-WebRequest -Uri $url -OutFile $outpath
+}
+$NetArgs = @("join","-t",$token)
+Start-Process -Filepath $outpath -ArgumentList $NetArgs
+
+if ((Get-Command "netclient.exe" -ErrorAction SilentlyContinue) -eq $null) { 
+    if (-not (Test-Path -Path "C:\ProgramData\Netclient\bin\netclient.exe")) {
+        New-Item -Path "C:\ProgramData\Netclient" -Name "bin" -ItemType "directory"
+        Move-Item -Path "$PSScriptRoot/netclient.exe" -Destination "C:\ProgramData\Netclient\bin\netclient.exe"
+    }
+    '''
+    Please add netclient.exe to your path to make it executable from powershell:
+        1. Open "Edit environment variables for your account"
+        2. Double click on "Path"
+        3. On a new line, paste the following: %USERPROFILE%\AppData\Netclient\bin
+        4. Click "Ok"
+    '''
+}
+
+Write-Host "'netclient' is installed."

+ 104 - 13
scripts/netclient-install.sh

@@ -1,12 +1,14 @@
 #!/bin/sh
 
 if [ $(id -u) -ne 0 ]; then
-   echo "This script must be run as root" 
+   echo "This script must be run as root"
    exit 1
 fi
 
 echo "checking dependencies..."
 
+OS=$(uname)
+
 if [ -f /etc/debian_version ]; then
 	install_cmd='apt-get install -y'
 elif [ -f /etc/alpine-release ]; then
@@ -15,6 +17,8 @@ elif [ -f /etc/centos-release ]; then
 	install_cmd='yum install -y'
 elif [ -f /etc/fedora-release ]; then
 	install_cmd='dnf install -y'
+elif [ "${OS}" = "FreeBSD" ]; then
+	install_cmd='pkg install -y'
 else
 	install_cmd=''
 fi
@@ -27,21 +31,41 @@ dependencies="wireguard"
 set -- $dependencies
 while [ -n "$1" ]; do
     echo $1
-	is_installed=$(dpkg-query -W --showformat='${Status}\n' $1 | grep "install ok installed")
-	if [ "${is_installed}" = "install ok installed" ]; then
-		echo "    " $1 is installed
+	if [ "${OS}" = "FreeBSD" ]; then
+		is_installed=$(pkg check -d $1 | grep '100%')
+		if [ "${is_installed}" = '100%' ]; then
+			echo "    " $1 is installed
+		else
+			echo "    " $1 is not installed. Attempting install.
+			${install_cmd} $1
+			sleep 5
+			is_installed=$(pkg check -d $1 | grep '100%')
+			if [ "${is_installed}" = '100%' ]; then
+				echo "    " $1 is installed
+			elif [ -x "$(command -v $1)" ]; then
+				echo "    " $1 is installed
+			else
+				echo "    " FAILED TO INSTALL $1
+				echo "    " This may break functionality.
+			fi
+		fi	
 	else
-		echo "    " $1 is not installed. Attempting install.
-		${install_cmd} $1
-		sleep 5
 		is_installed=$(dpkg-query -W --showformat='${Status}\n' $1 | grep "install ok installed")
-          	if [ "${is_installed}" = "install ok installed" ]; then
-			echo "    " $1 is installed
-		elif [ -x "$(command -v $1)" ]; then
+		if [ "${is_installed}" = "install ok installed" ]; then
 			echo "    " $1 is installed
 		else
-			echo "    " FAILED TO INSTALL $1
-			echo "    " This may break functionality.
+			echo "    " $1 is not installed. Attempting install.
+			${install_cmd} $1
+			sleep 5
+			is_installed=$(dpkg-query -W --showformat='${Status}\n' $1 | grep "install ok installed")
+				if [ "${is_installed}" = "install ok installed" ]; then
+				echo "    " $1 is installed
+			elif [ -x "$(command -v $1)" ]; then
+				echo "    " $1 is installed
+			else
+				echo "    " FAILED TO INSTALL $1
+				echo "    " This may break functionality.
+			fi
 		fi
 	fi
 	shift
@@ -52,6 +76,7 @@ set -e
 [ -z "$KEY" ] && KEY=nokey;
 [ -z "$VERSION" ] && echo "no \$VERSION provided, fallback to latest" && VERSION=latest;
 [ "latest" != "$VERSION" ] && [ "v" != `echo $VERSION | cut -c1` ] && VERSION="v$VERSION"
+[ -z "$NAME" ] && NAME="";
 
 dist=netclient
 
@@ -92,6 +117,36 @@ case $(uname | tr '[:upper:]' '[:lower:]') in
 	darwin)
         	dist=netclient-darwin
 	;;
+	freebsd*)
+		if [ -z "$CPU_ARCH" ]; then
+			CPU_ARCH=$(uname -m)
+		fi
+		case $CPU_ARCH in
+			amd64)
+				dist=netclient-freebsd
+			;;
+			x86_64)
+				dist=netclient-freebsd
+			;;
+                        x86_32)
+                                dist=netclient-freebsd-32
+                        ;;
+ 			arm64)
+				dist=netclient-freebsd-arm64
+			;;
+			aarch64)
+                                dist=netclient-freebsd-arm64
+			;;
+			armv7l)
+                                dist=netclient-freebsd-armv7
+			;;
+			arm*)
+				dist=netclient-freebsd-$CPU_ARCH
+            		;;
+			*)
+				fatal "$CPU_ARCH : cpu architecture not supported"
+    		esac
+	;;
 esac
 
 echo "Binary = $dist"
@@ -105,5 +160,41 @@ else
 	wget -nv -O netclient https://github.com/gravitl/netmaker/releases/download/latest/$dist
 fi
 chmod +x netclient
-./netclient join -t $KEY
+
+EXTRA_ARGS=""
+if [ "${OS}" = "FreeBSD" ]; then
+	EXTRA_ARGS = "--daemon=off"
+fi
+
+if [ -z "${NAME}" ]; then
+  ./netclient join -t $KEY $EXTRA_ARGS
+else
+  ./netclient join -t $KEY --name $NAME $EXTRA_ARGS
+fi
+
 rm -f netclient
+
+if [ "${OS}" = "FreeBSD" ]; then
+	tee /usr/local/etc/rc.d/netclient <<'EOF' >/dev/null
+#!/bin/sh
+
+# PROVIDE: netclient
+# REQUIRE: LOGIN DAEMON NETWORKING SERVERS FILESYSTEM
+# BEFORE:  
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="netclient"
+rcvar=netclient_enable
+pidfile="/var/run/${name}.pid"
+command="/usr/sbin/daemon"
+command_args="-c -f -P ${pidfile} -R 10 -t "Netclient" -u root -o /etc/netclient/netclient.log /etc/netclient/netclient checkin -n all"
+
+load_rc_config $name
+run_rc_command "$1"
+
+EOF
+	/usr/local/etc/rc.d/netclient enable
+	/usr/local/etc/rc.d/netclient start
+fi

+ 17 - 0
scripts/netclient-rc-freebsd

@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# PROVIDE: netclient
+# REQUIRE: LOGIN DAEMON NETWORKING SERVERS FILESYSTEM
+# BEFORE:  
+# KEYWORD: shutdown
+
+. /etc/rc.subr
+
+name="netclient"
+rcvar=netclient_enable
+pidfile="/var/run/${name}.pid"
+command="/usr/sbin/daemon"
+command_args="-c -f -P ${pidfile} -R 10 -t "Netclient" -u root -o /etc/netclient/netclient.log /etc/netclient/netclient checkin -n all"
+
+load_rc_config $name
+run_rc_command "$1"

+ 0 - 109
scripts/netclient.sh

@@ -1,109 +0,0 @@
-#!/bin/bash
-echo "checking for root permissions..."
-
-if [ $EUID -ne 0 ]; then
-   echo "This script must be run as root" 
-   exit 1
-fi
-
-
-echo "checking dependencies..."
-
-declare -A osInfo;
-osInfo[/etc/debian_version]="apt-get install -y"
-osInfo[/etc/alpine-release]="apk --update add"
-osInfo[/etc/centos-release]="yum install -y"
-osInfo[/etc/fedora-release]="dnf install -y"
-
-for f in ${!osInfo[@]}
-do
-    if [[ -f $f ]];then
-        install_cmd=${osInfo[$f]}
-    fi
-done
-
-dependencies=("docker.io" "docker-compose" "wireguard")
-
-for dependency in ${dependencies[@]}; do
-    is_installed=$(dpkg-query -W --showformat='${Status}\n' ${dependency} | grep "install ok installed")
-
-    if [ "${is_installed}" == "install ok installed" ]; then
-        echo "    " ${dependency} is installed
-    else
-            echo "    " ${dependency} is not installed. Attempting install.
-            ${install_cmd} ${dependency}
-            sleep 5
-            is_installed=$(dpkg-query -W --showformat='${Status}\n' ${dependency} | grep "install ok installed")
-            if [ "${is_installed}" == "install ok installed" ]; then
-                echo "    " ${dependency} is installed
-            elif [ -x "$(command -v ${dependency})" ]; then
-                echo "    " ${dependency} is installed
-            else
-                echo "    " failed to install ${dependency}. Exiting.
-                exit 1
-            fi
-    fi
-done
-
-
-
-
-set -e
-
-echo "setting public ip values..."
-
-NETMAKER_BASE_DOMAIN=nm.$(curl -s ifconfig.me | tr . -).nip.io
-COREDNS_IP=$(ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p')
-SERVER_PUBLIC_IP=$(curl -s ifconfig.me)
-REPLACE_MASTER_KEY=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 30 ; echo '')
-EMAIL="[email protected]"
-
-echo "        domain: $NETMAKER_BASE_DOMAIN"
-echo "    coredns ip: $COREDNS_IP"
-echo "     public ip: $SERVER_PUBLIC_IP"
-echo "    master key: $REPLACE_MASTER_KEY"
-
-
-echo "setting caddyfile..."
-
-
-wget -O /root/Caddyfile https://raw.githubusercontent.com/gravitl/netmaker/master/docker/Caddyfile
-sed -i "s/NETMAKER_BASE_DOMAIN/$NETMAKER_BASE_DOMAIN/g" /root/Caddyfile
-sed -i "s/YOUR_EMAIL/$EMAIL/g" /root/Caddyfile
-
-
-echo "setting docker-compose..."
-
-wget -O /root/docker-compose.yml https://raw.githubusercontent.com/gravitl/netmaker/master/compose/docker-compose.caddy.yml
-sed -i "s/NETMAKER_BASE_DOMAIN/$NETMAKER_BASE_DOMAIN/g" /root/docker-compose.yml
-sed -i "s/SERVER_PUBLIC_IP/$SERVER_PUBLIC_IP/g" /root/docker-compose.yml
-sed -i "s/COREDNS_IP/$COREDNS_IP/g" /root/docker-compose.yml
-sed -i "s/REPLACE_MASTER_KEY/$REPLACE_MASTER_KEY/g" /root/docker-compose.yml
-
-echo "starting containers..."
-
-docker-compose -f /root/docker-compose.yml up -d
-
-cat << "EOF"
-
-
-    ______     ______     ______     __   __   __     ______   __                        
-   /\  ___\   /\  == \   /\  __ \   /\ \ / /  /\ \   /\__  _\ /\ \                       
-   \ \ \__ \  \ \  __<   \ \  __ \  \ \ \'/   \ \ \  \/_/\ \/ \ \ \____                  
-    \ \_____\  \ \_\ \_\  \ \_\ \_\  \ \__|    \ \_\    \ \_\  \ \_____\                 
-     \/_____/   \/_/ /_/   \/_/\/_/   \/_/      \/_/     \/_/   \/_____/                 
-                                                                                         
- __   __     ______     ______   __    __     ______     __  __     ______     ______    
-/\ "-.\ \   /\  ___\   /\__  _\ /\ "-./  \   /\  __ \   /\ \/ /    /\  ___\   /\  == \   
-\ \ \-.  \  \ \  __\   \/_/\ \/ \ \ \-./\ \  \ \  __ \  \ \  _"-.  \ \  __\   \ \  __<   
- \ \_\\"\_\  \ \_____\    \ \_\  \ \_\ \ \_\  \ \_\ \_\  \ \_\ \_\  \ \_____\  \ \_\ \_\ 
-  \/_/ \/_/   \/_____/     \/_/   \/_/  \/_/   \/_/\/_/   \/_/\/_/   \/_____/   \/_/ /_/ 
-                                                                                         															 
-
-EOF
-
-echo "             finished installing"
-echo " "
-echo "             visit dashboard.$NETMAKER_BASE_DOMAIN to log in"
-echo " "
-echo " "

+ 1 - 1
scripts/nm-quick.sh

@@ -126,7 +126,7 @@ sed -i "s/YOUR_EMAIL/$EMAIL/g" /root/Caddyfile
 
 echo "setting docker-compose..."
 
-wget -q -O /root/docker-compose.yml https://raw.githubusercontent.com/gravitl/netmaker/develop/compose/docker-compose.caddy.yml
+wget -q -O /root/docker-compose.yml https://raw.githubusercontent.com/gravitl/netmaker/master/compose/docker-compose.contained.yml
 sed -i "s/NETMAKER_BASE_DOMAIN/$NETMAKER_BASE_DOMAIN/g" /root/docker-compose.yml
 sed -i "s/SERVER_PUBLIC_IP/$SERVER_PUBLIC_IP/g" /root/docker-compose.yml
 sed -i "s/COREDNS_IP/$COREDNS_IP/g" /root/docker-compose.yml

+ 39 - 0
scripts/openwrt-daemon-2.sh

@@ -0,0 +1,39 @@
+#!/bin/sh /etc/rc.common
+#Created by oycol<[email protected]>
+
+EXTRA_COMMANDS="status"
+EXTRA_HELP="        status      Check service is running"
+START=99
+
+LOG_FILE="/tmp/netclient.logs"
+
+start() {
+  if [ ! -f "${LOG_FILE}" ];then
+      touch "${LOG_FILE}"
+  fi
+  local PID=$(ps -e|grep "netclient checkin -n all"|grep -v grep|awk '{print $1}')
+  if [ "${PID}" ];then
+    echo "service is running"
+    return
+  fi
+  bash -c "while [ 1 ]; do /etc/netclient/netclient checkin -n all >> ${LOG_FILE} 2>&1;sleep 15;\
+           if [ $(ls -l ${LOG_FILE}|awk '{print $5}') -gt 10240000 ];then tar zcf "${LOG_FILE}.tar" -C / "tmp/netclient.logs"  && > $LOG_FILE;fi;done &"
+  echo "start"
+}
+
+stop() {
+  local PID=$(ps -e|grep "netclient checkin -n all"|grep -v grep|awk '{print $1}')
+  if [ "${PID}" ];then
+    kill "${PID}"
+  fi
+  echo "stop"
+}
+
+status() {
+  local PID=$(ps -e|grep "netclient checkin -n all"|grep -v grep|awk '{print $1}')
+  if [ "${PID}" ];then
+    echo -e "netclient[${PID}] is running \n"
+  else
+    echo -e "netclient is not running \n"
+  fi
+}

+ 39 - 0
scripts/openwrt-daemon.sh

@@ -0,0 +1,39 @@
+#!/bin/bash /etc/rc.common
+#Created by oycol<[email protected]>
+
+EXTRA_COMMANDS="status"
+EXTRA_HELP="        status  	Check service is running"
+START=99
+
+LOG_FILE="/tmp/netclient.logs"
+
+start() {
+  if [ ! -f "${LOG_FILE}" ];then
+      touch "${LOG_FILE}"
+  fi
+  local PIDS=($(ps -e|grep "netclient checkin -n all"|grep -v grep|awk '{print $1}'))
+  if [ ${PIDS} ];then
+    echo "service is running"
+    return
+  fi
+  bash -c "while [ 1 ]; do /etc/netclient/netclient checkin -n all >> ${LOG_FILE} 2>&1;sleep 15;\
+           if [ $(ls -l ${LOG_FILE}|awk '{print $5}') -gt 10240000 ];then tar zcf "${LOG_FILE}.tar" -C / "tmp/netclient.logs"  && > ${LOG_FILE};fi;done &"
+  echo "start"
+}
+
+stop() {
+  local PIDS=($(ps -e|grep "netclient checkin -n all"|grep -v grep|awk '{print $1}'))
+  for i in "${PIDS[@]}"; do
+    kill $i
+  done
+  echo "stop"
+}
+
+status() {
+  local PIDS=($(ps -e|grep "netclient checkin -n all"|grep -v grep|awk '{print $1}'))
+  if [ ${PIDS} ];then
+    echo -e "netclient[${PIDS}] is running \n"
+  else
+    echo -e "netclient is not running \n"
+  fi
+}

+ 31 - 0
servercfg/serverconf.go

@@ -33,6 +33,7 @@ func GetServerConfig() config.ServerConfig {
 	cfg.GRPCHost = GetGRPCHost()
 	cfg.GRPCPort = GetGRPCPort()
 	cfg.MasterKey = "(hidden)"
+	cfg.DNSKey = "(hidden)"
 	cfg.AllowedOrigin = GetAllowedOrigin()
 	cfg.RestBackend = "off"
 	cfg.Verbosity = GetVerbose()
@@ -54,6 +55,10 @@ func GetServerConfig() config.ServerConfig {
 	if IsDNSMode() {
 		cfg.DNSMode = "on"
 	}
+	cfg.DisplayKeys = "off"
+	if IsDisplayKeys() {
+		cfg.DisplayKeys = "on"
+	}
 	cfg.GRPCSSL = "off"
 	if IsGRPCSSL() {
 		cfg.GRPCSSL = "on"
@@ -246,6 +251,17 @@ func GetMasterKey() string {
 	return key
 }
 
+// GetDNSKey - gets the configured dns key of server
+func GetDNSKey() string {
+	key := "secretkey"
+	if os.Getenv("DNS_KEY") != "" {
+		key = os.Getenv("DNS_KEY")
+	} else if config.Config.Server.DNSKey != "" {
+		key = config.Config.Server.DNSKey
+	}
+	return key
+}
+
 // GetAllowedOrigin - get the allowed origin
 func GetAllowedOrigin() string {
 	allowedorigin := "*"
@@ -323,6 +339,21 @@ func IsDNSMode() bool {
 	return isdns
 }
 
+// IsDisplayKeys - should server be able to display keys?
+func IsDisplayKeys() bool {
+	isdisplay := true
+	if os.Getenv("DISPLAY_KEYS") != "" {
+		if os.Getenv("DISPLAY_KEYS") == "off" {
+			isdisplay = false
+		}
+	} else if config.Config.Server.DisplayKeys != "" {
+		if config.Config.Server.DisplayKeys == "off" {
+			isdisplay = false
+		}
+	}
+	return isdisplay
+}
+
 // IsGRPCSSL - ssl grpc on or off
 func IsGRPCSSL() bool {
 	isssl := false