Browse Source

major overhaul initiated

afeiszli 4 years ago
parent
commit
78ae219b03

+ 13 - 0
config/config.go

@@ -31,6 +31,7 @@ var Config *EnvironmentConfig
 type EnvironmentConfig struct {
 type EnvironmentConfig struct {
   Server ServerConfig `yaml:"server"`
   Server ServerConfig `yaml:"server"`
   MongoConn MongoConnConfig `yaml:"mongoconn"`
   MongoConn MongoConnConfig `yaml:"mongoconn"`
+  WG WG `yaml:"wg"`
 }
 }
 
 
 // ServerConfig :
 // ServerConfig :
@@ -48,6 +49,18 @@ type ServerConfig struct {
   DisableRemoteIPCheck string `yaml:"disableremoteipcheck"`
   DisableRemoteIPCheck string `yaml:"disableremoteipcheck"`
 }
 }
 
 
+type WG struct {
+  RegisterKeyRequired  string  `yaml:"keyrequired"`
+  GRPCWireGuard  string  `yaml:"grpcwg"`
+  GRPCWGInterface  string `yaml:"grpciface"`
+  GRPCWGAddress  string `yaml:"grpcaddr"`
+  GRPCWGAddressRange  string `yaml:"grpcaddrrange"`
+  GRPCWGEndpoint  string `yaml:"grpcendpoint"`
+  GRPCWGPort  string  `yaml:"grpcport"`
+  GRPCWGPubKey  string  `yaml:"pubkey"`
+  GRPCWGPrivKey  string  `yaml:"privkey"`
+}
+
 type MongoConnConfig struct {
 type MongoConnConfig struct {
   User   string  `yaml:"user"`
   User   string  `yaml:"user"`
   Pass   string  `yaml:"pass"`
   Pass   string  `yaml:"pass"`

BIN
controllers/.serverClient.go.swp


+ 1 - 0
controllers/common.go

@@ -58,6 +58,7 @@ func GetPeersList(networkName string) ([]models.PeersResponse, error) {
 	return peers, err
 	return peers, err
 }
 }
 
 
+
 func GetExtPeersList(networkName string, macaddress string) ([]models.ExtPeersResponse, error) {
 func GetExtPeersList(networkName string, macaddress string) ([]models.ExtPeersResponse, error) {
 
 
         var peers []models.ExtPeersResponse
         var peers []models.ExtPeersResponse

+ 0 - 233
controllers/externalHttpController.go.backup

@@ -1,233 +0,0 @@
-package controller
-
-import (
-	"context"
-	"encoding/json"
-	"errors"
-	// "fmt"
-	"net/http"
-	"time"
-
-	"github.com/gorilla/mux"
-	"github.com/gravitl/netmaker/functions"
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/mongoconn"
-	"go.mongodb.org/mongo-driver/bson"
-	"go.mongodb.org/mongo-driver/mongo/options"
-	// "github.com/skip2/go-qrcode"
-)
-
-func externalHandlers(r *mux.Router) {
-
-	r.HandleFunc("/api/externals", securityCheck(http.HandlerFunc(getAllExternals))).Methods("GET")
-	r.HandleFunc("/api/externals/{network}", securityCheck(http.HandlerFunc(getNetworkExternals))).Methods("GET")
-	r.HandleFunc("/api/externals/{network}/{clientid}", securityCheck(http.HandlerFunc(getExternal))).Methods("GET")
-	r.HandleFunc("/api/externals/{network}/{clientid}/qr", securityCheck(http.HandlerFunc(getExternal))).Methods("GET")
-	r.HandleFunc("/api/externals/{network}/{ingressgateway}", securityCheck(http.HandlerFunc(getExternal))).Methods("GET")
-	r.HandleFunc("/api/externals/{network}/{clientid}", securityCheck(http.HandlerFunc(updateExternal))).Methods("PUT")
-	r.HandleFunc("/api/externals/{network}/{clientid}", securityCheck(http.HandlerFunc(deleteExternal))).Methods("DELETE")
-	r.HandleFunc("/api/externals/{network}", securityCheck(http.HandlerFunc(createExternal))).Methods("POST")
-}
-
-//Gets all nodes associated with network, including pending nodes
-func getNetworkExternals(w http.ResponseWriter, r *http.Request) {
-
-	w.Header().Set("Content-Type", "application/json")
-
-	var nodes []models.External
-	var params = mux.Vars(r)
-	nodes, err := GetNetworkExternals(params["network"])
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
-	}
-
-	//Returns all the nodes in JSON format
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(nodes)
-}
-
-func GetNetworkExternals(network string) ([]models.External, error) {
-	var nodes []models.External
-	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	filter := bson.M{"network": network}
-	//Filtering out the ID field cuz Dillon doesn't like it. May want to filter out other fields in the future
-	cur, err := collection.Find(ctx, filter, options.Find().SetProjection(bson.M{"_id": 0}))
-	if err != nil {
-		return []models.External{}, err
-	}
-	defer cancel()
-	for cur.Next(context.TODO()) {
-		//Using a different model for the ReturnExternal (other than regular node).
-		//Either we should do this for ALL structs (so Networks and Keys)
-		//OR we should just use the original struct
-		//My preference is to make some new return structs
-		//TODO: Think about this. Not an immediate concern. Just need to get some consistency eventually
-		var node models.External
-		err := cur.Decode(&node)
-		if err != nil {
-			return []models.External{}, err
-		}
-		// add item our array of nodes
-		nodes = append(nodes, node)
-	}
-	//TODO: Another fatal error we should take care of.
-	if err := cur.Err(); err != nil {
-		return []models.External{}, err
-	}
-	return nodes, nil
-}
-
-//A separate function to get all nodes, not just nodes for a particular network.
-//Not quite sure if this is necessary. Probably necessary based on front end but may want to review after iteration 1 if it's being used or not
-func getAllExternals(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-	nodes, err := functions.GetAllExternals()
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
-	}
-	//Return all the nodes in JSON format
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(nodes)
-}
-
-//Get an individual node. Nothin fancy here folks.
-func getExternal(w http.ResponseWriter, r *http.Request) {
-	// set header.
-	w.Header().Set("Content-Type", "application/json")
-
-	var params = mux.Vars(r)
-
-	var node models.Node
-
-	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
-
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
-	filter := bson.M{"macaddress": params["macaddress"], "network": params["network"]}
-	err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&node)
-
-	defer cancel()
-
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
-}
-
-//This one's a doozy
-//To create a node
-//Must have valid key and be unique
-func createExternal(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-
-	var params = mux.Vars(r)
-
-	var errorResponse = models.ErrorResponse{
-		Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
-	}
-
-	networkName := params["network"]
-
-	//Check if network exists  first
-	//TODO: This is inefficient. Let's find a better way.
-	//Just a few rows down we grab the network anyway
-	networkexists, err := functions.NetworkExists(networkName)
-
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
-	} else if !networkexists {
-		errorResponse = models.ErrorResponse{
-			Code: http.StatusNotFound, Message: "W1R3: Network does not exist! ",
-		}
-		returnErrorResponse(w, r, errorResponse)
-		return
-	}
-
-	var external models.External
-
-	//get node from body of request
-	err = json.NewDecoder(r.Body).Decode(&external)
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
-	}
-	err = ValidateExternalCreate(external)
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "badrequest"))
-		return
-	}
-
-	node, err = CreateExternal(node, networkName)
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
-	}
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
-}
-
-func updateExternal(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-
-	var params = mux.Vars(r)
-
-	//Get id from parameters
-	//id, _ := primitive.ObjectIDFromHex(params["id"])
-
-	var node models.External
-
-	//start here
-	node, err := functions.GetExternalByMacAddress(params["network"], params["macaddress"])
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
-	}
-
-	var nodechange models.ExternalUpdate
-
-	// we decode our body request params
-	_ = json.NewDecoder(r.Body).Decode(&nodechange)
-	if nodechange.Network == "" {
-		nodechange.Network = node.Network
-	}
-	if nodechange.MacAddress == "" {
-		nodechange.MacAddress = node.MacAddress
-	}
-	err = ValidateExternalUpdate(params["network"], nodechange)
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "badrequest"))
-		return
-	}
-
-	node, err = UpdateExternal(nodechange, node)
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
-	}
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
-}
-
-//Delete a node
-//Pretty straightforward
-func deleteExternal(w http.ResponseWriter, r *http.Request) {
-	// Set header
-	w.Header().Set("Content-Type", "application/json")
-
-	// get params
-	var params = mux.Vars(r)
-
-	success, err := DeleteExternal(params["macaddress"], params["network"])
-
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
-	} else if !success {
-		err = errors.New("Could not delete node " + params["macaddress"])
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
-	}
-	returnSuccessResponse(w, r, params["macaddress"]+" deleted.")
-}

+ 26 - 0
controllers/networkHttpController.go

@@ -30,6 +30,7 @@ func networkHandlers(r *mux.Router) {
 	r.HandleFunc("/api/networks/{networkname}/keyupdate", securityCheck(http.HandlerFunc(keyUpdate))).Methods("POST")
 	r.HandleFunc("/api/networks/{networkname}/keyupdate", securityCheck(http.HandlerFunc(keyUpdate))).Methods("POST")
 	r.HandleFunc("/api/networks/{networkname}/keys", securityCheck(http.HandlerFunc(createAccessKey))).Methods("POST")
 	r.HandleFunc("/api/networks/{networkname}/keys", securityCheck(http.HandlerFunc(createAccessKey))).Methods("POST")
 	r.HandleFunc("/api/networks/{networkname}/keys", securityCheck(http.HandlerFunc(getAccessKeys))).Methods("GET")
 	r.HandleFunc("/api/networks/{networkname}/keys", securityCheck(http.HandlerFunc(getAccessKeys))).Methods("GET")
+        r.HandleFunc("/api/networks/{networkname}/signuptoken", securityCheck(http.HandlerFunc(getSignupToken))).Methods("GET")
 	r.HandleFunc("/api/networks/{networkname}/keys/{name}", securityCheck(http.HandlerFunc(deleteAccessKey))).Methods("DELETE")
 	r.HandleFunc("/api/networks/{networkname}/keys/{name}", securityCheck(http.HandlerFunc(deleteAccessKey))).Methods("DELETE")
 }
 }
 
 
@@ -640,6 +641,31 @@ func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models
 	return accesskey, nil
 	return accesskey, nil
 }
 }
 
 
+func GetSignupToken(netID string) (models.AccessKey, error) {
+
+	var accesskey models.AccessKey
+        address := servercfg.GetGRPCHost() + ":" + servercfg.GetGRPCPort()
+
+        accessstringdec := address + "|" + netID + "|" + "" + "|"
+        accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(accessstringdec))
+        return accesskey, nil
+}
+func getSignupToken(w http.ResponseWriter, r *http.Request) {
+        w.Header().Set("Content-Type", "application/json")
+        var params = mux.Vars(r)
+        netID := params["networkname"]
+
+	token, err := GetSignupToken(netID)
+        if err != nil {
+                returnErrorResponse(w, r, formatError(err, "internal"))
+                return
+        }
+        w.WriteHeader(http.StatusOK)
+        json.NewEncoder(w).Encode(token)
+}
+
+
+
 //pretty simple get
 //pretty simple get
 func getAccessKeys(w http.ResponseWriter, r *http.Request) {
 func getAccessKeys(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")

+ 51 - 0
controllers/nodeGrpcController.go

@@ -73,6 +73,57 @@ func (s *NodeServiceServer) ReadNode(ctx context.Context, req *nodepb.ReadNodeRe
 	return response, nil
 	return response, nil
 }
 }
 
 
+func (s *NodeServiceServer) GetConn(ctx context.Context, data *nodepb.Client) (*nodepb.Client, error) {
+        // Get the protobuf node type from the protobuf request type
+        // Essentially doing req.Node to access the struct with a nil check
+        // Now we have to convert this into a NodeItem type to convert into BSON
+        clientreq := models.ServerClient{
+                // ID:       primitive.NilObjectID,
+                Address:             data.GetAddress(),
+                Address6:            data.GetAddress6(),
+                AccessKey:           data.GetAccesskey(),
+                PublicKey:           data.GetPublickey(),
+                PrivateKey:           data.GetPrivatekey(),
+                ServerPort:          data.GetServerport(),
+                ServerKey:          data.GetServerkey(),
+                ServerEndpoint:          data.GetServerendpoint(),
+        }
+
+        //Check to see if key is valid
+        //TODO: Triple inefficient!!! This is the third call to the DB we make for networks
+        if servercfg.IsRegisterKeyRequired() {
+		validKey := functions.IsKeyValidGlobal(clientreq.AccessKey)
+		if !validKey {
+			return nil, status.Errorf(
+                                codes.Internal,
+                                fmt.Sprintf("Invalid key, and server does not allow no-key signups"),
+                        )
+		}
+	}
+	client, err := RegisterClient(clientreq)
+
+        if err != nil {
+                // return internal gRPC error to be handled later
+                return nil, status.Errorf(
+                        codes.Internal,
+                        fmt.Sprintf("Internal error: %v", err),
+                )
+        }
+        // return the node in a CreateNodeRes type
+        response := &nodepb.Client{
+                        Privatekey:   client.PrivateKey,
+                        Publickey: client.PublicKey,
+                        Accesskey:         client.AccessKey,
+                        Address:      client.Address,
+                        Address6:     client.Address6,
+                        Serverendpoint:     client.ServerEndpoint,
+                        Serverport:     client.ServerPort,
+                        Serverkey:    client.ServerKey,
+        }
+
+        return response, nil
+}
+
 func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.CreateNodeReq) (*nodepb.CreateNodeRes, error) {
 func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.CreateNodeReq) (*nodepb.CreateNodeRes, error) {
 	// Get the protobuf node type from the protobuf request type
 	// Get the protobuf node type from the protobuf request type
 	// Essentially doing req.Node to access the struct with a nil check
 	// Essentially doing req.Node to access the struct with a nil check

+ 1 - 0
controllers/nodeHttpController.go

@@ -32,6 +32,7 @@ func nodeHandlers(r *mux.Router) {
 	r.HandleFunc("/api/nodes/{network}/{macaddress}/deleteingress", securityCheck(http.HandlerFunc(deleteIngressGateway))).Methods("DELETE")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}/deleteingress", securityCheck(http.HandlerFunc(deleteIngressGateway))).Methods("DELETE")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}/approve", authorize(true, "master", http.HandlerFunc(uncordonNode))).Methods("POST")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}/approve", authorize(true, "master", http.HandlerFunc(uncordonNode))).Methods("POST")
 	r.HandleFunc("/api/nodes/{network}", createNode).Methods("POST")
 	r.HandleFunc("/api/nodes/{network}", createNode).Methods("POST")
+	//r.HandleFunc("/api/register", registerClient).Methods("POST")
 	r.HandleFunc("/api/nodes/adm/{network}/lastmodified", authorize(true, "network", http.HandlerFunc(getLastModified))).Methods("GET")
 	r.HandleFunc("/api/nodes/adm/{network}/lastmodified", authorize(true, "network", http.HandlerFunc(getLastModified))).Methods("GET")
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods("POST")
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods("POST")
 
 

+ 194 - 0
controllers/serverClient.go

@@ -0,0 +1,194 @@
+package controller
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+
+	// "fmt"
+	"net/http"
+	"time"
+	"strconv"
+	"github.com/gorilla/mux"
+	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/serverctl"
+	"github.com/gravitl/netmaker/servercfg"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mongoconn"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/mongo/options"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+	"github.com/skip2/go-qrcode"
+)
+
+func serverClientHandlers(r *mux.Router) {
+
+	r.HandleFunc("/api/wgconf/{macaddress}", securityCheck(http.HandlerFunc(getWGClientConf))).Methods("GET")
+	r.HandleFunc("/api/register", securityCheck(http.HandlerFunc(registerClient))).Methods("POST")
+}
+
+//Get an individual extclient. Nothin fancy here folks.
+func getWGClientConf(w http.ResponseWriter, r *http.Request) {
+        // set header.
+        w.Header().Set("Content-Type", "application/json")
+
+        var params = mux.Vars(r)
+
+        var extclient models.ExtClient
+
+        collection := mongoconn.Client.Database("netmaker").Collection("extclients")
+
+        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+
+        filter := bson.M{"network": "grpc", "clientid": params["clientid"]}
+        err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&extclient)
+        if err != nil {
+                returnErrorResponse(w, r, formatError(err, "internal"))
+                return
+        }
+
+        gwnode, err := functions.GetNodeByMacAddress(extclient.Network, extclient.IngressGatewayID)
+        if err != nil {
+		fmt.Println("Could not retrieve Ingress Gateway Node " + extclient.IngressGatewayID)
+		returnErrorResponse(w, r, formatError(err, "internal"))
+                return
+        }
+
+	network, err := functions.GetParentNetwork(extclient.Network)
+        if err != nil {
+                fmt.Println("Could not retrieve Ingress Gateway Network " + extclient.Network)
+                returnErrorResponse(w, r, formatError(err, "internal"))
+                return
+        }
+	keepalive := ""
+	if network.DefaultKeepalive != 0 {
+		keepalive = "PersistentKeepalive = " + strconv.Itoa(int(network.DefaultKeepalive))
+	}
+	gwendpoint := gwnode.Endpoint + ":" + strconv.Itoa(int(gwnode.ListenPort))
+	config := fmt.Sprintf(`[Interface]
+Address = %s
+PrivateKey = %s
+
+[Peer]
+PublicKey = %s
+AllowedIPs = %s
+Endpoint = %s
+%s
+
+`, extclient.Address + "/32",
+   extclient.PrivateKey,
+   gwnode.PublicKey,
+   network.AddressRange,
+   gwendpoint,
+   keepalive)
+
+	if params["type"] == "qr" {
+		bytes, err := qrcode.Encode(config, qrcode.Medium, 220)
+		if err != nil {
+			returnErrorResponse(w, r, formatError(err, "internal"))
+			return
+		}
+		w.Header().Set("Content-Type", "image/png")
+		w.WriteHeader(http.StatusOK)
+		_, err = w.Write(bytes)
+		if err != nil {
+			returnErrorResponse(w, r, formatError(err, "internal"))
+			return
+		}
+		return
+	}
+
+	if params["type"] == "file" {
+		name := extclient.ClientID + ".conf"
+                w.Header().Set("Content-Type", "application/config")
+		w.Header().Set("Content-Disposition", "attachment; filename=\"" + name + "\"")
+		w.WriteHeader(http.StatusOK)
+		_, err := fmt.Fprint(w, config)
+		if err != nil {
+                        returnErrorResponse(w, r, formatError(err, "internal"))
+		}
+		return
+	}
+
+        defer cancel()
+
+        w.WriteHeader(http.StatusOK)
+        json.NewEncoder(w).Encode(extclient)
+}
+
+func RegisterClient(client models.ServerClient) (models.ServerClient, error) {
+	if client.PrivateKey == "" {
+		privateKey, err := wgtypes.GeneratePrivateKey()
+		if err != nil {
+			return client, err
+		}
+
+		client.PrivateKey = privateKey.String()
+		client.PublicKey = privateKey.PublicKey().String()
+	}
+
+	if client.Address == "" {
+		newAddress, err := functions.UniqueAddress6(client.Network)
+		if err != nil {
+			return client, err
+		}
+		client.Address6 = newAddress
+	}
+        if client.Network == "" { client.Network = "comms" }
+	server, err := serverctl.GetServerWGConf()
+	if err != nil {
+		return client, err
+	}
+	client.ServerEndpoint = server.ServerEndpoint
+	client.ServerPort = server.ServerPort
+	client.ServerKey = server.ServerKey
+
+
+	collection := mongoconn.Client.Database("netmaker").Collection("serverclients")
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	// insert our network into the network table
+	_, err = collection.InsertOne(ctx, client)
+	defer cancel()
+
+	if err != nil {
+		return client, err
+	}
+
+	err = serverctl.ReconfigureServerWireGuard()
+
+	return client, err
+}
+func registerClient(w http.ResponseWriter, r *http.Request) {
+        w.Header().Set("Content-Type", "application/json")
+
+        var errorResponse = models.ErrorResponse{
+                Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
+        }
+
+        var clientreq models.ServerClient
+
+        //get node from body of request
+        err := json.NewDecoder(r.Body).Decode(&clientreq)
+        if err != nil {
+                returnErrorResponse(w, r, formatError(err, "internal"))
+                return
+        }
+        if servercfg.IsRegisterKeyRequired() {
+                validKey := functions.IsKeyValidGlobal(clientreq.AccessKey)
+                if !validKey {
+                                errorResponse = models.ErrorResponse{
+                                        Code: http.StatusUnauthorized, Message: "W1R3: Key invalid, or none provided.",
+                                }
+                                returnErrorResponse(w, r, errorResponse)
+                                return
+                }
+        }
+        client, err := RegisterClient(clientreq)
+
+        if err != nil {
+                returnErrorResponse(w, r, formatError(err, "internal"))
+                return
+        }
+        w.WriteHeader(http.StatusOK)
+        json.NewEncoder(w).Encode(client)
+}

+ 68 - 1
functions/helpers.go

@@ -9,11 +9,11 @@ import (
 	"encoding/base64"
 	"encoding/base64"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"log"
 	"math/rand"
 	"math/rand"
 	"net"
 	"net"
 	"strings"
 	"strings"
 	"time"
 	"time"
-
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/mongoconn"
 	"github.com/gravitl/netmaker/mongoconn"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
@@ -81,6 +81,48 @@ func CreateServerToken(netID string) (string, error) {
 	return accesskey.AccessString, nil
 	return accesskey.AccessString, nil
 }
 }
 
 
+func GetPeersList(networkName string) ([]models.PeersResponse, error) {
+
+        var peers []models.PeersResponse
+
+        //Connection mongoDB with mongoconn class
+        collection := mongoconn.Client.Database("netmaker").Collection("nodes")
+
+        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+
+        //Get all nodes in the relevant network which are NOT in pending state
+        filter := bson.M{"network": networkName, "ispending": false}
+        cur, err := collection.Find(ctx, filter)
+
+        if err != nil {
+                return peers, err
+        }
+
+        // Close the cursor once finished and cancel if it takes too long
+        defer cancel()
+
+        for cur.Next(context.TODO()) {
+
+                var peer models.PeersResponse
+                err := cur.Decode(&peer)
+                if err != nil {
+                        log.Fatal(err)
+                }
+
+                // add the node to our node array
+                //maybe better to just return this? But then that's just GetNodes...
+                peers = append(peers, peer)
+        }
+
+        //Uh oh, fatal error! This needs some better error handling
+        //TODO: needs appropriate error handling so the server doesnt shut down.
+        if err := cur.Err(); err != nil {
+                log.Fatal(err)
+        }
+
+        return peers, err
+}
+
 func IsFieldUnique(network string, field string, value string) bool {
 func IsFieldUnique(network string, field string, value string) bool {
 
 
 	var node models.Node
 	var node models.Node
@@ -344,6 +386,31 @@ func IsKeyValid(networkname string, keyvalue string) bool {
 	return isvalid
 	return isvalid
 }
 }
 
 
+func IsKeyValidGlobal(keyvalue string) bool {
+
+        networks, _ := ListNetworks()
+        var key models.AccessKey
+        foundkey := false
+        isvalid := false
+	for _, network := range networks {
+		for i := len(network.AccessKeys) - 1; i >= 0; i-- {
+	                currentkey := network.AccessKeys[i]
+	                if currentkey.Value == keyvalue {
+	                        key = currentkey
+	                        foundkey = true
+				break
+	                }
+	        }
+		if foundkey { break }
+	}
+        if foundkey {
+                if key.Uses > 0 {
+                        isvalid = true
+                }
+        }
+        return isvalid
+}
+
 //TODO: Contains a fatal error return. Need to change
 //TODO: Contains a fatal error return. Need to change
 //This just gets a network object from a network name
 //This just gets a network object from a network name
 //Should probably just be GetNetwork. kind of a dumb name.
 //Should probably just be GetNetwork. kind of a dumb name.

+ 4 - 0
go.mod

@@ -3,6 +3,7 @@ module github.com/gravitl/netmaker
 go 1.15
 go 1.15
 
 
 require (
 require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/go-playground/validator/v10 v10.5.0
 	github.com/go-playground/validator/v10 v10.5.0
 	github.com/golang/protobuf v1.4.3 // indirect
 	github.com/golang/protobuf v1.4.3 // indirect
@@ -11,6 +12,9 @@ require (
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
 	github.com/stretchr/testify v1.6.1
 	github.com/stretchr/testify v1.6.1
 	github.com/txn2/txeh v1.3.0
 	github.com/txn2/txeh v1.3.0
+	github.com/urfave/cli v1.22.5 // indirect
+	github.com/urfave/cli/v2 v2.3.0 // indirect
+	github.com/vishvananda/netlink v1.1.0 // indirect
 	go.mongodb.org/mongo-driver v1.4.3
 	go.mongodb.org/mongo-driver v1.4.3
 	golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
 	golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
 	golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
 	golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect

+ 18 - 0
go.sum

@@ -9,7 +9,10 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -133,7 +136,12 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
 github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
 github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@@ -159,6 +167,14 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV
 github.com/txn2/txeh v1.3.0 h1:vnbv63htVMZCaQgLqVBxKvj2+HHHFUzNW7I183zjg3E=
 github.com/txn2/txeh v1.3.0 h1:vnbv63htVMZCaQgLqVBxKvj2+HHHFUzNW7I183zjg3E=
 github.com/txn2/txeh v1.3.0/go.mod h1:O7M6gUTPeMF+vsa4c4Ipx3JDkOYrruB1Wry8QRsMcw8=
 github.com/txn2/txeh v1.3.0/go.mod h1:O7M6gUTPeMF+vsa4c4Ipx3JDkOYrruB1Wry8QRsMcw8=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
+github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
+github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
+github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
+github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
+github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
+github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
 github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo=
 github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo=
@@ -210,6 +226,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -268,6 +285,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
 gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=

+ 180 - 79
grpc/node.pb.go

@@ -646,6 +646,101 @@ func (m *ExtPeersResponse) GetKeepalive() int32 {
 	return 0
 	return 0
 }
 }
 
 
+type Client struct {
+	Privatekey           string   `protobuf:"bytes,1,opt,name=privatekey,proto3" json:"privatekey,omitempty"`
+	Publickey            string   `protobuf:"bytes,2,opt,name=publickey,proto3" json:"publickey,omitempty"`
+	Accesskey            string   `protobuf:"bytes,3,opt,name=accesskey,proto3" json:"accesskey,omitempty"`
+	Address              string   `protobuf:"bytes,4,opt,name=address,proto3" json:"address,omitempty"`
+	Address6             string   `protobuf:"bytes,5,opt,name=address6,proto3" json:"address6,omitempty"`
+	Serverendpoint       string   `protobuf:"bytes,6,opt,name=serverendpoint,proto3" json:"serverendpoint,omitempty"`
+	Serverport           string   `protobuf:"bytes,7,opt,name=serverport,proto3" json:"serverport,omitempty"`
+	Serverkey            string   `protobuf:"bytes,8,opt,name=serverkey,proto3" json:"serverkey,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Client) Reset()         { *m = Client{} }
+func (m *Client) String() string { return proto.CompactTextString(m) }
+func (*Client) ProtoMessage()    {}
+func (*Client) Descriptor() ([]byte, []int) {
+	return fileDescriptor_d13bd996b67da4ef, []int{6}
+}
+
+func (m *Client) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Client.Unmarshal(m, b)
+}
+func (m *Client) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Client.Marshal(b, m, deterministic)
+}
+func (m *Client) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Client.Merge(m, src)
+}
+func (m *Client) XXX_Size() int {
+	return xxx_messageInfo_Client.Size(m)
+}
+func (m *Client) XXX_DiscardUnknown() {
+	xxx_messageInfo_Client.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Client proto.InternalMessageInfo
+
+func (m *Client) GetPrivatekey() string {
+	if m != nil {
+		return m.Privatekey
+	}
+	return ""
+}
+
+func (m *Client) GetPublickey() string {
+	if m != nil {
+		return m.Publickey
+	}
+	return ""
+}
+
+func (m *Client) GetAccesskey() string {
+	if m != nil {
+		return m.Accesskey
+	}
+	return ""
+}
+
+func (m *Client) GetAddress() string {
+	if m != nil {
+		return m.Address
+	}
+	return ""
+}
+
+func (m *Client) GetAddress6() string {
+	if m != nil {
+		return m.Address6
+	}
+	return ""
+}
+
+func (m *Client) GetServerendpoint() string {
+	if m != nil {
+		return m.Serverendpoint
+	}
+	return ""
+}
+
+func (m *Client) GetServerport() string {
+	if m != nil {
+		return m.Serverport
+	}
+	return ""
+}
+
+func (m *Client) GetServerkey() string {
+	if m != nil {
+		return m.Serverkey
+	}
+	return ""
+}
+
 type CreateNodeReq struct {
 type CreateNodeReq struct {
 	Node                 *Node    `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"`
 	Node                 *Node    `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
@@ -657,7 +752,7 @@ func (m *CreateNodeReq) Reset()         { *m = CreateNodeReq{} }
 func (m *CreateNodeReq) String() string { return proto.CompactTextString(m) }
 func (m *CreateNodeReq) String() string { return proto.CompactTextString(m) }
 func (*CreateNodeReq) ProtoMessage()    {}
 func (*CreateNodeReq) ProtoMessage()    {}
 func (*CreateNodeReq) Descriptor() ([]byte, []int) {
 func (*CreateNodeReq) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d13bd996b67da4ef, []int{6}
+	return fileDescriptor_d13bd996b67da4ef, []int{7}
 }
 }
 
 
 func (m *CreateNodeReq) XXX_Unmarshal(b []byte) error {
 func (m *CreateNodeReq) XXX_Unmarshal(b []byte) error {
@@ -696,7 +791,7 @@ func (m *CreateNodeRes) Reset()         { *m = CreateNodeRes{} }
 func (m *CreateNodeRes) String() string { return proto.CompactTextString(m) }
 func (m *CreateNodeRes) String() string { return proto.CompactTextString(m) }
 func (*CreateNodeRes) ProtoMessage()    {}
 func (*CreateNodeRes) ProtoMessage()    {}
 func (*CreateNodeRes) Descriptor() ([]byte, []int) {
 func (*CreateNodeRes) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d13bd996b67da4ef, []int{7}
+	return fileDescriptor_d13bd996b67da4ef, []int{8}
 }
 }
 
 
 func (m *CreateNodeRes) XXX_Unmarshal(b []byte) error {
 func (m *CreateNodeRes) XXX_Unmarshal(b []byte) error {
@@ -735,7 +830,7 @@ func (m *UpdateNodeReq) Reset()         { *m = UpdateNodeReq{} }
 func (m *UpdateNodeReq) String() string { return proto.CompactTextString(m) }
 func (m *UpdateNodeReq) String() string { return proto.CompactTextString(m) }
 func (*UpdateNodeReq) ProtoMessage()    {}
 func (*UpdateNodeReq) ProtoMessage()    {}
 func (*UpdateNodeReq) Descriptor() ([]byte, []int) {
 func (*UpdateNodeReq) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d13bd996b67da4ef, []int{8}
+	return fileDescriptor_d13bd996b67da4ef, []int{9}
 }
 }
 
 
 func (m *UpdateNodeReq) XXX_Unmarshal(b []byte) error {
 func (m *UpdateNodeReq) XXX_Unmarshal(b []byte) error {
@@ -774,7 +869,7 @@ func (m *UpdateNodeRes) Reset()         { *m = UpdateNodeRes{} }
 func (m *UpdateNodeRes) String() string { return proto.CompactTextString(m) }
 func (m *UpdateNodeRes) String() string { return proto.CompactTextString(m) }
 func (*UpdateNodeRes) ProtoMessage()    {}
 func (*UpdateNodeRes) ProtoMessage()    {}
 func (*UpdateNodeRes) Descriptor() ([]byte, []int) {
 func (*UpdateNodeRes) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d13bd996b67da4ef, []int{9}
+	return fileDescriptor_d13bd996b67da4ef, []int{10}
 }
 }
 
 
 func (m *UpdateNodeRes) XXX_Unmarshal(b []byte) error {
 func (m *UpdateNodeRes) XXX_Unmarshal(b []byte) error {
@@ -814,7 +909,7 @@ func (m *ReadNodeReq) Reset()         { *m = ReadNodeReq{} }
 func (m *ReadNodeReq) String() string { return proto.CompactTextString(m) }
 func (m *ReadNodeReq) String() string { return proto.CompactTextString(m) }
 func (*ReadNodeReq) ProtoMessage()    {}
 func (*ReadNodeReq) ProtoMessage()    {}
 func (*ReadNodeReq) Descriptor() ([]byte, []int) {
 func (*ReadNodeReq) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d13bd996b67da4ef, []int{10}
+	return fileDescriptor_d13bd996b67da4ef, []int{11}
 }
 }
 
 
 func (m *ReadNodeReq) XXX_Unmarshal(b []byte) error {
 func (m *ReadNodeReq) XXX_Unmarshal(b []byte) error {
@@ -860,7 +955,7 @@ func (m *ReadNodeRes) Reset()         { *m = ReadNodeRes{} }
 func (m *ReadNodeRes) String() string { return proto.CompactTextString(m) }
 func (m *ReadNodeRes) String() string { return proto.CompactTextString(m) }
 func (*ReadNodeRes) ProtoMessage()    {}
 func (*ReadNodeRes) ProtoMessage()    {}
 func (*ReadNodeRes) Descriptor() ([]byte, []int) {
 func (*ReadNodeRes) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d13bd996b67da4ef, []int{11}
+	return fileDescriptor_d13bd996b67da4ef, []int{12}
 }
 }
 
 
 func (m *ReadNodeRes) XXX_Unmarshal(b []byte) error {
 func (m *ReadNodeRes) XXX_Unmarshal(b []byte) error {
@@ -900,7 +995,7 @@ func (m *DeleteNodeReq) Reset()         { *m = DeleteNodeReq{} }
 func (m *DeleteNodeReq) String() string { return proto.CompactTextString(m) }
 func (m *DeleteNodeReq) String() string { return proto.CompactTextString(m) }
 func (*DeleteNodeReq) ProtoMessage()    {}
 func (*DeleteNodeReq) ProtoMessage()    {}
 func (*DeleteNodeReq) Descriptor() ([]byte, []int) {
 func (*DeleteNodeReq) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d13bd996b67da4ef, []int{12}
+	return fileDescriptor_d13bd996b67da4ef, []int{13}
 }
 }
 
 
 func (m *DeleteNodeReq) XXX_Unmarshal(b []byte) error {
 func (m *DeleteNodeReq) XXX_Unmarshal(b []byte) error {
@@ -946,7 +1041,7 @@ func (m *DeleteNodeRes) Reset()         { *m = DeleteNodeRes{} }
 func (m *DeleteNodeRes) String() string { return proto.CompactTextString(m) }
 func (m *DeleteNodeRes) String() string { return proto.CompactTextString(m) }
 func (*DeleteNodeRes) ProtoMessage()    {}
 func (*DeleteNodeRes) ProtoMessage()    {}
 func (*DeleteNodeRes) Descriptor() ([]byte, []int) {
 func (*DeleteNodeRes) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d13bd996b67da4ef, []int{13}
+	return fileDescriptor_d13bd996b67da4ef, []int{14}
 }
 }
 
 
 func (m *DeleteNodeRes) XXX_Unmarshal(b []byte) error {
 func (m *DeleteNodeRes) XXX_Unmarshal(b []byte) error {
@@ -986,7 +1081,7 @@ func (m *GetPeersReq) Reset()         { *m = GetPeersReq{} }
 func (m *GetPeersReq) String() string { return proto.CompactTextString(m) }
 func (m *GetPeersReq) String() string { return proto.CompactTextString(m) }
 func (*GetPeersReq) ProtoMessage()    {}
 func (*GetPeersReq) ProtoMessage()    {}
 func (*GetPeersReq) Descriptor() ([]byte, []int) {
 func (*GetPeersReq) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d13bd996b67da4ef, []int{14}
+	return fileDescriptor_d13bd996b67da4ef, []int{15}
 }
 }
 
 
 func (m *GetPeersReq) XXX_Unmarshal(b []byte) error {
 func (m *GetPeersReq) XXX_Unmarshal(b []byte) error {
@@ -1033,7 +1128,7 @@ func (m *GetExtPeersReq) Reset()         { *m = GetExtPeersReq{} }
 func (m *GetExtPeersReq) String() string { return proto.CompactTextString(m) }
 func (m *GetExtPeersReq) String() string { return proto.CompactTextString(m) }
 func (*GetExtPeersReq) ProtoMessage()    {}
 func (*GetExtPeersReq) ProtoMessage()    {}
 func (*GetExtPeersReq) Descriptor() ([]byte, []int) {
 func (*GetExtPeersReq) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d13bd996b67da4ef, []int{15}
+	return fileDescriptor_d13bd996b67da4ef, []int{16}
 }
 }
 
 
 func (m *GetExtPeersReq) XXX_Unmarshal(b []byte) error {
 func (m *GetExtPeersReq) XXX_Unmarshal(b []byte) error {
@@ -1079,7 +1174,7 @@ func (m *GetPeersRes) Reset()         { *m = GetPeersRes{} }
 func (m *GetPeersRes) String() string { return proto.CompactTextString(m) }
 func (m *GetPeersRes) String() string { return proto.CompactTextString(m) }
 func (*GetPeersRes) ProtoMessage()    {}
 func (*GetPeersRes) ProtoMessage()    {}
 func (*GetPeersRes) Descriptor() ([]byte, []int) {
 func (*GetPeersRes) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d13bd996b67da4ef, []int{16}
+	return fileDescriptor_d13bd996b67da4ef, []int{17}
 }
 }
 
 
 func (m *GetPeersRes) XXX_Unmarshal(b []byte) error {
 func (m *GetPeersRes) XXX_Unmarshal(b []byte) error {
@@ -1118,7 +1213,7 @@ func (m *GetExtPeersRes) Reset()         { *m = GetExtPeersRes{} }
 func (m *GetExtPeersRes) String() string { return proto.CompactTextString(m) }
 func (m *GetExtPeersRes) String() string { return proto.CompactTextString(m) }
 func (*GetExtPeersRes) ProtoMessage()    {}
 func (*GetExtPeersRes) ProtoMessage()    {}
 func (*GetExtPeersRes) Descriptor() ([]byte, []int) {
 func (*GetExtPeersRes) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d13bd996b67da4ef, []int{17}
+	return fileDescriptor_d13bd996b67da4ef, []int{18}
 }
 }
 
 
 func (m *GetExtPeersRes) XXX_Unmarshal(b []byte) error {
 func (m *GetExtPeersRes) XXX_Unmarshal(b []byte) error {
@@ -1157,7 +1252,7 @@ func (m *CheckInReq) Reset()         { *m = CheckInReq{} }
 func (m *CheckInReq) String() string { return proto.CompactTextString(m) }
 func (m *CheckInReq) String() string { return proto.CompactTextString(m) }
 func (*CheckInReq) ProtoMessage()    {}
 func (*CheckInReq) ProtoMessage()    {}
 func (*CheckInReq) Descriptor() ([]byte, []int) {
 func (*CheckInReq) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d13bd996b67da4ef, []int{18}
+	return fileDescriptor_d13bd996b67da4ef, []int{19}
 }
 }
 
 
 func (m *CheckInReq) XXX_Unmarshal(b []byte) error {
 func (m *CheckInReq) XXX_Unmarshal(b []byte) error {
@@ -1196,7 +1291,7 @@ func (m *CheckInRes) Reset()         { *m = CheckInRes{} }
 func (m *CheckInRes) String() string { return proto.CompactTextString(m) }
 func (m *CheckInRes) String() string { return proto.CompactTextString(m) }
 func (*CheckInRes) ProtoMessage()    {}
 func (*CheckInRes) ProtoMessage()    {}
 func (*CheckInRes) Descriptor() ([]byte, []int) {
 func (*CheckInRes) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d13bd996b67da4ef, []int{19}
+	return fileDescriptor_d13bd996b67da4ef, []int{20}
 }
 }
 
 
 func (m *CheckInRes) XXX_Unmarshal(b []byte) error {
 func (m *CheckInRes) XXX_Unmarshal(b []byte) error {
@@ -1231,6 +1326,7 @@ func init() {
 	proto.RegisterType((*CheckInResponse)(nil), "node.CheckInResponse")
 	proto.RegisterType((*CheckInResponse)(nil), "node.CheckInResponse")
 	proto.RegisterType((*PeersResponse)(nil), "node.PeersResponse")
 	proto.RegisterType((*PeersResponse)(nil), "node.PeersResponse")
 	proto.RegisterType((*ExtPeersResponse)(nil), "node.ExtPeersResponse")
 	proto.RegisterType((*ExtPeersResponse)(nil), "node.ExtPeersResponse")
+	proto.RegisterType((*Client)(nil), "node.Client")
 	proto.RegisterType((*CreateNodeReq)(nil), "node.CreateNodeReq")
 	proto.RegisterType((*CreateNodeReq)(nil), "node.CreateNodeReq")
 	proto.RegisterType((*CreateNodeRes)(nil), "node.CreateNodeRes")
 	proto.RegisterType((*CreateNodeRes)(nil), "node.CreateNodeRes")
 	proto.RegisterType((*UpdateNodeReq)(nil), "node.UpdateNodeReq")
 	proto.RegisterType((*UpdateNodeReq)(nil), "node.UpdateNodeReq")
@@ -1250,69 +1346,74 @@ func init() {
 func init() { proto.RegisterFile("grpc/node.proto", fileDescriptor_d13bd996b67da4ef) }
 func init() { proto.RegisterFile("grpc/node.proto", fileDescriptor_d13bd996b67da4ef) }
 
 
 var fileDescriptor_d13bd996b67da4ef = []byte{
 var fileDescriptor_d13bd996b67da4ef = []byte{
-	// 1020 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x57, 0xdb, 0x6e, 0x1b, 0xc5,
-	0x1b, 0x97, 0x9d, 0x93, 0xf3, 0x39, 0x4e, 0xd2, 0x49, 0x9b, 0xff, 0xfc, 0x4d, 0x55, 0x45, 0x16,
-	0x42, 0x29, 0x22, 0x76, 0x08, 0x52, 0x85, 0xc4, 0x05, 0x12, 0x2d, 0x8a, 0x40, 0x50, 0xc1, 0x22,
-	0x6e, 0xb8, 0x9b, 0xec, 0x7c, 0xde, 0x8e, 0xbc, 0x99, 0xd9, 0xec, 0x8c, 0xe3, 0xe6, 0x01, 0x78,
-	0x28, 0xc4, 0x9b, 0x70, 0xcd, 0x83, 0xa0, 0x39, 0xac, 0x77, 0x76, 0x63, 0x92, 0x54, 0xb9, 0xe3,
-	0x6e, 0xbf, 0xdf, 0x7c, 0xe7, 0xa3, 0x0d, 0x7b, 0x59, 0x59, 0xa4, 0x13, 0xa9, 0x38, 0x8e, 0x8b,
-	0x52, 0x19, 0x45, 0xd6, 0xed, 0xf7, 0x88, 0xc3, 0xce, 0x0f, 0x2a, 0x13, 0x32, 0xc1, 0xab, 0x39,
-	0x6a, 0x43, 0x5e, 0x00, 0x5c, 0xb2, 0x94, 0x71, 0x5e, 0xa2, 0xd6, 0xb4, 0x73, 0xd4, 0x39, 0xde,
-	0x4e, 0x22, 0x84, 0x0c, 0xa1, 0x57, 0x30, 0xad, 0x17, 0xaa, 0xe4, 0xb4, 0xeb, 0x5e, 0x97, 0x34,
-	0xa1, 0xb0, 0x25, 0xd1, 0x2c, 0x54, 0x39, 0xa3, 0x6b, 0xee, 0xa9, 0x22, 0x47, 0x9f, 0xc3, 0x20,
-	0x58, 0xd1, 0x85, 0x92, 0x1a, 0xc9, 0x11, 0xf4, 0x59, 0x9a, 0xa2, 0xd6, 0x46, 0xcd, 0x50, 0x06,
-	0x3b, 0x31, 0x34, 0xfa, 0x6b, 0x13, 0xd6, 0xdf, 0x2a, 0x8e, 0x64, 0x17, 0xba, 0x82, 0x07, 0x8e,
-	0xae, 0xe0, 0x84, 0xc0, 0xba, 0x64, 0x97, 0x18, 0xac, 0xbb, 0x6f, 0x6b, 0xb9, 0x72, 0x39, 0x58,
-	0x8e, 0xfc, 0x0d, 0x9f, 0xaf, 0xe8, 0xd0, 0xfb, 0x5b, 0xd1, 0x36, 0xd6, 0x5c, 0x68, 0x83, 0xb2,
-	0x50, 0xa5, 0xa1, 0xeb, 0x47, 0x9d, 0xe3, 0x8d, 0x24, 0x42, 0xc8, 0x73, 0xd8, 0x2e, 0xe6, 0x17,
-	0xb9, 0x48, 0x67, 0x78, 0x43, 0x37, 0x9c, 0x70, 0x0d, 0x58, 0xcd, 0x28, 0x79, 0xa1, 0x84, 0x34,
-	0x74, 0xd3, 0x6b, 0xae, 0xe8, 0x56, 0x16, 0xb7, 0xee, 0xcc, 0x62, 0xaf, 0x95, 0xc5, 0x23, 0xe8,
-	0xdb, 0xca, 0x54, 0x99, 0xdc, 0xf6, 0xa9, 0x89, 0x20, 0xeb, 0x97, 0xd0, 0x05, 0x4a, 0x2e, 0x64,
-	0x46, 0xe1, 0xa8, 0x73, 0xdc, 0x4b, 0x6a, 0x80, 0x1c, 0xc2, 0x66, 0xa1, 0xb4, 0x99, 0x17, 0xb4,
-	0xef, 0x44, 0x03, 0xe5, 0x6c, 0x2a, 0x6d, 0xb8, 0x5a, 0x48, 0xba, 0x13, 0x6c, 0x06, 0xda, 0x6a,
-	0x9c, 0x21, 0x16, 0x2c, 0x17, 0xd7, 0x48, 0x07, 0x2e, 0x11, 0x35, 0x60, 0xa3, 0xd1, 0xec, 0x1a,
-	0x53, 0x25, 0xa7, 0x22, 0xa3, 0xbb, 0xce, 0x60, 0x84, 0x58, 0x69, 0x5f, 0x39, 0x9b, 0xa7, 0x3d,
-	0x9f, 0xa7, 0x25, 0xe0, 0xbc, 0x95, 0x06, 0xcb, 0x29, 0x4b, 0x91, 0xee, 0xfb, 0xd7, 0x25, 0x60,
-	0xa3, 0xcd, 0x99, 0x36, 0xe9, 0x3b, 0x4c, 0x67, 0x42, 0xd2, 0x27, 0x3e, 0xda, 0x08, 0x22, 0x23,
-	0xd8, 0xb1, 0xe4, 0xa5, 0xe2, 0x62, 0x2a, 0x90, 0x53, 0xe2, 0x58, 0x1a, 0x18, 0x39, 0x86, 0xbd,
-	0xc0, 0xee, 0x34, 0x5f, 0xb3, 0x9c, 0x1e, 0xb8, 0x28, 0xda, 0xb0, 0xd3, 0xa6, 0x52, 0x96, 0x57,
-	0xb5, 0x79, 0x1a, 0xb4, 0x45, 0x98, 0xf5, 0xc9, 0x66, 0x26, 0x7d, 0xc7, 0x64, 0x86, 0x9a, 0x3e,
-	0xf3, 0x3e, 0x45, 0x90, 0xcd, 0x08, 0xcb, 0x73, 0xb5, 0x40, 0x2e, 0x0a, 0x4d, 0x0f, 0x7d, 0x7d,
-	0x6b, 0xc4, 0xf6, 0xa3, 0xd0, 0x4e, 0x27, 0xfd, 0x9f, 0x4b, 0x57, 0x45, 0x92, 0x4f, 0x61, 0x5f,
-	0x68, 0x21, 0x33, 0x6b, 0x28, 0x63, 0x06, 0x17, 0xec, 0x86, 0x3e, 0x77, 0x2c, 0xb7, 0x70, 0xeb,
-	0x87, 0xd0, 0x7c, 0xce, 0x72, 0x6d, 0x58, 0x3a, 0xa3, 0x1f, 0x39, 0xb6, 0x18, 0xb2, 0xb5, 0xe6,
-	0x52, 0xab, 0xe9, 0x94, 0x52, 0xf7, 0x18, 0x28, 0xd7, 0xd9, 0xd6, 0x5c, 0x69, 0xdd, 0xa5, 0xff,
-	0xf7, 0xfe, 0xd5, 0xc8, 0xe8, 0xf7, 0x2e, 0xec, 0xbd, 0xb6, 0x99, 0xf9, 0xae, 0x1e, 0x49, 0x0a,
-	0x5b, 0x7a, 0xee, 0xaa, 0xe6, 0x86, 0xad, 0x97, 0x54, 0x24, 0xf9, 0x04, 0x76, 0x25, 0x22, 0x2f,
-	0x10, 0xcb, 0x79, 0xc1, 0x99, 0xf1, 0xb3, 0xd7, 0x4b, 0x5a, 0xa8, 0x8d, 0xcd, 0x22, 0xbe, 0x2b,
-	0x02, 0xe7, 0x9a, 0x8f, 0xad, 0x8d, 0x57, 0x5d, 0x7e, 0x89, 0x5a, 0xb3, 0x0c, 0xdd, 0xf0, 0x85,
-	0x2e, 0x0f, 0x50, 0xb3, 0xcb, 0x37, 0xda, 0x5d, 0xfe, 0x31, 0x0c, 0xac, 0xce, 0x19, 0xde, 0x04,
-	0x43, 0x9b, 0x8e, 0xa3, 0x09, 0xda, 0x3c, 0x58, 0x80, 0x63, 0x8e, 0x06, 0xdd, 0x1c, 0xf6, 0x92,
-	0x08, 0x19, 0xfd, 0xd9, 0x85, 0xc1, 0x4f, 0x88, 0xa5, 0x5e, 0x66, 0xe1, 0x18, 0xf6, 0x84, 0xc6,
-	0x46, 0x79, 0x7c, 0x36, 0xda, 0x30, 0x19, 0x03, 0x69, 0x00, 0x3e, 0xd7, 0x7e, 0x2b, 0xad, 0x78,
-	0x79, 0xc4, 0x36, 0x79, 0xd8, 0x76, 0xeb, 0x7d, 0xe0, 0x76, 0x6b, 0x4f, 0xc2, 0xd6, 0x8a, 0x49,
-	0xb8, 0x73, 0x2f, 0x8c, 0xfe, 0xee, 0xc0, 0xfe, 0xb7, 0xef, 0x4d, 0x33, 0x81, 0xff, 0xbd, 0x30,
-	0x27, 0x30, 0x78, 0x5d, 0x22, 0x33, 0x68, 0xcf, 0x51, 0x82, 0x57, 0xe4, 0x05, 0xb8, 0xdb, 0xe9,
-	0x1a, 0xa3, 0x7f, 0x06, 0x63, 0x77, 0x54, 0xdd, 0xa3, 0xbf, 0xa9, 0x2d, 0x01, 0xfd, 0x10, 0x81,
-	0x5f, 0x5d, 0xc3, 0x7e, 0x80, 0x85, 0x58, 0xe0, 0x7e, 0x0b, 0xe7, 0xd0, 0x4f, 0x90, 0xf1, 0x5a,
-	0xff, 0xdd, 0x57, 0x3e, 0xba, 0xe4, 0xdd, 0xe6, 0x25, 0x3f, 0x89, 0x15, 0xdd, 0x6f, 0xf7, 0x67,
-	0x18, 0xbc, 0x71, 0xa3, 0xf6, 0x50, 0xcb, 0x76, 0x2f, 0x78, 0x53, 0x6f, 0xeb, 0x23, 0x1f, 0x43,
-	0xa3, 0x97, 0x4d, 0x95, 0xfa, 0xdf, 0x17, 0x97, 0x8d, 0xfa, 0x1c, 0xab, 0xfe, 0x7c, 0x4c, 0xd4,
-	0xdf, 0xc3, 0xee, 0x39, 0x9a, 0xba, 0xd7, 0x1f, 0xa3, 0xeb, 0xcb, 0xd8, 0x29, 0x4d, 0x5e, 0xc2,
-	0x86, 0x5d, 0xa1, 0x3a, 0xa4, 0xf0, 0xc0, 0xa7, 0xb0, 0x31, 0x53, 0x89, 0xe7, 0x18, 0xbd, 0x69,
-	0x79, 0xa1, 0xc9, 0x19, 0xf4, 0xf0, 0xbd, 0x89, 0xe5, 0x0f, 0xbd, 0x7c, 0x7b, 0x2c, 0x93, 0x25,
-	0xdf, 0xe8, 0x33, 0x80, 0xe5, 0xea, 0xbf, 0xbf, 0xd3, 0x7e, 0x8c, 0xb8, 0x35, 0xf9, 0x7a, 0x79,
-	0x67, 0xcb, 0xa0, 0x38, 0x08, 0x3e, 0xf3, 0x82, 0xad, 0x9b, 0x92, 0xb4, 0xb9, 0xcf, 0xfe, 0x58,
-	0x83, 0xbe, 0xd5, 0xfe, 0x0b, 0x96, 0xd7, 0x22, 0x45, 0x72, 0x0a, 0x1b, 0xee, 0x87, 0x21, 0x21,
-	0x5e, 0x41, 0xfc, 0x5b, 0x74, 0x78, 0xd0, 0xc0, 0xc2, 0x7e, 0x79, 0x05, 0x50, 0x0f, 0x17, 0x09,
-	0x2c, 0x8d, 0xf9, 0x1c, 0xae, 0x00, 0x35, 0x39, 0x85, 0x5e, 0xd5, 0xb8, 0xe4, 0x89, 0x67, 0x88,
-	0x26, 0x62, 0x78, 0x0b, 0xd2, 0xd6, 0x52, 0x3d, 0x64, 0x95, 0xa5, 0xc6, 0x9c, 0x0e, 0x57, 0x80,
-	0x4e, 0xae, 0x6e, 0xd0, 0x4a, 0xae, 0x31, 0x05, 0xc3, 0x15, 0xa0, 0x2b, 0x66, 0xd5, 0x18, 0x95,
-	0x87, 0x51, 0xf7, 0x0e, 0x6f, 0x41, 0xfa, 0xb4, 0x43, 0xbe, 0x72, 0xcd, 0x54, 0x55, 0x9b, 0x3c,
-	0x5d, 0xf2, 0x44, 0xbd, 0x3a, 0x5c, 0x85, 0x5a, 0xe1, 0x13, 0xd8, 0x0a, 0x05, 0x23, 0xfb, 0xad,
-	0xfa, 0x5d, 0x0d, 0xdb, 0x88, 0xfe, 0x66, 0xf2, 0xdb, 0x49, 0xa6, 0x54, 0x96, 0xe3, 0x38, 0x53,
-	0x39, 0x93, 0xd9, 0x58, 0x95, 0xd9, 0xc4, 0xfd, 0x97, 0xb8, 0x98, 0x4f, 0x27, 0xe6, 0xa6, 0x40,
-	0x3d, 0x99, 0x49, 0xb5, 0x90, 0xee, 0x5f, 0x46, 0x71, 0x71, 0xb1, 0xe9, 0x1e, 0xbf, 0xf8, 0x27,
-	0x00, 0x00, 0xff, 0xff, 0x4a, 0xa4, 0x7a, 0x56, 0x7b, 0x0c, 0x00, 0x00,
+	// 1093 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x57, 0x4d, 0x6f, 0x1b, 0x45,
+	0x18, 0x96, 0x9d, 0xd8, 0x71, 0x5f, 0xd7, 0x49, 0x3a, 0x6d, 0xc3, 0x60, 0xaa, 0x2a, 0x5a, 0xa1,
+	0x2a, 0x45, 0x24, 0x0e, 0x41, 0xaa, 0x90, 0x38, 0x20, 0x91, 0xa2, 0x08, 0x04, 0x15, 0x2c, 0xe2,
+	0xc2, 0x6d, 0xb2, 0xfb, 0x7a, 0x3b, 0xf2, 0x66, 0x66, 0xb3, 0x33, 0xb6, 0x9b, 0x1b, 0x17, 0x7e,
+	0x14, 0xe2, 0x9f, 0x70, 0xe6, 0x87, 0xa0, 0xf9, 0x58, 0xef, 0xec, 0xc6, 0x4d, 0x52, 0xe5, 0xc6,
+	0xcd, 0xf3, 0xcc, 0xfb, 0xf9, 0xbc, 0x1f, 0x3b, 0x86, 0x9d, 0xac, 0x2c, 0x92, 0x89, 0x90, 0x29,
+	0x1e, 0x15, 0xa5, 0xd4, 0x92, 0x6c, 0x9a, 0xdf, 0x51, 0x0a, 0x0f, 0x7f, 0x94, 0x19, 0x17, 0x31,
+	0x5e, 0xce, 0x51, 0x69, 0xf2, 0x1c, 0xe0, 0x82, 0x25, 0x2c, 0x4d, 0x4b, 0x54, 0x8a, 0x76, 0xf6,
+	0x3b, 0x07, 0x0f, 0xe2, 0x00, 0x21, 0x63, 0x18, 0x14, 0x4c, 0xa9, 0xa5, 0x2c, 0x53, 0xda, 0xb5,
+	0xb7, 0xab, 0x33, 0xa1, 0xb0, 0x25, 0x50, 0x2f, 0x65, 0x39, 0xa3, 0x1b, 0xf6, 0xaa, 0x3a, 0x46,
+	0x5f, 0xc0, 0xc8, 0x7b, 0x51, 0x85, 0x14, 0x0a, 0xc9, 0x3e, 0x0c, 0x59, 0x92, 0xa0, 0x52, 0x5a,
+	0xce, 0x50, 0x78, 0x3f, 0x21, 0x14, 0xfd, 0xd3, 0x87, 0xcd, 0x37, 0x32, 0x45, 0xb2, 0x0d, 0x5d,
+	0x9e, 0x7a, 0x89, 0x2e, 0x4f, 0x09, 0x81, 0x4d, 0xc1, 0x2e, 0xd0, 0x7b, 0xb7, 0xbf, 0x8d, 0xe7,
+	0x2a, 0x64, 0xef, 0x39, 0x88, 0xd7, 0xff, 0x7c, 0x45, 0xc7, 0x2e, 0xde, 0xea, 0x6c, 0x72, 0xcd,
+	0xb9, 0xd2, 0x28, 0x0a, 0x59, 0x6a, 0xba, 0xb9, 0xdf, 0x39, 0xe8, 0xc5, 0x01, 0x42, 0x9e, 0xc1,
+	0x83, 0x62, 0x7e, 0x9e, 0xf3, 0x64, 0x86, 0x57, 0xb4, 0x67, 0x95, 0x6b, 0xc0, 0x58, 0x46, 0x91,
+	0x16, 0x92, 0x0b, 0x4d, 0xfb, 0xce, 0x72, 0x75, 0x6e, 0xb1, 0xb8, 0x75, 0x23, 0x8b, 0x83, 0x16,
+	0x8b, 0xfb, 0x30, 0x34, 0x95, 0xa9, 0x98, 0x7c, 0xe0, 0xa8, 0x09, 0x20, 0x13, 0x17, 0x57, 0x05,
+	0x8a, 0x94, 0x8b, 0x8c, 0xc2, 0x7e, 0xe7, 0x60, 0x10, 0xd7, 0x00, 0xd9, 0x83, 0x7e, 0x21, 0x95,
+	0x9e, 0x17, 0x74, 0x68, 0x55, 0xfd, 0xc9, 0xfa, 0x94, 0x4a, 0xa7, 0x72, 0x29, 0xe8, 0x43, 0xef,
+	0xd3, 0x9f, 0x8d, 0xc5, 0x19, 0x62, 0xc1, 0x72, 0xbe, 0x40, 0x3a, 0xb2, 0x44, 0xd4, 0x80, 0xc9,
+	0x46, 0xb1, 0x05, 0x26, 0x52, 0x4c, 0x79, 0x46, 0xb7, 0xad, 0xc3, 0x00, 0x31, 0xda, 0xae, 0x72,
+	0x86, 0xa7, 0x1d, 0xc7, 0xd3, 0x0a, 0xb0, 0xd1, 0x0a, 0x8d, 0xe5, 0x94, 0x25, 0x48, 0x77, 0xdd,
+	0xed, 0x0a, 0x30, 0xd9, 0xe6, 0x4c, 0xe9, 0xe4, 0x2d, 0x26, 0x33, 0x2e, 0xe8, 0x23, 0x97, 0x6d,
+	0x00, 0x91, 0x08, 0x1e, 0x9a, 0xe3, 0x85, 0x4c, 0xf9, 0x94, 0x63, 0x4a, 0x89, 0x15, 0x69, 0x60,
+	0xe4, 0x00, 0x76, 0xbc, 0xb8, 0xb5, 0xbc, 0x60, 0x39, 0x7d, 0x6c, 0xb3, 0x68, 0xc3, 0xd6, 0x9a,
+	0x4c, 0x58, 0x5e, 0xd5, 0xe6, 0x89, 0xb7, 0x16, 0x60, 0x26, 0x26, 0xc3, 0x4c, 0xf2, 0x96, 0x89,
+	0x0c, 0x15, 0x7d, 0xea, 0x62, 0x0a, 0x20, 0xc3, 0x08, 0xcb, 0x73, 0xb9, 0xc4, 0x94, 0x17, 0x8a,
+	0xee, 0xb9, 0xfa, 0xd6, 0x88, 0xe9, 0x47, 0xae, 0xac, 0x4d, 0xfa, 0x91, 0xa5, 0xab, 0x3a, 0x92,
+	0xcf, 0x60, 0x97, 0x2b, 0x2e, 0x32, 0xe3, 0x28, 0x63, 0x1a, 0x97, 0xec, 0x8a, 0x3e, 0xb3, 0x22,
+	0xd7, 0x70, 0x13, 0x07, 0x57, 0xe9, 0x9c, 0xe5, 0x4a, 0xb3, 0x64, 0x46, 0x3f, 0xb1, 0x62, 0x21,
+	0x64, 0x6a, 0x9d, 0x0a, 0x25, 0xa7, 0x53, 0x4a, 0xed, 0xa5, 0x3f, 0xd9, 0xce, 0x36, 0xee, 0x4a,
+	0x13, 0x2e, 0xfd, 0xd8, 0xc5, 0x57, 0x23, 0xd1, 0x9f, 0x5d, 0xd8, 0x39, 0x35, 0xcc, 0x7c, 0x5f,
+	0x8f, 0x24, 0x85, 0x2d, 0x35, 0xb7, 0x55, 0xb3, 0xc3, 0x36, 0x88, 0xab, 0x23, 0x79, 0x01, 0xdb,
+	0x02, 0x31, 0x2d, 0x10, 0xcb, 0x79, 0x91, 0x32, 0xed, 0x66, 0x6f, 0x10, 0xb7, 0x50, 0x93, 0x9b,
+	0x41, 0x5c, 0x57, 0x78, 0xc9, 0x0d, 0x97, 0x5b, 0x1b, 0xaf, 0xba, 0xfc, 0x02, 0x95, 0x62, 0x19,
+	0xda, 0xe1, 0xf3, 0x5d, 0xee, 0xa1, 0x66, 0x97, 0xf7, 0xda, 0x5d, 0xfe, 0x29, 0x8c, 0x8c, 0xcd,
+	0x19, 0x5e, 0x79, 0x47, 0x7d, 0x2b, 0xd1, 0x04, 0x0d, 0x0f, 0x06, 0x48, 0x31, 0x47, 0x8d, 0x76,
+	0x0e, 0x07, 0x71, 0x80, 0x44, 0x7f, 0x77, 0x61, 0xf4, 0x33, 0x62, 0xa9, 0x56, 0x2c, 0x1c, 0xc0,
+	0x0e, 0x57, 0xd8, 0x28, 0x8f, 0x63, 0xa3, 0x0d, 0x93, 0x23, 0x20, 0x0d, 0xc0, 0x71, 0xed, 0xb6,
+	0xd2, 0x9a, 0x9b, 0x7b, 0x6c, 0x93, 0xbb, 0x6d, 0xb7, 0xc1, 0x07, 0x6e, 0xb7, 0xf6, 0x24, 0x6c,
+	0xad, 0x99, 0x84, 0x1b, 0xf7, 0x42, 0xf4, 0x6f, 0x07, 0x76, 0xbf, 0x7b, 0xa7, 0x9b, 0x04, 0xfe,
+	0xff, 0xd2, 0xfc, 0xa3, 0x0b, 0xfd, 0xd3, 0x9c, 0xa3, 0xdb, 0xeb, 0x45, 0xc9, 0x17, 0x4c, 0xa3,
+	0xc9, 0xce, 0x7f, 0x1d, 0x6b, 0xa4, 0x99, 0x7c, 0xb7, 0x9d, 0x7c, 0x63, 0x4f, 0x6e, 0xb4, 0xf7,
+	0x64, 0x90, 0xfe, 0xe6, 0xfb, 0xd3, 0xef, 0xb5, 0xd2, 0x7f, 0x01, 0xdb, 0x0a, 0xcb, 0x05, 0x96,
+	0x2d, 0x5a, 0x5b, 0xa8, 0xdd, 0xe1, 0x16, 0xb1, 0x34, 0xf9, 0x2f, 0x52, 0x8d, 0x98, 0xd8, 0xdc,
+	0xc9, 0xc4, 0xe6, 0x38, 0xae, 0x81, 0x68, 0x02, 0xa3, 0xd3, 0x12, 0x99, 0x46, 0xf3, 0x45, 0x8e,
+	0xf1, 0x92, 0x3c, 0x07, 0xfb, 0x7c, 0xb0, 0x14, 0x0c, 0x4f, 0xe0, 0xc8, 0xbe, 0x2b, 0xec, 0xa5,
+	0x7b, 0x56, 0xb4, 0x14, 0xd4, 0x5d, 0x14, 0x7e, 0xb3, 0x33, 0xfb, 0x01, 0x1e, 0x42, 0x85, 0xdb,
+	0x3d, 0x9c, 0xc1, 0x30, 0x46, 0x96, 0xd6, 0xf6, 0x6f, 0x7e, 0xe8, 0x04, 0x8f, 0x99, 0x6e, 0xf3,
+	0x31, 0x73, 0x18, 0x1a, 0xba, 0xdd, 0xef, 0x2f, 0x30, 0x7a, 0x6d, 0xb7, 0xcd, 0x5d, 0x3d, 0x9b,
+	0xd5, 0xe8, 0x5c, 0xbd, 0xa9, 0xdf, 0x39, 0x21, 0x14, 0xbd, 0x6c, 0x9a, 0x54, 0xef, 0xdf, 0xdd,
+	0x26, 0xeb, 0x33, 0xac, 0x46, 0xf4, 0x3e, 0x59, 0xff, 0x00, 0xdb, 0x67, 0xa8, 0xeb, 0x71, 0xbf,
+	0x8f, 0xad, 0xaf, 0xc2, 0xa0, 0x14, 0x79, 0x09, 0x3d, 0xf3, 0x15, 0x51, 0x9e, 0xc2, 0xc7, 0x8e,
+	0xc2, 0xc6, 0x5a, 0x89, 0x9d, 0x44, 0xf4, 0xba, 0x15, 0x85, 0x22, 0x27, 0x30, 0xc0, 0x77, 0x3a,
+	0xd4, 0xdf, 0x73, 0xfa, 0xed, 0xcd, 0x14, 0xaf, 0xe4, 0xa2, 0xcf, 0x01, 0x56, 0x5f, 0xbf, 0xdb,
+	0x3b, 0xed, 0xa7, 0x40, 0x5a, 0x91, 0x6f, 0x56, 0x4f, 0x8d, 0xd2, 0x1b, 0xf6, 0x8a, 0x4f, 0x9d,
+	0x62, 0xeb, 0xb3, 0x1a, 0xb7, 0xa5, 0x4f, 0xfe, 0xda, 0x80, 0xa1, 0xb1, 0xfe, 0x2b, 0x96, 0x0b,
+	0x9e, 0x20, 0x39, 0x86, 0x9e, 0x7d, 0x1b, 0x13, 0xe2, 0x0c, 0x84, 0xcf, 0xf1, 0xf1, 0xe3, 0x06,
+	0xe6, 0x57, 0xec, 0x2b, 0x80, 0x7a, 0xb8, 0x88, 0x17, 0x69, 0xcc, 0xe7, 0x78, 0x0d, 0xa8, 0xc8,
+	0x31, 0x0c, 0xaa, 0xc6, 0x25, 0x8f, 0x9c, 0x40, 0x30, 0x11, 0xe3, 0x6b, 0x90, 0x32, 0x9e, 0xea,
+	0x21, 0xab, 0x3c, 0x35, 0xe6, 0x74, 0xbc, 0x06, 0xb4, 0x7a, 0x75, 0x83, 0x56, 0x7a, 0x8d, 0x29,
+	0x18, 0xaf, 0x01, 0x6d, 0x31, 0xab, 0xc6, 0xa8, 0x22, 0x0c, 0xba, 0x77, 0x7c, 0x0d, 0x52, 0xc7,
+	0x1d, 0xf2, 0xb5, 0x6d, 0xa6, 0xaa, 0xda, 0xe4, 0xc9, 0x4a, 0x26, 0xe8, 0xd5, 0xf1, 0x3a, 0xd4,
+	0x28, 0x1f, 0xc2, 0x96, 0x2f, 0x18, 0xd9, 0x6d, 0xd5, 0xef, 0x72, 0xdc, 0x46, 0xd4, 0xb7, 0x93,
+	0xdf, 0x0f, 0x33, 0x29, 0xb3, 0x1c, 0x8f, 0x32, 0x99, 0x33, 0x91, 0x1d, 0xc9, 0x32, 0x9b, 0xd8,
+	0xbf, 0x53, 0xe7, 0xf3, 0xe9, 0x44, 0x5f, 0x15, 0xa8, 0x26, 0x33, 0x21, 0x97, 0xc2, 0xfe, 0xd1,
+	0x2a, 0xce, 0xcf, 0xfb, 0xf6, 0xf2, 0xcb, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x11, 0xa6, 0x77,
+	0x7c, 0x7e, 0x0d, 0x00, 0x00,
 }
 }

+ 11 - 0
grpc/node.proto

@@ -84,6 +84,17 @@ message ExtPeersResponse {
     int32 keepalive = 13;
     int32 keepalive = 13;
 }
 }
 
 
+message Client {
+    string privatekey = 1;
+    string publickey = 2;
+    string accesskey = 3;
+    string address = 4;
+    string address6 = 5;
+    string serverendpoint = 6;
+    string serverport = 7;
+    string serverkey = 8;
+}
+
 message CreateNodeReq {
 message CreateNodeReq {
     Node node = 1; // Node id blank
     Node node = 1; // Node id blank
 }
 }

+ 11 - 1
main.go

@@ -59,6 +59,16 @@ func main() {
 		installserver = true
 		installserver = true
 	}
 	}
 
 
+	if servercfg.IsGRPCWireGuard() {
+		err = serverctl.InitServerWireGuard()
+                if err != nil {
+                        log.Fatal(err)
+                }
+		err = serverctl.ReconfigureServerWireGuard()
+                if err != nil {
+                        log.Fatal(err)
+                }
+	}
 	//NOTE: Removed Check and Logic for DNS Mode
 	//NOTE: Removed Check and Logic for DNS Mode
 	//Reasoning. DNS Logic is very small on server. Can run with little/no impact. Just sets a tiny config file.
 	//Reasoning. DNS Logic is very small on server. Can run with little/no impact. Just sets a tiny config file.
 	//Real work is done by CoreDNS
 	//Real work is done by CoreDNS
@@ -113,7 +123,7 @@ func runGRPC(wg *sync.WaitGroup, installserver bool) {
 	listener, err := net.Listen("tcp", ":"+grpcport)
 	listener, err := net.Listen("tcp", ":"+grpcport)
         // Handle errors if any
         // Handle errors if any
         if err != nil {
         if err != nil {
-                log.Fatalf("Unable to listen on port" + grpcport + ": %v", err)
+                log.Fatalf("Unable to listen on port " + grpcport + ", error: %v", err)
         }
         }
 
 
          s := grpc.NewServer(
          s := grpc.NewServer(

BIN
models/.serverclient.go.swp


+ 10 - 0
models/network.go

@@ -31,6 +31,9 @@ type Network struct {
 	AllowManualSignUp   *bool       `json:"allowmanualsignup" bson:"allowmanualsignup"`
 	AllowManualSignUp   *bool       `json:"allowmanualsignup" bson:"allowmanualsignup"`
 	IsLocal             *bool       `json:"islocal" bson:"islocal"`
 	IsLocal             *bool       `json:"islocal" bson:"islocal"`
 	IsDualStack         *bool       `json:"isdualstack" bson:"isdualstack"`
 	IsDualStack         *bool       `json:"isdualstack" bson:"isdualstack"`
+	IsIPv4         string       `json:"isipv4" bson:"isipv4"`
+	IsIPv6         string       `json:"isipv6" bson:"isipv6"`
+	IsGRPCHub         string       `json:"isgrpchub" bson:"isgrpchub"`
 	LocalRange          string      `json:"localrange" bson:"localrange" validate:"omitempty,cidr"`
 	LocalRange          string      `json:"localrange" bson:"localrange" validate:"omitempty,cidr"`
 	//can't have min=1 with omitempty
 	//can't have min=1 with omitempty
 	DefaultCheckInInterval int32 `json:"checkininterval,omitempty" bson:"checkininterval,omitempty" validate:"omitempty,numeric,min=2,max=100000"`
 	DefaultCheckInInterval int32 `json:"checkininterval,omitempty" bson:"checkininterval,omitempty" validate:"omitempty,numeric,min=2,max=100000"`
@@ -103,4 +106,11 @@ func (network *Network) SetDefaults() {
 		signup := false
 		signup := false
 		network.AllowManualSignUp = &signup
 		network.AllowManualSignUp = &signup
 	}
 	}
+	if (network.IsDualStack != nil) && *network.IsDualStack {
+		network.IsIPv6 = "yes"
+		network.IsIPv4 = "yes"
+	} else if network.IsGRPCHub != "yes" {
+                network.IsIPv6 = "no"
+                network.IsIPv4 = "yes"
+	}
 }
 }

+ 16 - 0
models/serverclient.go

@@ -0,0 +1,16 @@
+package models
+
+import (
+)
+type ServerClient struct {
+	PrivateKey     string             `json:"privatekey" bson:"privatekey"`
+	PublicKey      string             `json:"publickey" bson:"publickey"`
+	AccessKey      string             `json:"publickey" bson:"accesskey"`
+	Address        string             `json:"address" bson:"address"`
+	Address6        string             `json:"address6" bson:"address6"`
+	Network        string             `json:"network" bson:"network"`
+	ServerEndpoint  string             `json:"serverendpoint" bson:"serverendpoint"`
+	ServerPort     string             `json:"serverport" bson:"serverport"`
+	ServerKey      string             `json:"serverkey" bson:"serverkey"`
+	IsServer       string             `json:"isserver" bson:"isserver"`
+}

+ 17 - 0
models/wglink.go

@@ -0,0 +1,17 @@
+package models
+
+import (
+        "github.com/vishvananda/netlink"
+)
+
+type WireGuardLink struct {
+	LinkAttrs *netlink.LinkAttrs
+}
+
+func (link *WireGuardLink) Type() string {
+	return "wireguard"
+}
+
+func (link *WireGuardLink) Attrs() *netlink.LinkAttrs {
+	return link.LinkAttrs
+}

+ 1 - 2
netclient/functions/auth.go → netclient/auth/auth.go

@@ -1,4 +1,4 @@
-package functions
+package auth
 
 
 import (
 import (
     "github.com/gravitl/netmaker/netclient/config"
     "github.com/gravitl/netmaker/netclient/config"
@@ -19,7 +19,6 @@ func SetJWT(client nodepb.NodeServiceClient, network string) (context.Context, e
 		home := "/etc/netclient"
 		home := "/etc/netclient"
 		tokentext, err := ioutil.ReadFile(home + "/nettoken-"+network)
 		tokentext, err := ioutil.ReadFile(home + "/nettoken-"+network)
                 if err != nil {
                 if err != nil {
-			fmt.Println("Error reading token. Logging in to retrieve new token.")
 			err = AutoLogin(client, network)
 			err = AutoLogin(client, network)
 			if err != nil {
 			if err != nil {
                                 return nil, status.Errorf(codes.Unauthenticated, fmt.Sprintf("Something went wrong with Auto Login: %v", err))
                                 return nil, status.Errorf(codes.Unauthenticated, fmt.Sprintf("Something went wrong with Auto Login: %v", err))

+ 100 - 0
netclient/command/commands.go

@@ -0,0 +1,100 @@
+package command
+
+import (
+        "github.com/gravitl/netmaker/netclient/functions"
+        "github.com/gravitl/netmaker/models"
+        "github.com/gravitl/netmaker/netclient/config"
+        "github.com/gravitl/netmaker/netclient/local"
+        "golang.zx2c4.com/wireguard/wgctrl"
+        nodepb "github.com/gravitl/netmaker/grpc"
+	"os"
+	"strings"
+	"log"
+)
+
+var (
+        wgclient *wgctrl.Client
+)
+
+var (
+        wcclient nodepb.NodeServiceClient
+)
+
+func Register(cfg config.ClientConfig) error {
+
+        err := functions.Register(cfg)
+        return err
+}
+
+func Join(cfg config.ClientConfig) error {
+
+	err := functions.JoinNetwork(cfg)
+	if err != nil {
+		 if !strings.Contains(err.Error(), "ALREADY_INSTALLED") {
+		 log.Println("Error installing: ", err)
+		 err = functions.LeaveNetwork(cfg.Network)
+		 if err != nil {
+			err = local.WipeLocal(cfg.Network)
+			if err != nil {
+				log.Println("Error removing artifacts: ", err)
+			}
+                        err = local.RemoveSystemDServices(cfg.Network)
+                        if err != nil {
+                                log.Println("Error removing services: ", err)
+                        }
+		}
+		os.Exit(1)
+		} else {
+			log.Println(err.Error())
+			os.Exit(1)
+		}
+	}
+        log.Println("joined " + cfg.Network)
+	if cfg.Daemon != "off" {
+		err = functions.Install(cfg)
+	        log.Println("installed daemon")
+	}
+	return err
+}
+
+func CheckIn(cfg config.ClientConfig) error {
+                        if cfg.Network == "nonetwork" || cfg.Network == "" {
+                                log.Println("Required, '-n'. No network provided. Exiting.")
+                                os.Exit(1)
+                        }
+			log.Println("Beginning node check in for network " + cfg.Network)
+			err := functions.CheckIn(cfg.Network)
+			if err != nil {
+				log.Println("Error checking in: ", err)
+				os.Exit(1)
+			}
+	return nil
+}
+
+func Leave(cfg config.ClientConfig) error {
+	err := functions.LeaveNetwork(cfg.Network)
+        if err != nil {
+		log.Println("Error attempting to leave network " + cfg.Network)
+        }
+	return err
+}
+
+func Push(cfg config.ClientConfig) error {
+	log.Println("pushing to network")
+	return nil
+}
+
+func Pull(cfg config.ClientConfig) error {
+        log.Println("pulling from network")
+        return nil
+}
+
+func Status(cfg config.ClientConfig) error {
+        log.Println("retrieving network status")
+        return nil
+}
+
+func Uninstall(cfg config.ClientConfig) error {
+        log.Println("uninstalling")
+        return nil
+}

+ 173 - 39
netclient/config/config.go

@@ -1,22 +1,24 @@
 package config
 package config
 
 
 import (
 import (
-//  "github.com/davecgh/go-spew/spew"
-  "os"
-  "errors"
-  "fmt"
-  "log"
-  "gopkg.in/yaml.v3"
-  //homedir "github.com/mitchellh/go-homedir"
+	//"github.com/davecgh/go-spew/spew"
+	"github.com/urfave/cli/v2"
+	"os"
+        "encoding/base64"
+	"errors"
+	"strings"
+	"fmt"
+	"log"
+	"gopkg.in/yaml.v3"
+	nodepb "github.com/gravitl/netmaker/grpc"
 )
 )
 
 
-//var Config *ClientConfig
-
-// Configurations exported
 type ClientConfig struct {
 type ClientConfig struct {
 	Server ServerConfig `yaml:"server"`
 	Server ServerConfig `yaml:"server"`
 	Node NodeConfig `yaml:"node"`
 	Node NodeConfig `yaml:"node"`
-	Network string
+	Network string `yaml:"network"`
+	Daemon string `yaml:"daemon"`
+	OperatingSystem string `yaml:"operatingsystem"`
 }
 }
 type ServerConfig struct {
 type ServerConfig struct {
         Address string `yaml:"address"`
         Address string `yaml:"address"`
@@ -32,11 +34,11 @@ type NodeConfig struct {
         LocalAddress string `yaml:"localaddress"`
         LocalAddress string `yaml:"localaddress"`
         WGAddress string `yaml:"wgaddress"`
         WGAddress string `yaml:"wgaddress"`
         WGAddress6 string `yaml:"wgaddress6"`
         WGAddress6 string `yaml:"wgaddress6"`
-        RoamingOff bool `yaml:"roamingoff"`
-        DNSOff bool `yaml:"dnsoff"`
-        IsLocal bool `yaml:"islocal"`
-        IsDualStack bool `yaml:"isdualstack"`
-        IsIngressGateway bool `yaml:"isingressgateway"`
+        Roaming string `yaml:"roaming"`
+        DNS string `yaml:"dns"`
+        IsLocal string `yaml:"islocal"`
+        IsDualStack string `yaml:"isdualstack"`
+        IsIngressGateway string `yaml:"isingressgateway"`
         AllowedIPs string `yaml:"allowedips"`
         AllowedIPs string `yaml:"allowedips"`
         LocalRange string `yaml:"localrange"`
         LocalRange string `yaml:"localrange"`
         PostUp string `yaml:"postup"`
         PostUp string `yaml:"postup"`
@@ -56,8 +58,6 @@ func Write(config *ClientConfig, network string) error{
 		err := errors.New("No network provided. Exiting.")
 		err := errors.New("No network provided. Exiting.")
 		return err
 		return err
 	}
 	}
-	nofile := false
-        //home, err := homedir.Dir()
         _, err := os.Stat("/etc/netclient") 
         _, err := os.Stat("/etc/netclient") 
 	if os.IsNotExist(err) {
 	if os.IsNotExist(err) {
 		      os.Mkdir("/etc/netclient", 744)
 		      os.Mkdir("/etc/netclient", 744)
@@ -71,29 +71,12 @@ func Write(config *ClientConfig, network string) error{
         }
         }
         file := fmt.Sprintf(home + "/netconfig-" + network)
         file := fmt.Sprintf(home + "/netconfig-" + network)
         f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
         f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
-        if err != nil {
-                nofile = true
-                //fmt.Println("Could not access " + home + "/netconfig,  proceeding...")
-        }
         defer f.Close()
         defer f.Close()
 
 
-        if !nofile {
-                err = yaml.NewEncoder(f).Encode(config)
-                if err != nil {
-                        fmt.Println("trouble writing file")
-                        return err
-                }
-        } else {
-
-		newf, err := os.Create(home + "/netconfig-" + network)
-		err = yaml.NewEncoder(newf).Encode(config)
-		defer newf.Close()
-		if err != nil {
-			return err
-		}
+	err = yaml.NewEncoder(f).Encode(config)
+	if err != nil {
+		return err
 	}
 	}
-
-
         return err
         return err
 }
 }
 func WriteServer(server string, accesskey string, network string) error{
 func WriteServer(server string, accesskey string, network string) error{
@@ -212,16 +195,158 @@ func(config *ClientConfig) ReadConfig() {
 	}
 	}
 }
 }
 
 
+func ModConfig(node *nodepb.Node) error{
+        network := node.Nodenetwork
+        if network == "" {
+                return errors.New("No Network Provided")
+        }
+	var modconfig ClientConfig
+	var err error
+	if FileExists("/etc/netclient/netconfig-"+network) {
+		useconfig, err := ReadConfig(network)
+		if err != nil {
+			return err
+		}
+		modconfig = *useconfig
+	}
+        nodecfg := modconfig.Node
+        if node.Name != ""{
+                nodecfg.Name = node.Name
+        }
+        if node.Interface != ""{
+                nodecfg.Interface = node.Interface
+        }
+        if node.Nodenetwork != ""{
+                nodecfg.Network = node.Nodenetwork
+        }
+        if node.Macaddress != ""{
+                nodecfg.MacAddress = node.Macaddress
+        }
+        if node.Localaddress != ""{
+                nodecfg.LocalAddress = node.Localaddress
+        }
+        if node.Postup != ""{
+                nodecfg.PostUp = node.Postup
+        }
+        if node.Postdown != ""{
+                nodecfg.PostDown = node.Postdown
+        }
+        if node.Listenport != 0{
+                nodecfg.Port = node.Listenport
+        }
+        if node.Keepalive != 0{
+                nodecfg.KeepAlive = node.Keepalive
+        }
+        if node.Publickey != ""{
+                nodecfg.PublicKey = node.Publickey
+        }
+        if node.Endpoint != ""{
+                nodecfg.Endpoint = node.Endpoint
+        }
+        if node.Password != ""{
+                nodecfg.Password = node.Password
+        }
+        if node.Address != ""{
+                nodecfg.WGAddress = node.Address
+        }
+        if node.Address6 != ""{
+                nodecfg.WGAddress6 = node.Address6
+        }
+        if node.Postchanges != "" {
+                nodecfg.PostChanges = node.Postchanges
+        }
+        if node.Dnsoff == true {
+		nodecfg.DNS = "off"
+        }
+        if node.Isdualstack == true {
+                nodecfg.IsDualStack = "yes"
+        }
+	if node.Isingressgateway {
+		nodecfg.IsIngressGateway = "yes"
+	} else {
+                nodecfg.IsIngressGateway = "no"
+	}
+        if node.Localrange != "" && node.Islocal {
+                nodecfg.IsLocal = "yes"
+                nodecfg.LocalRange = node.Localrange
+        }
+        modconfig.Node = nodecfg
+        err = Write(&modconfig, network)
+        return err
+}
+
+func GetCLIConfig(c *cli.Context) (ClientConfig, error){
+	var cfg ClientConfig
+	if c.String("token") != "" {
+                tokenbytes, err := base64.StdEncoding.DecodeString(c.String("token"))
+                if err  != nil {
+			log.Println("error decoding token")
+			return cfg, err
+                }
+                token := string(tokenbytes)
+                tokenvals := strings.Split(token, "|")
+                cfg.Server.Address = tokenvals[0]
+                cfg.Network = tokenvals[1]
+                cfg.Node.Network = tokenvals[1]
+                cfg.Server.AccessKey = tokenvals[2]
+                cfg.Node.LocalRange = tokenvals[3]
+
+		if c.String("server") != "" {
+			cfg.Server.Address = c.String("server")
+		}
+		if c.String("key") != "" {
+			cfg.Server.AccessKey = c.String("key")
+		}
+		if c.String("network") != "all" {
+			cfg.Network = c.String("network")
+			cfg.Node.Network = c.String("network")
+		}
+		if c.String("localrange") != "" {
+			cfg.Node.LocalRange = c.String("localrange")
+		}
+	} else {
+		cfg.Server.Address = c.String("server")
+		cfg.Server.AccessKey = c.String("key")
+                cfg.Network = c.String("network")
+                cfg.Node.Network = c.String("network")
+                cfg.Node.LocalRange = c.String("localrange")
+	}
+	cfg.Node.Name = c.String("name")
+	cfg.Node.Interface = c.String("interface")
+	cfg.Node.Password = c.String("password")
+	cfg.Node.MacAddress = c.String("macaddress")
+	cfg.Node.LocalAddress = c.String("localaddress")
+	cfg.Node.LocalRange = c.String("localrange")
+	cfg.Node.WGAddress = c.String("address")
+	cfg.Node.WGAddress6 = c.String("addressIPV6")
+	cfg.Node.Roaming = c.String("")
+	cfg.Node.DNS = c.String("")
+	cfg.Node.IsLocal = c.String("")
+	cfg.Node.IsDualStack = c.String("")
+	cfg.Node.IsIngressGateway = c.String("")
+	cfg.Node.PostUp = c.String("")
+	cfg.Node.PostDown = c.String("")
+	cfg.Node.Port = int32(c.Int(""))
+	cfg.Node.KeepAlive = int32(c.Int(""))
+	cfg.Node.PublicKey = c.String("")
+	cfg.Node.PrivateKey = c.String("")
+	cfg.Node.Endpoint = c.String("")
+	cfg.Node.IPForwarding = c.String("")
+
+	return cfg, nil
+}
+
+
 func ReadConfig(network string) (*ClientConfig, error) {
 func ReadConfig(network string) (*ClientConfig, error) {
         if network == "" {
         if network == "" {
                 err := errors.New("No network provided. Exiting.")
                 err := errors.New("No network provided. Exiting.")
                 return nil, err
                 return nil, err
         }
         }
 	nofile := false
 	nofile := false
-	//home, err := homedir.Dir()
 	home := "/etc/netclient"
 	home := "/etc/netclient"
 	file := fmt.Sprintf(home + "/netconfig-" + network)
 	file := fmt.Sprintf(home + "/netconfig-" + network)
 	f, err := os.Open(file)
 	f, err := os.Open(file)
+
 	if err != nil {
 	if err != nil {
 		nofile = true
 		nofile = true
 	}
 	}
@@ -239,3 +364,12 @@ func ReadConfig(network string) (*ClientConfig, error) {
 	}
 	}
 	return &cfg, err
 	return &cfg, err
 }
 }
+
+func FileExists(f string) bool {
+    info, err := os.Stat(f)
+    if os.IsNotExist(err) {
+        return false
+    }
+    return !info.IsDir()
+}
+

BIN
netclient/functions/.register.go.swp


+ 286 - 0
netclient/functions/checkin.go

@@ -0,0 +1,286 @@
+package functions
+
+import (
+	"fmt"
+	"context"
+	"strings"
+	"log"
+	"net"
+	"os/exec"
+        "github.com/gravitl/netmaker/netclient/config"
+        "github.com/gravitl/netmaker/netclient/wireguard"
+        "github.com/gravitl/netmaker/netclient/server"
+        "github.com/gravitl/netmaker/netclient/auth"
+        nodepb "github.com/gravitl/netmaker/grpc"
+        "google.golang.org/grpc"
+	"google.golang.org/grpc/metadata"
+	//homedir "github.com/mitchellh/go-homedir"
+)
+
+func CheckIn(network string) error {
+	node := server.GetNode(network)
+        cfg, err := config.ReadConfig(network)
+        if err != nil {
+                return err
+        }
+	nodecfg := cfg.Node
+	servercfg := cfg.Server
+	fmt.Println("Checking into server: " + servercfg.Address)
+
+	setupcheck := true
+	ipchange := false
+
+	if !(nodecfg.IPForwarding == "off") {
+		out, err := exec.Command("sysctl", "net.ipv4.ip_forward").Output()
+                 if err != nil {
+	                 fmt.Println(err)
+			 fmt.Println("WARNING: Error encountered setting ip forwarding. This can break functionality.")
+                 } else {
+                         s := strings.Fields(string(out))
+                         if s[2] != "1" {
+				_, err = exec.Command("sysctl", "-w", "net.ipv4.ip_forward=1").Output()
+				if err != nil {
+					fmt.Println(err)
+					fmt.Println("WARNING: Error encountered setting ip forwarding. You may want to investigate this.")
+				}
+			}
+		}
+	}
+
+	if nodecfg.Roaming != "off" {
+		if nodecfg.IsLocal != "yes" {
+		fmt.Println("Checking to see if public addresses have changed")
+		extIP, err := getPublicIP()
+		if err != nil {
+			fmt.Printf("Error encountered checking ip addresses: %v", err)
+		}
+		if nodecfg.Endpoint != extIP  && extIP != "" {
+	                fmt.Println("Endpoint has changed from " +
+			nodecfg.Endpoint + " to " + extIP)
+			fmt.Println("Updating address")
+			nodecfg.Endpoint = extIP
+			nodecfg.PostChanges = "true"
+			node.Endpoint = extIP
+			node.Postchanges = "true"
+			ipchange = true
+		}
+		intIP, err := getPrivateAddr()
+                if err != nil {
+                        fmt.Printf("Error encountered checking ip addresses: %v", err)
+                }
+                if nodecfg.LocalAddress != intIP  && intIP != "" {
+                        fmt.Println("Local Address has changed from " +
+			nodecfg.LocalAddress + " to " + intIP)
+			fmt.Println("Updating address")
+			nodecfg.LocalAddress = intIP
+			nodecfg.PostChanges = "true"
+			node.Localaddress = intIP
+			node.Postchanges = "true"
+			ipchange = true
+                }
+		} else {
+                fmt.Println("Checking to see if local addresses have changed")
+                localIP, err := getLocalIP(nodecfg.LocalRange)
+                if err != nil {
+                        fmt.Printf("Error encountered checking ip addresses: %v", err)
+                }
+                if nodecfg.Endpoint != localIP  && localIP != "" {
+                        fmt.Println("Endpoint has changed from " +
+                        nodecfg.Endpoint + " to " + localIP)
+                        fmt.Println("Updating address")
+                        nodecfg.Endpoint = localIP
+                        nodecfg.LocalAddress = localIP
+                        nodecfg.PostChanges = "true"
+                        node.Endpoint = localIP
+                        node.Localaddress = localIP
+                        node.Postchanges = "true"
+                        ipchange = true
+                }
+		}
+		if node.Postchanges != "true" {
+			fmt.Println("Addresses have not changed.")
+		}
+	}
+	if ipchange {
+		err := config.ModConfig(&node)
+                if err != nil {
+                        return err
+                        log.Fatalf("Error: %v", err)
+                }
+                err = wireguard.SetWGConfig(network)
+                if err != nil {
+                        return err
+                        log.Fatalf("Error: %v", err)
+                }
+	        node = server.GetNode(network)
+		cfg, err := config.ReadConfig(network)
+		if err != nil {
+			return err
+		}
+		nodecfg = cfg.Node
+	}
+
+
+        var wcclient nodepb.NodeServiceClient
+        var requestOpts grpc.DialOption
+        requestOpts = grpc.WithInsecure()
+        conn, err := grpc.Dial(servercfg.Address, requestOpts)
+        if err != nil {
+		fmt.Printf("Cant dial GRPC server: %v", err)
+		return err
+        }
+        wcclient = nodepb.NewNodeServiceClient(conn)
+
+        ctx := context.Background()
+        fmt.Println("Authenticating with GRPC Server")
+        ctx, err = auth.SetJWT(wcclient, network)
+        if err != nil {
+                fmt.Printf("Failed to authenticate: %v", err)
+		return err
+	}
+        fmt.Println("Authenticated")
+        fmt.Println("Checking In.")
+
+        var header metadata.MD
+	node.Nodenetwork = network
+        checkinres, err := wcclient.CheckIn(
+                ctx,
+                &nodepb.CheckInReq{
+                        Node: &node,
+                },
+		grpc.Header(&header),
+        )
+        if err != nil {
+        if  checkinres != nil && checkinres.Checkinresponse.Ispending {
+                fmt.Println("Node is in pending status. Waiting for Admin approval of  node before making further updates.")
+                return nil
+        }
+                fmt.Printf("Unable to process Check In request: %v", err)
+		return err
+        }
+	fmt.Println("Checked in.")
+	if  checkinres.Checkinresponse.Ispending {
+		fmt.Println("Node is in pending status. Waiting for Admin approval of  node before making further updates.")
+		return err
+	}
+
+                newinterface := server.GetNode(network).Interface
+                readreq := &nodepb.ReadNodeReq{
+                        Macaddress: node.Macaddress,
+                        Network: node.Nodenetwork,
+                }
+                readres, err := wcclient.ReadNode(ctx, readreq, grpc.Header(&header))
+                if err != nil {
+                        fmt.Printf("Error: %v", err)
+                } else {
+                currentiface := readres.Node.Interface
+                ifaceupdate := newinterface != currentiface
+                if err != nil {
+                        log.Printf("Error retrieving interface: %v", err)
+                }
+                if ifaceupdate {
+			fmt.Println("Interface update: " + currentiface +
+			" >>>> " + newinterface)
+                        err := DeleteInterface(currentiface, nodecfg.PostDown)
+                        if err != nil {
+                                fmt.Println("ERROR DELETING INTERFACE: " + currentiface)
+                        }
+                err = wireguard.SetWGConfig(network)
+                if err != nil {
+                        log.Printf("Error updating interface: %v", err)
+                }
+		}
+		}
+
+	if checkinres.Checkinresponse.Needconfigupdate {
+		fmt.Println("Server has requested that node update config.")
+		fmt.Println("Updating config from remote server.")
+                req := &nodepb.ReadNodeReq{
+                        Macaddress: node.Macaddress,
+                        Network: node.Nodenetwork,
+                }
+                readres, err := wcclient.ReadNode(ctx, req, grpc.Header(&header))
+                if err != nil {
+			return err
+                        log.Fatalf("Error: %v", err)
+                }
+                err = config.ModConfig(readres.Node)
+                if err != nil {
+			return err
+                        log.Fatalf("Error: %v", err)
+                }
+                err = wireguard.SetWGConfig(network)
+                if err != nil {
+			return err
+                        log.Fatalf("Error: %v", err)
+                }
+		setupcheck = false
+	} else if nodecfg.PostChanges == "true" {
+                fmt.Println("Node has requested to update remote config.")
+                fmt.Println("Posting local config to remote server.")
+		postnode := server.GetNode(network)
+
+		req := &nodepb.UpdateNodeReq{
+                               Node: &postnode,
+                        }
+		res, err := wcclient.UpdateNode(ctx, req, grpc.Header(&header))
+                if err != nil {
+			return err
+			log.Fatalf("Error: %v", err)
+                }
+		res.Node.Postchanges = "false"
+		err = config.ModConfig(res.Node)
+                if err != nil {
+			return err
+                        log.Fatalf("Error: %v", err)
+                }
+		err = wireguard.SetWGConfig(network)
+                if err != nil {
+			return err
+                        log.Fatalf("Error: %v", err)
+                }
+		setupcheck = false
+	}
+        if checkinres.Checkinresponse.Needkeyupdate {
+                fmt.Println("Server has requested that node update key pairs.")
+                fmt.Println("Proceeding to re-generate key pairs for Wiregard.")
+                err = wireguard.SetWGKeyConfig(network, servercfg.Address)
+                if err != nil {
+                        return err
+                        log.Fatalf("Unable to process reset keys request: %v", err)
+                }
+                setupcheck = false
+        }
+        if checkinres.Checkinresponse.Needpeerupdate {
+                fmt.Println("Server has requested that node update peer list.")
+                fmt.Println("Updating peer list from remote server.")
+                err = wireguard.SetWGConfig(network)
+                if err != nil {
+			return err
+                        log.Fatalf("Unable to process Set Peers request: %v", err)
+                }
+		setupcheck = false
+        }
+	if checkinres.Checkinresponse.Needdelete {
+		fmt.Println("This machine got the delete signal. Deleting.")
+                err := LeaveNetwork(network)
+                if err != nil {
+                        return err
+                        log.Fatalf("Error: %v", err)
+                }
+	}
+	if setupcheck {
+	iface := nodecfg.Interface
+	_, err := net.InterfaceByName(iface)
+        if err != nil {
+		fmt.Println("interface " + iface + " does not currently exist. Setting up WireGuard.")
+                err = wireguard.SetWGKeyConfig(network, servercfg.Address)
+                if err != nil {
+                        return err
+                        log.Fatalf("Error: %v", err)
+                }
+	}
+	}
+	return nil
+}
+

File diff suppressed because it is too large
+ 77 - 1146
netclient/functions/common.go


+ 21 - 0
netclient/functions/install.go

@@ -0,0 +1,21 @@
+package functions
+
+import (
+        "github.com/gravitl/netmaker/netclient/config"
+        "github.com/gravitl/netmaker/netclient/local"
+)
+
+func Install(cfg config.ClientConfig) error {
+
+	var err error
+	err = local.ConfigureSystemD(cfg.Network)
+	return err
+}
+
+func getOS() (config.ClientConfig, error) {
+
+	var cfg config.ClientConfig
+
+	return cfg, nil
+}
+

+ 237 - 0
netclient/functions/join.go

@@ -0,0 +1,237 @@
+package functions
+
+import (
+	"fmt"
+	"errors"
+	"context"
+	"log"
+	"net"
+	"strconv"
+        "github.com/gravitl/netmaker/netclient/config"
+        "github.com/gravitl/netmaker/netclient/wireguard"
+        "github.com/gravitl/netmaker/netclient/server"
+        "github.com/gravitl/netmaker/netclient/local"
+        nodepb "github.com/gravitl/netmaker/grpc"
+	"golang.zx2c4.com/wireguard/wgctrl"
+        "google.golang.org/grpc"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+	//homedir "github.com/mitchellh/go-homedir"
+)
+
+func JoinNetwork(cfg config.ClientConfig) error {
+
+	hasnet := local.HasNetwork(cfg.Network)
+	if hasnet { 
+		   err := errors.New("ALREADY_INSTALLED. Netclient appears to already be installed for cfg.Network " + cfg.Network + ". To re-install, please remove by executing 'sudo netclient -c remove -n " + cfg.Network + "'. Then re-run the install command.")
+		return err
+	}
+	err := config.Write(&cfg, cfg.Network)
+	if err != nil {
+		return err
+	}
+
+        wgclient, err := wgctrl.New()
+        if err != nil {
+		return err
+        }
+        defer wgclient.Close()
+
+	if cfg.Node.LocalRange != "" {
+	if cfg.Node.LocalAddress == "" {
+		ifaces, err := net.Interfaces()
+                if err != nil {
+                        return err
+                }
+		_, localrange, err := net.ParseCIDR(cfg.Node.LocalRange)
+                if err != nil {
+                        return err
+                }
+
+		var local string
+		found := false
+		for _, i := range ifaces {
+			if i.Flags&net.FlagUp == 0 {
+				continue // interface down
+			}
+			if i.Flags&net.FlagLoopback != 0 {
+				continue // loopback interface
+			}
+			addrs, err := i.Addrs()
+			if err != nil {
+				return err
+			}
+			for _, addr := range addrs {
+				var ip net.IP
+				switch v := addr.(type) {
+				case *net.IPNet:
+					if !found {
+						ip = v.IP
+						local = ip.String()
+						if cfg.Node.IsLocal == "yes" {
+							found = localrange.Contains(ip)
+						} else {
+							found = true
+						}
+					}
+				case *net.IPAddr:
+					if  !found {
+						ip = v.IP
+						local = ip.String()
+						if cfg.Node.IsLocal == "yes" {
+							found = localrange.Contains(ip)
+
+						} else {
+							found = true
+						}
+					}
+				}
+			}
+		}
+		cfg.Node.LocalAddress = local
+	}
+	}
+        if cfg.Node.Endpoint == "" {
+		if cfg.Node.IsLocal == "yes" && cfg.Node.LocalAddress != "" {
+			cfg.Node.Endpoint = cfg.Node.LocalAddress
+		} else {
+
+			cfg.Node.Endpoint, err = getPublicIP()
+			if err != nil {
+				fmt.Println("Error setting cfg.Node.Endpoint.")
+				return err
+			}
+		}
+        } else {
+                cfg.Node.Endpoint = cfg.Node.Endpoint
+		fmt.Println("Endpoint set in config. Setting to address: " + cfg.Node.Endpoint)
+        }
+	if cfg.Node.PrivateKey == "" {
+		privatekey, err := wgtypes.GeneratePrivateKey()
+		if err != nil {
+			log.Fatal(err)
+		}
+		cfg.Node.PrivateKey = privatekey.String()
+		cfg.Node.PublicKey = privatekey.PublicKey().String()
+	}
+
+	if cfg.Node.MacAddress == "" {
+		macs, err := getMacAddr()
+		if err != nil {
+			return err
+		} else if len(macs) == 0 {
+			log.Fatal()
+		} else {
+			cfg.Node.MacAddress  = macs[0]
+		}
+	}
+
+	var wcclient nodepb.NodeServiceClient
+	var requestOpts grpc.DialOption
+        requestOpts = grpc.WithInsecure()
+        conn, err := grpc.Dial(cfg.Server.Address, requestOpts)
+        if err != nil {
+                log.Fatalf("Unable to establish client connection to localhost:50051: %v", err)
+        }
+        wcclient = nodepb.NewNodeServiceClient(conn)
+
+        postnode := &nodepb.Node{
+                Password: cfg.Node.Password,
+                Macaddress: cfg.Node.MacAddress,
+                Accesskey: cfg.Server.AccessKey,
+                Nodenetwork:  cfg.Network,
+                Listenport: cfg.Node.Port,
+                Postup: cfg.Node.PostUp,
+                Postdown: cfg.Node.PostDown,
+                Keepalive: cfg.Node.KeepAlive,
+		Localaddress: cfg.Node.LocalAddress,
+		Interface: cfg.Node.Interface,
+                Publickey: cfg.Node.PublicKey,
+                Name: cfg.Node.Name,
+                Endpoint: cfg.Node.Endpoint,
+        }
+        err = config.ModConfig(postnode)
+        if err != nil {
+		return err
+        }
+        res, err := wcclient.CreateNode(
+                context.TODO(),
+                &nodepb.CreateNodeReq{
+                        Node: postnode,
+                },
+        )
+        if err != nil {
+                return err
+        }
+        node := res.Node
+        if err != nil {
+                return err
+        }
+
+       fmt.Println("Node Settings: ")
+       fmt.Println("     Password: " + node.Password)
+       fmt.Println("     WG Address: " + node.Address)
+       fmt.Println("     WG ipv6 Address: " + node.Address6)
+       fmt.Println("     Network: " + node.Nodenetwork)
+       fmt.Println("     Public  Endpoint: " + node.Endpoint)
+       fmt.Println("     Local Address: " + node.Localaddress)
+       fmt.Println("     Name: " + node.Name)
+       fmt.Println("     Interface: " + node.Interface)
+       fmt.Println("     PostUp: " + node.Postup)
+       fmt.Println("     PostDown: " + node.Postdown)
+       fmt.Println("     Port: " + strconv.FormatInt(int64(node.Listenport), 10))
+       fmt.Println("     KeepAlive: " + strconv.FormatInt(int64(node.Keepalive), 10))
+       fmt.Println("     Public Key: " + node.Publickey)
+       fmt.Println("     Mac Address: " + node.Macaddress)
+       fmt.Println("     Is Local?: " + strconv.FormatBool(node.Islocal))
+       fmt.Println("     Is Dual Stack?: " + strconv.FormatBool(node.Isdualstack))
+       fmt.Println("     Is Ingress Gateway?: " + strconv.FormatBool(node.Isingressgateway))
+       fmt.Println("     Local Range: " + node.Localrange)
+
+       if node.Dnsoff==true  {
+		cfg.Node.DNS = "yes"
+	}
+	if !(cfg.Node.IsLocal == "yes") && node.Islocal && node.Localrange != "" {
+		node.Localaddress, err = getLocalIP(node.Localrange)
+		if err != nil {
+			return err
+		}
+		node.Endpoint = node.Localaddress
+	}
+
+        err = config.ModConfig(node)
+        if err != nil {
+                return err
+        }
+
+	if node.Ispending {
+		fmt.Println("Node is marked as PENDING.")
+		fmt.Println("Awaiting approval from Admin before configuring WireGuard.")
+	        if cfg.Daemon != "no" {
+			fmt.Println("Configuring Netmaker Service.")
+			err = local.ConfigureSystemD(cfg.Network)
+			return err
+		}
+	}
+
+	peers, hasGateway, gateways, err := server.GetPeers(node.Macaddress, cfg.Network, cfg.Server.Address, node.Isdualstack, node.Isingressgateway)
+
+	if err != nil {
+                return err
+        }
+	err = wireguard.StorePrivKey(cfg.Node.PrivateKey, cfg.Network)
+        if err != nil {
+                return err
+        }
+	err = wireguard.InitWireguard(node, cfg.Node.PrivateKey, peers, hasGateway, gateways)
+        if err != nil {
+                return err
+        }
+	if cfg.Daemon == "off" {
+		err = local.ConfigureSystemD(cfg.Network)
+	}
+        if err != nil {
+                return err
+        }
+
+	return err
+}

+ 114 - 0
netclient/functions/register.go

@@ -0,0 +1,114 @@
+package functions
+
+import (
+	"fmt"
+	"errors"
+	"context"
+	"log"
+	"net"
+	"strconv"
+        "github.com/gravitl/netmaker/netclient/config"
+        "github.com/gravitl/netmaker/netclient/wireguard"
+        "github.com/gravitl/netmaker/netclient/server"
+        "github.com/gravitl/netmaker/netclient/local"
+        nodepb "github.com/gravitl/netmaker/grpc"
+	"golang.zx2c4.com/wireguard/wgctrl"
+        "google.golang.org/grpc"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+	//homedir "github.com/mitchellh/go-homedir"
+)
+
+func Register(cfg config.ClientConfig) error {
+
+        if err != nil {
+                log.Fatalf("Unable to establish client connection to localhost:50051: %v", err)
+        }
+        wcclient = nodepb.NewNodeServiceClient(conn)
+
+        postclient := &models.ServerClient{
+                AccessKey: cfg.Server.AccessKey,
+                Publickey: cfg.Node.PublicKey,
+                Privatekey: cfg.Node.PublicKey,
+		Address: cfg.Node.Address,
+		Address6: cfg.Node.Address6,
+		Network: "comms"
+	}
+	bytes, err := json.Marshal(postclient)
+	body := bytes.NewBuffer(bytes)
+	res, err := http.Post("http://"+cfg.Server.Address+""jsonplaceholder.typicode.com/posts/1")
+        if err != nil {
+                return err
+        }
+        node := res.Node
+        if err != nil {
+                return err
+        }
+
+       fmt.Println("Node Settings: ")
+       fmt.Println("     Password: " + node.Password)
+       fmt.Println("     WG Address: " + node.Address)
+       fmt.Println("     WG ipv6 Address: " + node.Address6)
+       fmt.Println("     Network: " + node.Nodenetwork)
+       fmt.Println("     Public  Endpoint: " + node.Endpoint)
+       fmt.Println("     Local Address: " + node.Localaddress)
+       fmt.Println("     Name: " + node.Name)
+       fmt.Println("     Interface: " + node.Interface)
+       fmt.Println("     PostUp: " + node.Postup)
+       fmt.Println("     PostDown: " + node.Postdown)
+       fmt.Println("     Port: " + strconv.FormatInt(int64(node.Listenport), 10))
+       fmt.Println("     KeepAlive: " + strconv.FormatInt(int64(node.Keepalive), 10))
+       fmt.Println("     Public Key: " + node.Publickey)
+       fmt.Println("     Mac Address: " + node.Macaddress)
+       fmt.Println("     Is Local?: " + strconv.FormatBool(node.Islocal))
+       fmt.Println("     Is Dual Stack?: " + strconv.FormatBool(node.Isdualstack))
+       fmt.Println("     Is Ingress Gateway?: " + strconv.FormatBool(node.Isingressgateway))
+       fmt.Println("     Local Range: " + node.Localrange)
+
+       if node.Dnsoff==true  {
+		cfg.Node.DNS = "yes"
+	}
+	if !(cfg.Node.IsLocal == "yes") && node.Islocal && node.Localrange != "" {
+		node.Localaddress, err = getLocalIP(node.Localrange)
+		if err != nil {
+			return err
+		}
+		node.Endpoint = node.Localaddress
+	}
+
+        err = config.ModConfig(node)
+        if err != nil {
+                return err
+        }
+
+	if node.Ispending {
+		fmt.Println("Node is marked as PENDING.")
+		fmt.Println("Awaiting approval from Admin before configuring WireGuard.")
+	        if cfg.Daemon != "no" {
+			fmt.Println("Configuring Netmaker Service.")
+			err = local.ConfigureSystemD(cfg.Network)
+			return err
+		}
+	}
+
+	peers, hasGateway, gateways, err := server.GetPeers(node.Macaddress, cfg.Network, cfg.Server.Address, node.Isdualstack, node.Isingressgateway)
+
+	if err != nil {
+                return err
+        }
+	err = wireguard.StorePrivKey(cfg.Node.PrivateKey, cfg.Network)
+        if err != nil {
+                return err
+        }
+	err = wireguard.InitWireguard(node, cfg.Node.PrivateKey, peers, hasGateway, gateways)
+        if err != nil {
+                return err
+        }
+	if cfg.Daemon == "off" {
+		err = local.ConfigureSystemD(cfg.Network)
+	}
+        if err != nil {
+                return err
+        }
+
+	return err
+}

+ 68 - 35
netclient/functions/local.go → netclient/local/local.go

@@ -1,8 +1,9 @@
-package functions
+package local
 
 
 import (
 import (
         //"github.com/davecgh/go-spew/spew"
         //"github.com/davecgh/go-spew/spew"
-        "fmt"
+        "github.com/gravitl/netmaker/netclient/config"
+	"fmt"
         "io/ioutil"
         "io/ioutil"
 	"path/filepath"
 	"path/filepath"
         "io"
         "io"
@@ -12,6 +13,19 @@ import (
         "os/exec"
         "os/exec"
 )
 )
 
 
+func RunCmds(commands []string) error {
+        var err error
+        for _, command := range commands {
+                fmt.Println("Running command: " + command)
+                args := strings.Fields(command)
+                out, err := exec.Command(args[0], args[1:]...).Output()
+                fmt.Println(string(out))
+                if err != nil {
+                        return err
+                }
+        }
+        return err
+}
 
 
 func FileExists(f string) bool {
 func FileExists(f string) bool {
     info, err := os.Stat(f)
     info, err := os.Stat(f)
@@ -92,7 +106,7 @@ Wants=netclient.timer
 
 
 [Service]
 [Service]
 Type=simple
 Type=simple
-ExecStart=/etc/netclient/netclient -c checkin -n %i
+ExecStart=/etc/netclient/netclient checkin -n %i
 
 
 [Install]
 [Install]
 WantedBy=multi-user.target
 WantedBy=multi-user.target
@@ -213,38 +227,11 @@ func RemoveSystemDServices(network string) error {
 		fmt.Println(err)
 		fmt.Println(err)
 	}
 	}
 
 
-	cmdSysDisableService := exec.Command("systemctl","disable","[email protected]")/* &exec.Cmd {
-                Path: sysExec,
-                Args: []string{ sysExec, "disable", "[email protected]"},
-                Stdout: os.Stdout,
-                Stderr: os.Stdout,
-        }*/
-        cmdSysDaemonReload := exec.Command("systemctl","daemon-reload")/*&exec.Cmd {
-                Path: sysExec,
-                Args: []string{ sysExec, "daemon-reload"},
-                Stdout: os.Stdout,
-                Stderr: os.Stdout,
-        }*/
-        cmdSysResetFailed := exec.Command("systemctl","reset-failed")/*&exec.Cmd {
-                Path: sysExec,
-                Args: []string{ sysExec, "reset-failed"},
-                Stdout: os.Stdout,
-                Stderr: os.Stdout,
-        }*/
-        cmdSysStopTimer := exec.Command("systemctl", "stop", "netclient-"+network+".timer")/*&exec.Cmd {
-                Path: sysExec,
-                Args: []string{ sysExec, "stop", "netclient-"+network+".timer" },
-                Stdout: os.Stdout,
-                Stderr: os.Stdout,
-        }*/
-        cmdSysDisableTimer :=  exec.Command("systemctl", "disable", "netclient-"+network+".timer")/*&exec.Cmd {
-                Path: sysExec,
-                Args: []string{ sysExec, "disable", "netclient-"+network+".timer"},
-                Stdout: os.Stdout,
-                Stderr: os.Stdout,
-        }*/
-
-        //err = cmdSysStopService.Run()
+	cmdSysDisableService := exec.Command("systemctl","disable","[email protected]")
+        cmdSysDaemonReload := exec.Command("systemctl","daemon-reload")
+        cmdSysResetFailed := exec.Command("systemctl","reset-failed")
+        cmdSysStopTimer := exec.Command("systemctl", "stop", "netclient-"+network+".timer")
+        cmdSysDisableTimer :=  exec.Command("systemctl", "disable", "netclient-"+network+".timer")
         if  err  !=  nil {
         if  err  !=  nil {
                 fmt.Println("Error stopping [email protected]. Please investigate.")
                 fmt.Println("Error stopping [email protected]. Please investigate.")
                 fmt.Println(err)
                 fmt.Println(err)
@@ -288,6 +275,52 @@ func RemoveSystemDServices(network string) error {
 
 
 }
 }
 
 
+func WipeLocal(network string) error{
+        cfg, err := config.ReadConfig(network)
+        if err != nil {
+                return err
+        }
+        nodecfg := cfg.Node
+        ifacename := nodecfg.Interface
+
+        //home, err := homedir.Dir()
+        home := "/etc/netclient"
+        _ = os.Remove(home + "/netconfig-" + network)
+        _ = os.Remove(home + "/nettoken-" + network)
+        _ = os.Remove(home + "/wgkey-" + network)
+
+        ipExec, err := exec.LookPath("ip")
+
+        if ifacename != "" {
+        cmdIPLinkDel := &exec.Cmd {
+                Path: ipExec,
+                Args: []string{ ipExec, "link", "del", ifacename },
+                Stdout: os.Stdout,
+                Stderr: os.Stdout,
+        }
+        err = cmdIPLinkDel.Run()
+        if  err  !=  nil {
+                fmt.Println(err)
+        }
+        if nodecfg.PostDown != "" {
+                runcmds := strings.Split(nodecfg.PostDown, "; ")
+                err = RunCmds(runcmds)
+                if err != nil {
+                        fmt.Println("Error encountered running PostDown: " + err.Error())
+                }
+        }
+        }
+        return err
+
+}
+
+func HasNetwork(network string) bool{
+
+return  FileExists("/etc/systemd/system/netclient-"+network+".timer") ||
+	FileExists("/etc/netclient/netconfig-"+network)
+
+}
+
 func copy(src, dst string) (int64, error) {
 func copy(src, dst string) (int64, error) {
         sourceFileStat, err := os.Stat(src)
         sourceFileStat, err := os.Stat(src)
         if err != nil {
         if err != nil {

+ 313 - 164
netclient/main.go

@@ -1,55 +1,318 @@
 package main
 package main
 
 
 import (
 import (
-	"fmt"
-        "github.com/gravitl/netmaker/netclient/functions"
-        "golang.zx2c4.com/wireguard/wgctrl"
-        nodepb "github.com/gravitl/netmaker/grpc"
-	"flag"
+	"errors"
+	"os/exec"
+	"strconv"
+	"github.com/urfave/cli/v2"
 	"os"
 	"os"
-        "os/exec"
-        "strconv"
-	"strings"
+	"github.com/gravitl/netmaker/netclient/command"
+	"github.com/gravitl/netmaker/netclient/config"
 	"log"
 	"log"
 )
 )
 
 
-const (
-	// name of the service
-	name        = "netclient"
-	description = "Netmaker Daemon Service"
-)
-
-var password string
-var network string
-var server string
-var accesskey string
-
-var (
-        wgclient *wgctrl.Client
-)
-
-var (
-        wcclient nodepb.NodeServiceClient
-)
-
 func main() {
 func main() {
-	tpassword := flag.String("p", "changeme", "This node's password for accessing the server regularly")
-	taccesskey := flag.String("k", "badkey", "an access key generated by the server and used for one-time access (install only)")
-	taccesstoken := flag.String("t", "badtoken", "an token generated by the server and used for one-time access (install only)")
-	tname := flag.String("name", "noname", "give the node a name at runtime")
-	tserver := flag.String("s", "localhost:50051", "The location (including port) of the remote gRPC server.")
-	tnetwork := flag.String("n", "nonetwork", "The node network you are attempting to join.")
-	tdnsoff := flag.Bool("dnsoff", false, "DNS Mode. If true, netclient will not alter system dns. false by default.")
-	tpublicip := flag.String("ip4", "nopubip", "The node network you are attempting to join.")
-	tnoauto := flag.Bool("na", false, "No auto mode. If true, netmclient will not be installed as a system service and you will have to retrieve updates manually via checkin command.")
-	tipforward := flag.String("nf", "on", "No Forward mode. If true, netclient will not check for IP forwarding. This may break functionality")
-	command := flag.String("c", "required", "The command to run")
-
-
-        flag.Parse()
+        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."
 
 
+    cliFlags := []cli.Flag{
+        &cli.StringFlag{
+            Name:  "network",
+            Aliases: []string{"n"},
+            EnvVars: []string{"NETCLIENT_NETWORK"},
+	    Value: "all",
+	    Usage: "Network to perform specified action against.",
+        },
+        &cli.StringFlag{
+            Name:  "password",
+            Aliases: []string{"p"},
+            EnvVars: []string{"NETCLIENT_PASSWORD"},
+            Value: "badpassword",
+            Usage: "Password for authenticating with netmaker.",
+        },
+        &cli.StringFlag{
+            Name:  "endpoint",
+            Aliases: []string{"e"},
+            EnvVars: []string{"NETCLIENT_ENDPOINT"},
+            Value: "",
+            Usage: "Reachable (usually public) address for WireGuard (not the private WG address).",
+        },
+        &cli.StringFlag{
+            Name:  "macaddress",
+            Aliases: []string{"m"},
+            EnvVars: []string{"NETCLIENT_MACADDRESS"},
+            Value: "",
+            Usage: "Mac Address for this machine. Used as a unique identifier within Netmaker network.",
+        },
+        &cli.StringFlag{
+            Name:  "publickey",
+            Aliases: []string{"pubkey"},
+            EnvVars: []string{"NETCLIENT_PUBLICKEY"},
+            Value: "",
+            Usage: "Public Key for WireGuard Interface.",
+        },
+        &cli.StringFlag{
+            Name:  "privatekey",
+            Aliases: []string{"privkey"},
+            EnvVars: []string{"NETCLIENT_PRIVATEKEY"},
+            Value: "",
+            Usage: "Private Key for WireGuard Interface.",
+        },
+        &cli.StringFlag{
+            Name:  "port",
+            EnvVars: []string{"NETCLIENT_PORT"},
+            Value: "",
+            Usage: "Port for WireGuard Interface.",
+        },
+        &cli.IntFlag{
+            Name:  "keepalive",
+            EnvVars: []string{"NETCLIENT_KEEPALIVE"},
+            Value: 0,
+            Usage: "Default PersistentKeepAlive for Peers in WireGuard Interface.",
+        },
+        &cli.StringFlag{
+            Name:  "name",
+            EnvVars: []string{"NETCLIENT_NAME"},
+            Value: "",
+            Usage: "Identifiable name for machine within Netmaker network.",
+        },
+        &cli.StringFlag{
+            Name:  "localaddress",
+            EnvVars: []string{"NETCLIENT_LOCALADDRESS"},
+            Value: "",
+            Usage: "Local address for machine. Can be used in place of Endpoint for machines on the same LAN.",
+        },
+        &cli.StringFlag{
+            Name:  "address",
+            Aliases: []string{"a"},
+            EnvVars: []string{"NETCLIENT_ADDRESS"},
+            Value: "",
+            Usage: "WireGuard address for machine within Netmaker network.",
+        },
+        &cli.StringFlag{
+            Name:  "addressIPv6",
+            Aliases: []string{"a6"},
+            EnvVars: []string{"NETCLIENT_ADDRESSIPV6"},
+            Value: "",
+            Usage: "WireGuard address for machine within Netmaker network.",
+        },
+        &cli.StringFlag{
+            Name:  "interface",
+            Aliases: []string{"i"},
+            EnvVars: []string{"NETCLIENT_INTERFACE"},
+            Value: "",
+            Usage: "WireGuard local network interface name.",
+        },
+        &cli.StringFlag{
+            Name:  "server",
+            Aliases: []string{"s"},
+            EnvVars: []string{"NETCLIENT_SERVER"},
+            Value: "",
+	    Usage: "Address + GRPC Port (e.g. 1.2.3.4:50051) of Netmaker server.",
+        },
+        &cli.StringFlag{
+            Name:  "key",
+            Aliases: []string{"k"},
+            EnvVars: []string{"NETCLIENT_ACCESSKEY"},
+            Value: "",
+            Usage: "Access Key for signing up machine with Netmaker server during initial 'add'.",
+        },
+        &cli.StringFlag{
+            Name:  "token",
+            Aliases: []string{"t"},
+            EnvVars: []string{"NETCLIENT_ACCESSTOKEN"},
+            Value: "",
+            Usage: "Access Token for signing up machine with Netmaker server during initial 'add'.",
+        },
+        &cli.StringFlag{
+            Name:  "localrange",
+            EnvVars: []string{"NETCLIENT_LOCALRANGE"},
+            Value: "",
+            Usage: "Local Range if network is local, for instance 192.168.1.0/24.",
+        },
+        &cli.StringFlag{
+            Name:  "roaming",
+            EnvVars: []string{"NETCLIENT_ROAMING"},
+            Value: "on",
+            Usage: "Checks for changes in IP address during updates if 'on'. Stays static if 'off'. On by default.",
+        },
+        &cli.StringFlag{
+            Name:  "dns",
+            EnvVars: []string{"NETCLIENT_DNS"},
+            Value: "",
+            Usage: "Sets private dns if 'on'. Ignores if 'off'. Will retrieve from network if unset.",
+        },
+        &cli.StringFlag{
+            Name:  "islocal",
+            EnvVars: []string{"NETCLIENT_IS_LOCAL"},
+            Value: "",
+            Usage: "Sets endpoint to local address if 'yes'. Ignores if 'no'. Will retrieve from network if unset.",
+        },
+        &cli.StringFlag{
+            Name:  "isdualstack",
+            EnvVars: []string{"NETCLIENT_IS_DUALSTACK"},
+            Value: "",
+            Usage: "Sets ipv6 address if 'yes'. Ignores if 'no'. Will retrieve from network if unset.",
+        },
+        &cli.StringFlag{
+            Name:  "ipforwarding",
+            EnvVars: []string{"NETCLIENT_IPFORWARDING"},
+            Value: "on",
+	    Usage: "Sets ip forwarding on if 'on'. Ignores if 'off'. On by default.",
+        },
+        &cli.StringFlag{
+            Name:  "postup",
+            EnvVars: []string{"NETCLIENT_POSTUP"},
+            Value: "",
+            Usage: "Sets PostUp command for WireGuard.",
+        },
+        &cli.StringFlag{
+            Name:  "postdown",
+            EnvVars: []string{"NETCLIENT_POSTDOWN"},
+            Value: "",
+            Usage: "Sets PostDown command for WireGuard.",
+        },
+        &cli.StringFlag{
+            Name:  "daemon",
+            EnvVars: []string{"NETCLIENT_DAEMON"},
+            Value: "on",
+            Usage: "Installs daemon if 'on'. Ignores if 'off'. On by default.",
+        },
+    }
 
 
+    app.Commands = []*cli.Command{
+        {
+            Name:  "register",
+            Usage: "Register with Netmaker Server for secure GRPC communications."
+            Flags: cliFlags,
+            Action: func(c *cli.Context) error {
+                cfg, err := config.GetCLIConfig(c)
+                if err != nil {
+                        return err
+                }
+                if cfg.Server.Address == "" {
+                        err = errors.New("No server address provided.")
+                        return err
+                }
+                err = command.Register(cfg)
+                return err
+            },
+        },
+	{
+            Name:  "join",
+            Usage: "Join a Netmaker network.",
+            Flags: cliFlags,
+            Action: func(c *cli.Context) error {
+		cfg, err := config.GetCLIConfig(c)
+		if err != nil {
+			return err
+		}
+		if cfg.Network == "all" {
+			err = errors.New("No network provided.")
+			return err
+		}
+                if cfg.Server.Address == "" {
+                        err = errors.New("No server address provided.")
+                        return err
+                }
+		err = command.Join(cfg)
+                return err
+            },
+        },
+        {
+            Name:  "leave",
+            Usage: "Leave a Netmaker network.",
+            Flags: cliFlags,
+            // the action, or code that will be executed when
+            // we execute our `ns` command
+            Action: func(c *cli.Context) error {
+                cfg, err := config.GetCLIConfig(c)
+                if err != nil {
+                        return err
+                }
+                err = command.Leave(cfg)
+                return err
+            },
+        },
+        {
+            Name:  "checkin",
+            Usage: "Checks for local changes and then checks into the specified Netmaker network to ask about remote changes.",
+            Flags: cliFlags,
+            // the action, or code that will be executed when
+            // we execute our `ns` command
+            Action: func(c *cli.Context) error {
+                cfg, err := config.GetCLIConfig(c)
+                if err != nil {
+                        return err
+                }
+                err = command.CheckIn(cfg)
+                return err
+            },
+        },
+        {
+            Name:  "push",
+            Usage: "Push configuration changes to server.",
+            Flags: cliFlags,
+            // the action, or code that will be executed when
+            // we execute our `ns` command
+            Action: func(c *cli.Context) error {
+                cfg, err := config.GetCLIConfig(c)
+                if err != nil {
+                        return err
+                }
+                err = command.Push(cfg)
+                return err
+            },
+        },
+        {
+            Name:  "pull",
+            Usage: "Pull latest configuration and peers from server.",
+            Flags: cliFlags,
+            // the action, or code that will be executed when
+            // we execute our `ns` command
+            Action: func(c *cli.Context) error {
+                cfg, err := config.GetCLIConfig(c)
+                if err != nil {
+                        return err
+                }
+                err = command.Pull(cfg)
+                return err
+            },
+        },
+        {
+            Name:  "status",
+            Usage: "Check network status.",
+            Flags: cliFlags,
+            // the action, or code that will be executed when
+            // we execute our `ns` command
+            Action: func(c *cli.Context) error {
+                cfg, err := config.GetCLIConfig(c)
+                if err != nil {
+                        return err
+                }
+                err = command.Status(cfg)
+                return err
+            },
+        },
+        {
+            Name:  "uninstall",
+            Usage: "Uninstall the netclient system service.",
+            Flags: cliFlags,
+            // the action, or code that will be executed when
+            // we execute our `ns` command
+            Action: func(c *cli.Context) error {
+                cfg, err := config.GetCLIConfig(c)
+                if err != nil {
+                        return err
+                }
+                err = command.Uninstall(cfg)
+                return err
+            },
+        },
+    }
 
 
+    // start our application
          getID := exec.Command("id", "-u")
          getID := exec.Command("id", "-u")
          out, err := getID.Output()
          out, err := getID.Output()
 
 
@@ -67,128 +330,14 @@ func main() {
          }
          }
 
 
 
 
-	_, err = exec.LookPath("wg")
-	if err != nil {
-		log.Println(err)
-		log.Fatal("WireGuard not installed. Please install WireGuard (wireguard-tools) and try again.")
-	}
-
-        switch *command {
-		case "getport":
-			portno, err := functions.GetFreePort(51821)
-			fmt.Printf("Port Number: %v", portno)
-			fmt.Println("")
-			if err != nil {
-				log.Fatal(err)
-			}
-		case "required":
-                        fmt.Println("command flag 'c' is required. Pick one of |install|checkin|update|remove|")
-                        os.Exit(1)
-			log.Fatal("Exiting")
-                case "install":
-
-                        if *taccesstoken == "badtoken" && (*tnetwork == "nonetwork"  || *tnetwork == "") {
-                                fmt.Println("Required, '-n'. No network provided. Exiting.")
-                                os.Exit(1)
-                        }
-			/*
-			if !*tnoforward {
-				forward := exec.Command("sysctl", "net.ipv4.ip_forward")
-				out, err := forward.Output()
-
-				if err != nil {
-					log.Fatal(err)
-				}
-				//s := strings.Split(string(out), " ", "\n")
-				s := strings.Fields(string(out))
-				if err != nil {
-					log.Fatal(err)
-				}
-				if s[2] != "1" {
-					log.Fatal("It is recommended to enable IP Forwarding. Current status is: " +  s[2] + ", but should be 1. if you would like to run without IP Forwarding, re-run with flag '-nf true'")
-				}
-			}
-			*/
-			fmt.Println("Beginning agent installation.")
-			err := functions.Install(*taccesskey, *tpassword, *tserver, *tnetwork, *tnoauto, *taccesstoken, *tname, *tpublicip, *tdnsoff, *tipforward)
-			if err != nil {
-				fmt.Println("Error encountered while installing.")
-				if !strings.Contains(err.Error(), "ALREADY_INSTALLED") {
-				fmt.Println("Error installing: ", err)
-				fmt.Println("Cleaning up (uninstall)")
-				err = functions.Remove(*tnetwork)
-				if err != nil {
-                                        fmt.Println("Error uninstalling: ", err)
-                                        fmt.Println("Wiping local.")
-					err = functions.WipeLocal(*tnetwork)
-					if err != nil {
-						fmt.Println("Error removing artifacts: ", err)
-					}
-                                        err = functions.RemoveSystemDServices(*tnetwork)
-                                        if err != nil {
-                                                fmt.Println("Error removing services: ", err)
-                                        }
-				}
-				os.Exit(1)
-				} else {
-					fmt.Println(err.Error())
-					os.Exit(1)
-				}
-			}
-		/*
-		case "service-install":
-                        fmt.Println("Beginning service installation.")
-                        err := functions.ConfigureSystemD()
-                        if err != nil {
-                                fmt.Println("Error installing service: ", err)
-                                os.Exit(1)
-                        }
-                case "service-uninstall":
-                        fmt.Println("Beginning service uninstall.")
-                        err := functions.RemoveSystemDServices()
-                        if err != nil {
-                                fmt.Println("Error installing service: ", err)
-                                os.Exit(1)
-                        }
-		*/
-		case "checkin":
-                        if *tnetwork == "nonetwork" || *tnetwork == "" {
-                                fmt.Println("Required, '-n'. No network provided. Exiting.")
-                                os.Exit(1)
-                        }
-			fmt.Println("Beginning node check in for network " + *tnetwork)
-			err := functions.CheckIn(*tnetwork)
-			if err != nil {
-				fmt.Println("Error checking in: ", err)
-				os.Exit(1)
-			}
-		case "remove":
-			if *tnetwork == "nonetwork" || *tnetwork == "" {
-                                fmt.Println("Required, '-n'. No network provided. Exiting.")
-                                os.Exit(1)
-			}
-                        fmt.Println("Beginning node cleanup.")
-			err := functions.Remove(*tnetwork)
-                        if err != nil {
-					/*
-                                        fmt.Println("Error uninstalling: ", err)
-                                        fmt.Println("Wiping local.")
-                                        err = functions.WipeLocal()
-                                        if err != nil {
-                                                fmt.Println("Error removing artifacts: ", err)
-                                        }
-                                        err = functions.RemoveSystemDServices()
-                                        if err != nil {
-                                                fmt.Println("Error removing services: ", err)
-                                        }
-					*/
-                                fmt.Println("Error deleting node: ", err)
-                                os.Exit(1)
-                        }
-		default:
-			fmt.Println("You must select from the following commands: install|remove|checkin", err)
-			os.Exit(1)
+        _, err = exec.LookPath("wg")
+        if err != nil {
+                log.Println(err)
+                log.Fatal("WireGuard not installed. Please install WireGuard (wireguard-tools) and try again.")
+        }
 
 
-	}
-	fmt.Println("Command " + *command + " Executed Successfully")
+    err = app.Run(os.Args)
+    if err != nil {
+        log.Fatal(err)
+    }
 }
 }

+ 196 - 0
netclient/main.go.old

@@ -0,0 +1,196 @@
+package main
+
+import (
+	"fmt"
+	"github.com/urfave/cli"
+        "github.com/gravitl/netmaker/netclient/functions"
+        "golang.zx2c4.com/wireguard/wgctrl"
+        nodepb "github.com/gravitl/netmaker/grpc"
+	"flag"
+	"os"
+        "os/exec"
+        "strconv"
+	"strings"
+	"log"
+)
+
+const (
+	// name of the service
+	name        = "netclient"
+	description = "Netmaker Daemon Service"
+)
+
+var password string
+var network string
+var server string
+var accesskey string
+
+var (
+        wgclient *wgctrl.Client
+)
+
+var (
+        wcclient nodepb.NodeServiceClient
+)
+
+func main() {
+
+	tpassword := flag.String("p", "changeme", "This node's password for accessing the server regularly")
+	taccesskey := flag.String("k", "badkey", "an access key generated by the server and used for one-time access (install only)")
+	taccesstoken := flag.String("t", "badtoken", "an token generated by the server and used for one-time access (install only)")
+	tname := flag.String("name", "noname", "give the node a name at runtime")
+	tserver := flag.String("s", "localhost:50051", "The location (including port) of the remote gRPC server.")
+	tnetwork := flag.String("n", "nonetwork", "The node network you are attempting to join.")
+	tdnsoff := flag.Bool("dnsoff", false, "DNS Mode. If true, netclient will not alter system dns. false by default.")
+	tpublicip := flag.String("ip4", "nopubip", "The node network you are attempting to join.")
+	tnoauto := flag.Bool("na", false, "No auto mode. If true, netmclient will not be installed as a system service and you will have to retrieve updates manually via checkin command.")
+	tipforward := flag.String("nf", "on", "No Forward mode. If true, netclient will not check for IP forwarding. This may break functionality")
+	command := flag.String("c", "required", "The command to run")
+
+
+        flag.Parse()
+
+
+
+         getID := exec.Command("id", "-u")
+         out, err := getID.Output()
+
+         if err != nil {
+                 log.Fatal(err)
+         }
+         id, err := strconv.Atoi(string(out[:len(out)-1]))
+
+         if err != nil {
+                 log.Fatal(err)
+         }
+
+         if id != 0 {
+                 log.Fatal("This program must be run with elevated privileges (sudo). This program installs a SystemD service and configures WireGuard and networking rules. Please re-run with sudo/root.")
+         }
+
+
+	_, err = exec.LookPath("wg")
+	if err != nil {
+		log.Println(err)
+		log.Fatal("WireGuard not installed. Please install WireGuard (wireguard-tools) and try again.")
+	}
+
+        switch *command {
+		case "getport":
+			portno, err := functions.GetFreePort(51821)
+			fmt.Printf("Port Number: %v", portno)
+			fmt.Println("")
+			if err != nil {
+				log.Fatal(err)
+			}
+		case "required":
+                        fmt.Println("command flag 'c' is required. Pick one of |install|checkin|update|remove|")
+                        os.Exit(1)
+			log.Fatal("Exiting")
+                case "install":
+
+                        if *taccesstoken == "badtoken" && (*tnetwork == "nonetwork"  || *tnetwork == "") {
+                                fmt.Println("Required, '-n'. No network provided. Exiting.")
+                                os.Exit(1)
+                        }
+			/*
+			if !*tnoforward {
+				forward := exec.Command("sysctl", "net.ipv4.ip_forward")
+				out, err := forward.Output()
+
+				if err != nil {
+					log.Fatal(err)
+				}
+				//s := strings.Split(string(out), " ", "\n")
+				s := strings.Fields(string(out))
+				if err != nil {
+					log.Fatal(err)
+				}
+				if s[2] != "1" {
+					log.Fatal("It is recommended to enable IP Forwarding. Current status is: " +  s[2] + ", but should be 1. if you would like to run without IP Forwarding, re-run with flag '-nf true'")
+				}
+			}
+			*/
+			fmt.Println("Beginning agent installation.")
+			err := functions.Install(*taccesskey, *tpassword, *tserver, *tnetwork, *tnoauto, *taccesstoken, *tname, *tpublicip, *tdnsoff, *tipforward)
+			if err != nil {
+				fmt.Println("Error encountered while installing.")
+				if !strings.Contains(err.Error(), "ALREADY_INSTALLED") {
+				fmt.Println("Error installing: ", err)
+				fmt.Println("Cleaning up (uninstall)")
+				err = functions.Remove(*tnetwork)
+				if err != nil {
+                                        fmt.Println("Error uninstalling: ", err)
+                                        fmt.Println("Wiping local.")
+					err = functions.WipeLocal(*tnetwork)
+					if err != nil {
+						fmt.Println("Error removing artifacts: ", err)
+					}
+                                        err = functions.RemoveSystemDServices(*tnetwork)
+                                        if err != nil {
+                                                fmt.Println("Error removing services: ", err)
+                                        }
+				}
+				os.Exit(1)
+				} else {
+					fmt.Println(err.Error())
+					os.Exit(1)
+				}
+			}
+		/*
+		case "service-install":
+                        fmt.Println("Beginning service installation.")
+                        err := functions.ConfigureSystemD()
+                        if err != nil {
+                                fmt.Println("Error installing service: ", err)
+                                os.Exit(1)
+                        }
+                case "service-uninstall":
+                        fmt.Println("Beginning service uninstall.")
+                        err := functions.RemoveSystemDServices()
+                        if err != nil {
+                                fmt.Println("Error installing service: ", err)
+                                os.Exit(1)
+                        }
+		*/
+		case "checkin":
+                        if *tnetwork == "nonetwork" || *tnetwork == "" {
+                                fmt.Println("Required, '-n'. No network provided. Exiting.")
+                                os.Exit(1)
+                        }
+			fmt.Println("Beginning node check in for network " + *tnetwork)
+			err := functions.CheckIn(*tnetwork)
+			if err != nil {
+				fmt.Println("Error checking in: ", err)
+				os.Exit(1)
+			}
+		case "remove":
+			if *tnetwork == "nonetwork" || *tnetwork == "" {
+                                fmt.Println("Required, '-n'. No network provided. Exiting.")
+                                os.Exit(1)
+			}
+                        fmt.Println("Beginning node cleanup.")
+			err := functions.Remove(*tnetwork)
+                        if err != nil {
+					/*
+                                        fmt.Println("Error uninstalling: ", err)
+                                        fmt.Println("Wiping local.")
+                                        err = functions.WipeLocal()
+                                        if err != nil {
+                                                fmt.Println("Error removing artifacts: ", err)
+                                        }
+                                        err = functions.RemoveSystemDServices()
+                                        if err != nil {
+                                                fmt.Println("Error removing services: ", err)
+                                        }
+					*/
+                                fmt.Println("Error deleting node: ", err)
+                                os.Exit(1)
+                        }
+		default:
+			fmt.Println("You must select from the following commands: install|remove|checkin", err)
+			os.Exit(1)
+
+	}
+	fmt.Println("Command " + *command + " Executed Successfully")
+}

+ 127 - 28
netclient/functions/peers.go → netclient/server/grpc.go

@@ -1,23 +1,132 @@
-package functions
+package server
 
 
 import (
 import (
-        "fmt"
-        "time"
-        "context"
-        "io"
-        "strings"
-        "log"
-        "net"
-        "strconv"
-        "github.com/gravitl/netmaker/netclient/config"
+	"fmt"
+	"context"
+	"log"
+	"strings"
+	"strconv"
+	"net"
+	"time"
+	"io"
+        "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+	"github.com/gravitl/netmaker/netclient/config"
+        "github.com/gravitl/netmaker/netclient/auth"
+        "github.com/gravitl/netmaker/netclient/local"
         nodepb "github.com/gravitl/netmaker/grpc"
         nodepb "github.com/gravitl/netmaker/grpc"
         "google.golang.org/grpc"
         "google.golang.org/grpc"
-        "google.golang.org/grpc/metadata"
-        "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
-        //homedir "github.com/mitchellh/go-homedir"
+	"google.golang.org/grpc/metadata"
+	//homedir "github.com/mitchellh/go-homedir"
 )
 )
 
 
-func getPeers(macaddress string, network string, server string, dualstack bool, isIngressGateway bool) ([]wgtypes.PeerConfig, bool, []string, error) {
+func GetNode(network string) nodepb.Node {
+
+        modcfg, err := config.ReadConfig(network)
+        if err != nil {
+                log.Fatalf("Error: %v", err)
+        }
+
+	nodecfg := modcfg.Node
+	var node nodepb.Node
+
+	node.Name = nodecfg.Name
+	node.Interface = nodecfg.Interface
+	node.Nodenetwork = nodecfg.Network
+	node.Localaddress = nodecfg.LocalAddress
+	node.Address = nodecfg.WGAddress
+	node.Address6 = nodecfg.WGAddress6
+	node.Listenport = nodecfg.Port
+	node.Keepalive = nodecfg.KeepAlive
+	node.Postup = nodecfg.PostUp
+	node.Postdown = nodecfg.PostDown
+	node.Publickey = nodecfg.PublicKey
+	node.Macaddress = nodecfg.MacAddress
+	node.Endpoint = nodecfg.Endpoint
+	node.Password = nodecfg.Password
+	if nodecfg.DNS == "on" {
+		node.Dnsoff = false
+	} else {
+		node.Dnsoff = true
+	}
+        if nodecfg.IsDualStack == "yes" {
+                node.Isdualstack = true
+        } else {
+                node.Isdualstack = false
+        }
+        if nodecfg.IsIngressGateway == "yes" {
+                node.Isingressgateway= true
+        } else {
+                node.Isingressgateway = false
+        }
+        return node
+}
+
+
+
+func RemoveNetwork(network string) error {
+        //need to  implement checkin on server side
+        cfg, err := config.ReadConfig(network)
+        if err != nil {
+                return err
+        }
+	servercfg := cfg.Server
+        node := cfg.Node
+	fmt.Println("Deleting remote node with MAC: " + node.MacAddress)
+
+
+        var wcclient nodepb.NodeServiceClient
+        var requestOpts grpc.DialOption
+        requestOpts = grpc.WithInsecure()
+        conn, err := grpc.Dial(servercfg.Address, requestOpts)
+	if err != nil {
+                log.Printf("Unable to establish client connection to " + servercfg.Address + ": %v", err)
+		//return err
+        }else {
+        wcclient = nodepb.NewNodeServiceClient(conn)
+
+        ctx := context.Background()
+        fmt.Println("Authenticating with GRPC Server")
+        ctx, err = auth.SetJWT(wcclient, network)
+        if err != nil {
+                //return err
+                log.Printf("Failed to authenticate: %v", err)
+        } else {
+        fmt.Println("Authenticated")
+
+        var header metadata.MD
+
+        _, err = wcclient.DeleteNode(
+                ctx,
+                &nodepb.DeleteNodeReq{
+                        Macaddress: node.MacAddress,
+                        NetworkName: node.Network,
+                },
+                grpc.Header(&header),
+        )
+        if err != nil {
+		log.Printf("Encountered error deleting node: %v", err)
+		fmt.Println(err)
+        } else {
+		fmt.Println("Deleted node " + node.MacAddress)
+	}
+	}
+	}
+	err = local.WipeLocal(network)
+	if err != nil {
+                log.Printf("Unable to wipe local config: %v", err)
+	}
+	err =  local.RemoveSystemDServices(network)
+        if err != nil {
+                return err
+                log.Printf("Unable to remove systemd services: %v", err)
+        }
+	fmt.Printf("Please investigate any stated errors to ensure proper removal.")
+	fmt.Printf("Failure to delete node from server via gRPC will mean node still exists and needs to be manually deleted by administrator.")
+
+	return nil
+}
+
+func GetPeers(macaddress string, network string, server string, dualstack bool, isIngressGateway bool) ([]wgtypes.PeerConfig, bool, []string, error) {
         //need to  implement checkin on server side
         //need to  implement checkin on server side
         hasGateway := false
         hasGateway := false
         var gateways []string
         var gateways []string
@@ -35,7 +144,6 @@ func getPeers(macaddress string, network string, server string, dualstack bool,
         }
         }
 
 
 
 
-        fmt.Println("Registering with GRPC Server")
         requestOpts := grpc.WithInsecure()
         requestOpts := grpc.WithInsecure()
         conn, err := grpc.Dial(server, requestOpts)
         conn, err := grpc.Dial(server, requestOpts)
         if err != nil {
         if err != nil {
@@ -49,8 +157,7 @@ func getPeers(macaddress string, network string, server string, dualstack bool,
                 Network: network,
                 Network: network,
         }
         }
         ctx := context.Background()
         ctx := context.Background()
-        fmt.Println("Authenticating with GRPC Server")
-        ctx, err = SetJWT(wcclient, network)
+        ctx, err = auth.SetJWT(wcclient, network)
         if err != nil {
         if err != nil {
                 fmt.Println("Failed to authenticate.")
                 fmt.Println("Failed to authenticate.")
                 return peers, hasGateway, gateways, err
                 return peers, hasGateway, gateways, err
@@ -63,7 +170,6 @@ func getPeers(macaddress string, network string, server string, dualstack bool,
                 fmt.Println(err)
                 fmt.Println(err)
                 return nil, hasGateway, gateways, err
                 return nil, hasGateway, gateways, err
         }
         }
-        fmt.Println("Parsing peers response")
         for {
         for {
                 res, err := stream.Recv()
                 res, err := stream.Recv()
                 // If end of stream, break the loop
                 // If end of stream, break the loop
@@ -88,11 +194,9 @@ func getPeers(macaddress string, network string, server string, dualstack bool,
                 }
                 }
 
 
                 if nodecfg.PublicKey == res.Peers.Publickey {
                 if nodecfg.PublicKey == res.Peers.Publickey {
-                        fmt.Println("Peer is self. Skipping")
                         continue
                         continue
                 }
                 }
                 if nodecfg.Endpoint == res.Peers.Endpoint {
                 if nodecfg.Endpoint == res.Peers.Endpoint {
-                        fmt.Println("Peer is self. Skipping")
                         continue
                         continue
                 }
                 }
 
 
@@ -149,8 +253,7 @@ func getPeers(macaddress string, network string, server string, dualstack bool,
 
 
         }
         }
         if isIngressGateway {
         if isIngressGateway {
-                fmt.Println("Adding external peers...")
-                extPeers, err := getExtPeers(macaddress, network, server, dualstack)
+                extPeers, err := GetExtPeers(macaddress, network, server, dualstack)
                 if err == nil {
                 if err == nil {
                         peers = append(peers, extPeers...)
                         peers = append(peers, extPeers...)
                         fmt.Println("Added " + strconv.Itoa(len(extPeers)) + " external clients.")
                         fmt.Println("Added " + strconv.Itoa(len(extPeers)) + " external clients.")
@@ -159,11 +262,10 @@ func getPeers(macaddress string, network string, server string, dualstack bool,
                         fmt.Println(err)
                         fmt.Println(err)
                 }
                 }
         }
         }
-        fmt.Println("Finished parsing peers response")
         return peers, hasGateway, gateways, err
         return peers, hasGateway, gateways, err
 }
 }
 
 
-func getExtPeers(macaddress string, network string, server string, dualstack bool) ([]wgtypes.PeerConfig, error) {
+func GetExtPeers(macaddress string, network string, server string, dualstack bool) ([]wgtypes.PeerConfig, error) {
         var peers []wgtypes.PeerConfig
         var peers []wgtypes.PeerConfig
         var wcclient nodepb.NodeServiceClient
         var wcclient nodepb.NodeServiceClient
         cfg, err := config.ReadConfig(network)
         cfg, err := config.ReadConfig(network)
@@ -186,8 +288,7 @@ func getExtPeers(macaddress string, network string, server string, dualstack boo
                 Network: network,
                 Network: network,
         }
         }
         ctx := context.Background()
         ctx := context.Background()
-        fmt.Println("Authenticating with GRPC Server")
-        ctx, err = SetJWT(wcclient, network)
+        ctx, err = auth.SetJWT(wcclient, network)
         if err != nil {
         if err != nil {
                 fmt.Println("Failed to authenticate.")
                 fmt.Println("Failed to authenticate.")
                 return peers, err
                 return peers, err
@@ -200,7 +301,6 @@ func getExtPeers(macaddress string, network string, server string, dualstack boo
                 fmt.Println(err)
                 fmt.Println(err)
                 return nil, err
                 return nil, err
         }
         }
-        fmt.Println("Parsing peers response")
         for {
         for {
                 res, err := stream.Recv()
                 res, err := stream.Recv()
                 // If end of stream, break the loop
                 // If end of stream, break the loop
@@ -225,7 +325,6 @@ func getExtPeers(macaddress string, network string, server string, dualstack boo
                 }
                 }
 
 
                 if nodecfg.PublicKey == res.Extpeers.Publickey {
                 if nodecfg.PublicKey == res.Extpeers.Publickey {
-                        fmt.Println("Peer is self. Skipping")
                         continue
                         continue
                 }
                 }
 
 

+ 322 - 0
netclient/wireguard/kernel.go

@@ -0,0 +1,322 @@
+package wireguard
+
+import (
+	"fmt"
+	"context"
+        "io/ioutil"
+	"strings"
+	"log"
+	"net"
+	"os"
+	"os/exec"
+        "github.com/gravitl/netmaker/netclient/config"
+        "github.com/gravitl/netmaker/netclient/local"
+        "github.com/gravitl/netmaker/netclient/auth"
+        "github.com/gravitl/netmaker/netclient/server"
+        nodepb "github.com/gravitl/netmaker/grpc"
+	"golang.zx2c4.com/wireguard/wgctrl"
+        "google.golang.org/grpc"
+	"google.golang.org/grpc/metadata"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+	//homedir "github.com/mitchellh/go-homedir"
+)
+
+func InitWireguard(node *nodepb.Node, privkey string, peers []wgtypes.PeerConfig, hasGateway bool, gateways []string) error  {
+
+	ipExec, err := exec.LookPath("ip")
+	if err !=  nil {
+		return err
+	}
+	key, err := wgtypes.ParseKey(privkey)
+        if err !=  nil {
+                return err
+        }
+
+        wgclient, err := wgctrl.New()
+	//modcfg := config.Config
+	//modcfg.ReadConfig()
+	modcfg, err := config.ReadConfig(node.Nodenetwork)
+        if err != nil {
+                return err
+        }
+
+
+	nodecfg := modcfg.Node
+	servercfg := modcfg.Server
+
+
+        if err != nil {
+                log.Fatalf("failed to open client: %v", err)
+        }
+        defer wgclient.Close()
+
+
+	ifacename := node.Interface
+	if nodecfg.Interface != "" {
+		ifacename = nodecfg.Interface
+	} else if node.Interface != "" {
+		ifacename = node.Interface
+	} else  {
+		log.Fatal("no interface to configure")
+	}
+	if node.Address == "" {
+		log.Fatal("no address to configure")
+	}
+	nameserver := servercfg.Address
+	nameserver = strings.Split(nameserver, ":")[0]
+	network := node.Nodenetwork
+        if nodecfg.Network != "" {
+                network = nodecfg.Network
+        } else if node.Nodenetwork != "" {
+                network = node.Nodenetwork
+        }
+        cmdIPDevLinkAdd := &exec.Cmd {
+                Path: ipExec,
+                Args: []string{ ipExec, "link", "add", "dev", ifacename, "type",  "wireguard" },
+                Stdout: os.Stdout,
+                Stderr: os.Stdout,
+        }
+        cmdIPAddrAdd := &exec.Cmd {
+                Path: ipExec,
+                Args: []string{ ipExec, "address", "add", "dev", ifacename, node.Address+"/24"},
+                Stdout: os.Stdout,
+                Stderr: os.Stdout,
+        }
+
+         currentiface, err := net.InterfaceByName(ifacename)
+
+
+        if err != nil {
+		err = cmdIPDevLinkAdd.Run()
+	if  err  !=  nil && !strings.Contains(err.Error(), "exists") {
+		fmt.Println("Error creating interface")
+		//fmt.Println(err.Error())
+		//return err
+	}
+	}
+	match := false
+	addrs, _ := currentiface.Addrs()
+	for _, a := range addrs {
+		if strings.Contains(a.String(), node.Address){
+			match = true
+		}
+	}
+	if !match {
+        err = cmdIPAddrAdd.Run()
+        if  err  !=  nil {
+		fmt.Println("Error adding address")
+                //return err
+        }
+	}
+	var nodeport int
+	nodeport = int(node.Listenport)
+
+
+	//pubkey := privkey.PublicKey()
+	conf := wgtypes.Config{
+		PrivateKey: &key,
+		ListenPort: &nodeport,
+		ReplacePeers: true,
+		Peers: peers,
+	}
+	_, err = wgclient.Device(ifacename)
+        if err != nil {
+                if os.IsNotExist(err) {
+                        fmt.Println("Device does not exist: ")
+                        fmt.Println(err)
+                } else {
+                        log.Fatalf("Unknown config error: %v", err)
+                }
+        }
+
+
+	err = wgclient.ConfigureDevice(ifacename, conf)
+
+	if err != nil {
+		if os.IsNotExist(err) {
+			fmt.Println("Device does not exist: ")
+			fmt.Println(err)
+		} else {
+			fmt.Printf("This is inconvenient: %v", err)
+		}
+	}
+
+	//=========DNS Setup==========\\
+	if nodecfg.DNS == "on" {
+
+	        _, err := exec.LookPath("resolvectl")
+		if err != nil {
+			fmt.Println(err)
+			fmt.Println("WARNING: resolvectl not present. Unable to set dns. Install resolvectl or run manually.")
+		} else {
+			_, err = exec.Command("resolvectl", "domain", ifacename, "~"+network).Output()
+			if err != nil {
+				fmt.Println(err)
+				fmt.Println("WARNING: Error encountered setting dns. Aborted setting dns.")
+			} else {
+				_, err = exec.Command("resolvectl", "default-route", ifacename, "false").Output()
+				if err != nil {
+	                                fmt.Println(err)
+	                                fmt.Println("WARNING: Error encountered setting dns. Aborted setting dns.")
+				} else {
+					_, err = exec.Command("resolvectl", "dns", ifacename, nameserver).Output()
+					fmt.Println(err)
+				}
+			}
+		}
+	}
+        //=========End DNS Setup=======\\
+
+        cmdIPLinkUp := &exec.Cmd {
+                Path: ipExec,
+                Args: []string{ ipExec, "link", "set", "up", "dev", ifacename},
+                Stdout: os.Stdout,
+                Stderr: os.Stdout,
+        }
+        cmdIPLinkDown := &exec.Cmd {
+                Path: ipExec,
+                Args: []string{ ipExec, "link", "set", "down", "dev", ifacename},
+                Stdout: os.Stdout,
+                Stderr: os.Stdout,
+        }
+        err = cmdIPLinkDown.Run()
+        if nodecfg.PostDown != "" {
+		runcmds := strings.Split(nodecfg.PostDown, "; ")
+		err = local.RunCmds(runcmds)
+		if err != nil {
+			fmt.Println("Error encountered running PostDown: " + err.Error())
+		}
+	}
+
+	err = cmdIPLinkUp.Run()
+        if  err  !=  nil {
+                return err
+        }
+
+	if nodecfg.PostUp != "" {
+                runcmds := strings.Split(nodecfg.PostUp, "; ")
+                err = local.RunCmds(runcmds)
+                if err != nil {
+                        fmt.Println("Error encountered running PostUp: " + err.Error())
+                }
+        }
+	if (hasGateway) {
+		for _, gateway := range gateways {
+			out, err := exec.Command(ipExec,"-4","route","add",gateway,"dev",ifacename).Output()
+                fmt.Println(string(out))
+		if err != nil {
+                        fmt.Println("Error encountered adding gateway: " + err.Error())
+                }
+		}
+	}
+        if (node.Address6 != "" && node.Isdualstack) {
+		fmt.Println("Adding address: " + node.Address6)
+                out, err := exec.Command(ipExec, "address", "add", "dev", ifacename, node.Address6+"/64").Output()
+                if err != nil {
+                        fmt.Println(out)
+                        fmt.Println("Error encountered adding ipv6: " + err.Error())
+                }
+	}
+	return err
+}
+
+
+func SetWGKeyConfig(network string, serveraddr string) error {
+
+        ctx := context.Background()
+        var header metadata.MD
+
+        var wcclient nodepb.NodeServiceClient
+        var requestOpts grpc.DialOption
+        requestOpts = grpc.WithInsecure()
+        conn, err := grpc.Dial(serveraddr, requestOpts)
+        if err != nil {
+                fmt.Printf("Cant dial GRPC server: %v", err)
+                return err
+        }
+        wcclient = nodepb.NewNodeServiceClient(conn)
+
+        ctx, err = auth.SetJWT(wcclient, network)
+        if err != nil {
+                fmt.Printf("Failed to authenticate: %v", err)
+                return err
+        }
+
+	node := server.GetNode(network)
+
+	privatekey, err := wgtypes.GeneratePrivateKey()
+	if err != nil {
+		return err
+	}
+	privkeystring := privatekey.String()
+	publickey := privatekey.PublicKey()
+
+	node.Publickey = publickey.String()
+
+	err = StorePrivKey(privkeystring, network)
+        if err != nil {
+                return err
+        }
+        err = config.ModConfig(&node)
+        if err != nil {
+                return err
+        }
+
+
+	postnode := server.GetNode(network)
+
+        req := &nodepb.UpdateNodeReq{
+               Node: &postnode,
+        }
+
+        _, err = wcclient.UpdateNode(ctx, req, grpc.Header(&header))
+        if err != nil {
+                return err
+        }
+        err = SetWGConfig(network)
+        if err != nil {
+                return err
+                log.Fatalf("Error: %v", err)
+        }
+
+        return err
+}
+
+
+func SetWGConfig(network string) error {
+
+        cfg, err := config.ReadConfig(network)
+        if err != nil {
+                return err
+        }
+	servercfg := cfg.Server
+        nodecfg := cfg.Node
+        node := server.GetNode(network)
+
+	peers, hasGateway, gateways, err := server.GetPeers(node.Macaddress, nodecfg.Network, servercfg.Address, node.Isdualstack, node.Isingressgateway)
+        if err != nil {
+                return err
+        }
+	privkey, err := RetrievePrivKey(network)
+        if err != nil {
+                return err
+        }
+
+        err = InitWireguard(&node, privkey, peers, hasGateway, gateways)
+        if err != nil {
+                return err
+        }
+
+	return err
+}
+
+func StorePrivKey(key string, network string) error{
+	d1 := []byte(key)
+	err := ioutil.WriteFile("/etc/netclient/wgkey-" + network, d1, 0644)
+	return err
+}
+
+func RetrievePrivKey(network string) (string, error) {
+	dat, err := ioutil.ReadFile("/etc/netclient/wgkey-" + network)
+	return string(dat), err
+}

+ 3 - 0
scripts/runmongo.sh

@@ -0,0 +1,3 @@
+#!/bin.bash
+docker volume create mongovol
+docker run -d --name mongodb -v mongovol:/data/db --network host -e MONGO_INITDB_ROOT_USERNAME=mongoadmin -e MONGO_INITDB_ROOT_PASSWORD=mongopass mongo --bind_ip 0.0.0.0

+ 24 - 8
servercfg/serverconf.go

@@ -47,6 +47,21 @@ func GetConfig() config.ServerConfig {
 	return cfg
 	return cfg
 }
 }
 
 
+func GetWGConfig() config.WG{
+	var cfg config.WG
+	if IsGRPCWireGuard() {
+		cfg.GRPCWireGuard = "on"
+	} else {
+                cfg.GRPCWireGuard = "off"
+	}
+	cfg.GRPCWGInterface = GetGRPCWGInterface()
+	cfg.GRPCWGAddress = GetGRPCWGAddress()
+	cfg.GRPCWGPort = GetGRPCWGPort()
+	cfg.GRPCWGEndpoint = GetGRPCHost()
+
+	return cfg
+}
+
 func GetAPIHost() string {
 func GetAPIHost() string {
         serverhost := "127.0.0.1"
         serverhost := "127.0.0.1"
         if os.Getenv("SERVER_HTTP_HOST") != ""  {
         if os.Getenv("SERVER_HTTP_HOST") != ""  {
@@ -67,15 +82,16 @@ func GetAPIPort() string {
 	}
 	}
 	return apiport
 	return apiport
 }
 }
+
 func GetGRPCHost() string {
 func GetGRPCHost() string {
-        serverhost := "127.0.0.1"
-        if os.Getenv("SERVER_GRPC_HOST") != ""  {
-                serverhost = os.Getenv("SERVER_GRPC_HOST")
-        } else if config.Config.Server.GRPCHost != "" {
-                serverhost = config.Config.Server.GRPCHost
-        } else if os.Getenv("SERVER_HOST") != ""  {
-                serverhost = os.Getenv("SERVER_HOST")
-        }
+	serverhost := "127.0.0.1"
+	if os.Getenv("SERVER_GRPC_HOST") != ""  {
+	       serverhost = os.Getenv("SERVER_GRPC_HOST")
+	} else if config.Config.Server.GRPCHost != "" {
+	       serverhost = config.Config.Server.GRPCHost
+	} else if os.Getenv("SERVER_HOST") != ""  {
+	       serverhost = os.Getenv("SERVER_HOST")
+	}
         return serverhost
         return serverhost
 }
 }
 func GetGRPCPort() string {
 func GetGRPCPort() string {

+ 85 - 0
servercfg/wireguardconf.go

@@ -0,0 +1,85 @@
+package servercfg
+
+import (
+        "github.com/gravitl/netmaker/config"
+        "os"
+)
+func IsRegisterKeyRequired() bool {
+       isrequired := false
+       if os.Getenv("SERVER_GRPC_WG_KEYREQUIRED") != "" {
+                if os.Getenv("SERVER_GRPC_WG_KEYREQUIRED") == "yes" {
+                        isrequired = true
+                }
+       } else if config.Config.WG.RegisterKeyRequired != "" {
+                if config.Config.WG.RegisterKeyRequired == "yes" {
+                        isrequired = true
+                }
+       }
+       return isrequired
+}
+func IsGRPCWireGuard() bool {
+       iswg := true
+       if os.Getenv("SERVER_GRPC_WIREGUARD") != "" {
+                if os.Getenv("SERVER_GRPC_WIREGUARD") == "off" {
+                        iswg = false
+                }
+       } else if config.Config.WG.GRPCWireGuard != "" {
+                if config.Config.WG.GRPCWireGuard == "off" {
+                        iswg = false
+                }
+       }
+       return iswg
+}
+func GetGRPCWGInterface() string {
+       iface := "nm-grpc-wg"
+       if os.Getenv("SERVER_GRPC_WG_INTERFACE") != "" {
+                iface = os.Getenv("SERVER_GRPC_WG_INTERFACE")
+       } else if config.Config.WG.GRPCWGInterface != "" {
+                iface = config.Config.WG.GRPCWGInterface
+       }
+       return iface
+}
+func GetGRPCWGAddress() string {
+        address := "fd73:0093:84f3:a13d:0000:0000:0000:0001"
+      if os.Getenv("SERVER_GRPC_WG_ADDRESS") != ""  {
+              address = os.Getenv("SERVER_GRPC_WG_ADDRESS")
+      } else if config.Config.WG.GRPCWGAddress != "" {
+              address = config.Config.WG.GRPCWGAddress
+      }
+      return address
+}
+func GetGRPCWGAddressRange() string {
+        address := "fd73:0093:84f3:a13d::/64"
+      if os.Getenv("SERVER_GRPC_WG_ADDRESS_RANGE") != ""  {
+              address = os.Getenv("SERVER_GRPC_WG_ADDRESS_RANGE")
+      } else if config.Config.WG.GRPCWGAddressRange != "" {
+              address = config.Config.WG.GRPCWGAddressRange
+      }
+      return address
+}
+func GetGRPCWGPort() string {
+        port := "50555"
+      if os.Getenv("SERVER_GRPC_WG_PORT") != ""  {
+              port = os.Getenv("SERVER_GRPC_WG_PORT")
+      } else if config.Config.WG.GRPCWGPort != "" {
+              port = config.Config.WG.GRPCWGPort
+      }
+      return port
+}
+
+func GetGRPCWGPubKey() string {
+      key := os.Getenv("SERVER_GRPC_WG_PUBKEY")
+      if config.Config.WG.GRPCWGPubKey != "" {
+              key = config.Config.WG.GRPCWGPubKey
+      }
+      return key
+}
+
+func GetGRPCWGPrivKey() string {
+      key := os.Getenv("SERVER_GRPC_WG_PRIVKEY")
+      if config.Config.WG.GRPCWGPrivKey != "" {
+              key = config.Config.WG.GRPCWGPrivKey
+      }
+      return key
+}
+

+ 58 - 0
serverctl/serverctl.go

@@ -6,6 +6,8 @@ import (
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/mongoconn"
 	"github.com/gravitl/netmaker/mongoconn"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
+        "go.mongodb.org/mongo-driver/bson"
+        "go.mongodb.org/mongo-driver/mongo/options"
 	"io"
 	"io"
 	"time"
 	"time"
 	"context"
 	"context"
@@ -59,6 +61,62 @@ func CreateDefaultNetwork() (bool, error) {
 
 
 }
 }
 
 
+func GetServerWGConf() (models.ServerClient, error) {
+        var server models.ServerClient
+        collection := mongoconn.Client.Database("netmaker").Collection("serverclients")
+        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	filter := bson.M{"network": "comms", "isserver": "yes"}
+        err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&server)
+	defer cancel()
+
+	return server, err
+}
+
+
+func CreateCommsNetwork() (bool, error) {
+
+        fmt.Println("Creating GRPC network...")
+
+        iscreated := false
+        exists, err := functions.NetworkExists("comms")
+
+        if exists || err != nil {
+                fmt.Println("GRPC network already exists. Skipping...")
+                return true, nil
+        } else {
+
+        var network models.Network
+
+        network.NetID = "comms"
+	network.IsIPv6 = "yes"
+	network.IsIPv4 = "no"
+	network.IsGRPCHub = "yes"
+        network.AddressRange6 = servercfg.GetGRPCWGAddressRange()
+        network.DisplayName = "comms"
+        network.SetDefaults()
+        network.SetNodesLastModified()
+        network.SetNetworkLastModified()
+        network.KeyUpdateTimeStamp = time.Now().Unix()
+        priv := false
+        network.IsLocal = &priv
+        network.KeyUpdateTimeStamp = time.Now().Unix()
+
+        fmt.Println("Creating comms network.")
+
+        collection := mongoconn.Client.Database("netmaker").Collection("networks")
+        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+
+        // insert our network into the network table
+        _, err = collection.InsertOne(ctx, network)
+        defer cancel()
+
+        }
+        if err == nil {
+                iscreated = true
+        }
+        return iscreated, err
+}
+
 func DownloadNetclient() error {
 func DownloadNetclient() error {
 	/*
 	/*
 	// Get the data
 	// Get the data

+ 179 - 0
serverctl/wireguard.go

@@ -0,0 +1,179 @@
+package serverctl
+
+import (
+	"os"
+	"log"
+	"context"
+	"time"
+	"net"
+	"strconv"
+	"errors"
+	"github.com/vishvananda/netlink"
+	"golang.zx2c4.com/wireguard/wgctrl"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+        "github.com/gravitl/netmaker/servercfg"
+        "github.com/gravitl/netmaker/functions"
+        "github.com/gravitl/netmaker/models"
+        "github.com/gravitl/netmaker/mongoconn"
+)
+
+func InitServerWireGuard() error {
+
+	created, err := CreateCommsNetwork()
+	if !created {
+		return err
+	}
+	wgconfig := servercfg.GetWGConfig()
+	if !(wgconfig.GRPCWireGuard == "on") {
+		return errors.New("WireGuard not enabled on this server.")
+	}
+	ifaceSettings := netlink.NewLinkAttrs()
+
+	if wgconfig.GRPCWGInterface == "" {
+		return errors.New("No WireGuard Interface Name set.")
+	}
+	ifaceSettings.Name = wgconfig.GRPCWGInterface
+	wglink := &models.WireGuardLink{LinkAttrs: &ifaceSettings}
+
+	err = netlink.LinkAdd(wglink)
+	if err != nil {
+		if os.IsExist(err) {
+			log.Println("interface " + ifaceSettings.Name + " already exists")
+			log.Println("continuing setup using existing interface")
+		} else {
+			return err
+		}
+	}
+	address, err := netlink.ParseAddr(wgconfig.GRPCWGAddress + "/32")
+	if err != nil {
+		return err
+	}
+
+	err = netlink.AddrAdd(wglink, address)
+        if err != nil {
+                if os.IsExist(err) {
+                        log.Println("address " + wgconfig.GRPCWGAddress + " already exists")
+                        log.Println("continuing with existing setup")
+                } else {
+                        return err
+                }
+        }
+	err = netlink.LinkSetUp(wglink)
+	if err != nil {
+		log.Println("could not bring up wireguard interface")
+		return err
+	}
+	var client models.ServerClient
+	client.PrivateKey = servercfg.GetGRPCWGPrivKey()
+	client.PublicKey = servercfg.GetGRPCWGPubKey()
+	client.ServerEndpoint = servercfg.GetGRPCHost()
+	client.Address6 = servercfg.GetGRPCWGAddress()
+	client.IsServer = "yes"
+	client.Network = "comms"
+	err = RegisterServer(client)
+        return err
+}
+
+func RegisterServer(client models.ServerClient) error {
+        if client.PrivateKey == "" {
+                privateKey, err := wgtypes.GeneratePrivateKey()
+                if err != nil {
+                        return err
+                }
+
+                client.PrivateKey = privateKey.String()
+                client.PublicKey = privateKey.PublicKey().String()
+        }
+
+        if client.Address == "" {
+                newAddress, err := functions.UniqueAddress6(client.Network)
+                if err != nil {
+                        return err
+                }
+                client.Address6 = newAddress
+        }
+	if client.Network == "" { client.Network = "comms" }
+        client.ServerKey = client.PublicKey
+
+        collection := mongoconn.Client.Database("netmaker").Collection("serverclients")
+        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+        // insert our network into the network table
+        _, err := collection.InsertOne(ctx, client)
+        defer cancel()
+
+        ReconfigureServerWireGuard()
+
+        return err
+}
+
+func ReconfigureServerWireGuard() error {
+	server, err := GetServerWGConf()
+	if err != nil {
+                return err
+        }
+	serverkey, err := wgtypes.ParseKey(server.PrivateKey)
+        if err != nil {
+                return err
+        }
+	serverport, err := strconv.Atoi(servercfg.GetGRPCWGPort())
+        if err != nil {
+                return err
+        }
+
+	peers, err := functions.GetPeersList("comms")
+        if err != nil {
+                return err
+        }
+
+	wgserver, err := wgctrl.New()
+	if err != nil {
+		return err
+	}
+        var serverpeers []wgtypes.PeerConfig
+	for _, peer := range peers {
+
+                pubkey, err := wgtypes.ParseKey(peer.PublicKey)
+		if err != nil {
+			return err
+		}
+                var peercfg wgtypes.PeerConfig
+                var allowedips []net.IPNet
+                if peer.Address != "" {
+			var peeraddr = net.IPNet{
+	                        IP: net.ParseIP(peer.Address),
+	                        Mask: net.CIDRMask(32, 32),
+	                }
+	                allowedips = append(allowedips, peeraddr)
+		}
+		if peer.Address6 != "" {
+                        var addr6 = net.IPNet{
+                                IP: net.ParseIP(peer.Address6),
+                                Mask: net.CIDRMask(128, 128),
+                        }
+                        allowedips = append(allowedips, addr6)
+                }
+		peercfg = wgtypes.PeerConfig{
+                        PublicKey: pubkey,
+                        Endpoint: &net.UDPAddr{
+                                IP:   net.ParseIP(peer.Endpoint),
+                                Port: int(peer.ListenPort),
+                        },
+                        ReplaceAllowedIPs: true,
+                        AllowedIPs: allowedips,
+                }
+                serverpeers = append(serverpeers, peercfg)
+	}
+
+	wgconf := wgtypes.Config{
+		PrivateKey:   &serverkey,
+		ListenPort:   &serverport,
+		ReplacePeers: true,
+		Peers:        serverpeers,
+	}
+	err = wgserver.ConfigureDevice(servercfg.GetGRPCWGInterface(), wgconf)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}

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