Browse Source

Merge branch 'develop' into arm-docker

Alex 4 years ago
parent
commit
dc363495db

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

@@ -5,7 +5,7 @@ on:
     branches:
       - 'develop'
       - 'master'
-
+      
 jobs:
   docker:
     runs-on: ubuntu-latest
@@ -38,4 +38,4 @@ jobs:
           platforms: linux/amd64, linux/arm64
           push: true
           tags: |
-            gravitl/netmaker:develop
+            gravitl/netmaker:develop

+ 3 - 0
config/config.go

@@ -42,6 +42,9 @@ type ServerConfig struct {
   AllowedOrigin	string `yaml:"allowedorigin"`
   RestBackend bool `yaml:"restbackend"`
   AgentBackend bool `yaml:"agentbackend"`
+  DefaultNetName string `yaml:"defaultnetname"`
+  DefaultNetRange string `yaml:"defaultnetrange"`
+  CreateDefault bool `yaml:"createdefault"`
 }
 
 type MongoConnConfig struct {

+ 3 - 0
config/environments/dev.yaml

@@ -6,6 +6,9 @@ server:
   allowedorigin: "*"
   restbackend: true            
   agentbackend: true
+  defaultnetname: "default"
+  defaultnetrange: "10.10.10.0/24"
+  createdefault: true
 mongoconn:
   user: "mongoadmin"
   pass: "mongopass"

+ 7 - 3
controllers/authGrpc.go

@@ -105,6 +105,7 @@ func (s *NodeServiceServer) Login(ctx context.Context, req *nodepb.LoginRequest)
 
 	//out := new(LoginResponse)
 	macaddress := req.GetMacaddress()
+	network := req.GetNetwork()
 	password := req.GetPassword()
 
 	var result models.NodeAuth
@@ -123,7 +124,7 @@ func (s *NodeServiceServer) Login(ctx context.Context, req *nodepb.LoginRequest)
             //Search DB for node with Mac Address. Ignore pending nodes (they should not be able to authenticate with API untill approved).
             collection := mongoconn.Client.Database("netmaker").Collection("nodes")
             ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-            var err = collection.FindOne(ctx, bson.M{ "macaddress": macaddress}).Decode(&result)
+	    var err = collection.FindOne(ctx, bson.M{ "macaddress": macaddress, "group": network}).Decode(&result)
 
             defer cancel()
 
@@ -135,12 +136,15 @@ func (s *NodeServiceServer) Login(ctx context.Context, req *nodepb.LoginRequest)
            //might be able to have a common hash (certificates?) and compare those so that a password isn't passed in in plain text...
            //TODO: Consider a way of hashing the password client side before sending, or using certificates
            err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(password))
-           if err != nil && result.Password != password {
+	   if err != nil && result.Password != password {
 			return nil, err
            } else {
                 //Create a new JWT for the node
-                tokenString, _ := functions.CreateJWT(macaddress, result.Group)
+                tokenString, err := functions.CreateJWT(macaddress, result.Group)
 
+		if err != nil {
+			return nil, err
+		}
                 if tokenString == "" {
 		    err = errors.New("Something went wrong. Could not retrieve token.")
                     return nil, err

+ 21 - 14
controllers/common.go

@@ -117,10 +117,12 @@ func ValidateNode(operation string, groupName string, node models.Node) error {
         return err
 }
 
+
 func UpdateNode(nodechange models.Node, node models.Node) (models.Node, error) {
     //Question: Is there a better way  of doing  this than a bunch of "if" statements? probably...
     //Eventually, lets have a better way to check if any of the fields are filled out...
     queryMac := node.MacAddress
+    queryGroup := node.Group
     notifygroup := false
 
     if nodechange.Address != "" {
@@ -136,6 +138,9 @@ func UpdateNode(nodechange models.Node, node models.Node) (models.Node, error) {
     if nodechange.ListenPort != 0 {
         node.ListenPort = nodechange.ListenPort
     }
+    if nodechange.ExpirationDateTime != 0 {
+        node.ExpirationDateTime = nodechange.ExpirationDateTime
+    }
     if nodechange.PreUp != "" {
         node.PreUp = nodechange.PreUp
     }
@@ -174,6 +179,7 @@ func UpdateNode(nodechange models.Node, node models.Node) (models.Node, error) {
     }
     if nodechange.PublicKey != "" {
         node.PublicKey = nodechange.PublicKey
+	node.KeyUpdateTimeStamp = time.Now().Unix()
 	notifygroup = true
     }
 
@@ -183,7 +189,7 @@ func UpdateNode(nodechange models.Node, node models.Node) (models.Node, error) {
         ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
         // Create filter
-        filter := bson.M{"macaddress": queryMac}
+        filter := bson.M{"macaddress": queryMac, "group": queryGroup}
 
         node.SetLastModified()
 
@@ -194,6 +200,8 @@ func UpdateNode(nodechange models.Node, node models.Node) (models.Node, error) {
                         {"password", node.Password},
                         {"listenport", node.ListenPort},
                         {"publickey", node.PublicKey},
+                        {"keyupdatetimestamp", node.KeyUpdateTimeStamp},
+                        {"expdatetime", node.ExpirationDateTime},
                         {"endpoint", node.Endpoint},
                         {"postup", node.PostUp},
                         {"preup", node.PreUp},
@@ -306,7 +314,7 @@ func CreateNode(node models.Node, groupName string) (models.Node, error) {
         node.SetDefaultName()
 	node.SetLastCheckIn()
 	node.SetLastPeerUpdate()
-
+	node.KeyUpdateTimeStamp = time.Now().Unix()
 
         //Create a JWT for the node
         tokenString, _ := functions.CreateJWT(node.MacAddress, groupName)
@@ -365,7 +373,9 @@ func NodeCheckIn(node models.Node, groupName string) (models.CheckInResponse, er
 
         grouplm := parentgroup.GroupLastModified
 	peerslm := parentgroup.NodesLastModified
-        peerlistlm := parentnode.LastPeerUpdate
+	gkeyupdate := parentgroup.KeyUpdateTimeStamp
+	nkeyupdate := parentnode.KeyUpdateTimeStamp
+	peerlistlm := parentnode.LastPeerUpdate
         parentnodelm := parentnode.LastModified
 	parentnodelastcheckin := parentnode.LastCheckIn
 
@@ -379,23 +389,20 @@ func NodeCheckIn(node models.Node, groupName string) (models.CheckInResponse, er
 	if peerlistlm < peerslm {
 		response.NeedPeerUpdate = true
 	}
-	/*
-	if postchanges {
-		parentnode, err = UpdateNode(node, parentnode)
-	        if err != nil{
-			err = fmt.Errorf("%w; Couldnt Update Node: ", err)
-			return response, err
-		} else {
-			response.NodeUpdated = true
-		}
+	if nkeyupdate < gkeyupdate {
+		response.NeedKeyUpdate = true
 	}
-	*/
+        if time.Now().Unix() > parentnode.ExpirationDateTime {
+                response.NeedDelete = true
+		_, err =  DeleteNode(node.MacAddress, groupName)
+        } else {
 	err = TimestampNode(parentnode, true,  false, false)
 
 	if err != nil{
 		err = fmt.Errorf("%w; Couldnt Timestamp Node: ", err)
 		return response, err
 	}
+	}
 	response.Success = true
 
         return response, err
@@ -446,7 +453,7 @@ func TimestampNode(node models.Node, updatecheckin bool, updatepeers bool, updat
         ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
         // Create filter
-        filter := bson.M{"macaddress": node.MacAddress}
+        filter := bson.M{"macaddress": node.MacAddress, "group": node.Group}
 
         // prepare update model.
         update := bson.D{

+ 1 - 0
controllers/controller.go

@@ -29,6 +29,7 @@ func HandleRESTRequests(wg *sync.WaitGroup) {
     userHandlers(r)
     groupHandlers(r)
     fileHandlers(r)
+    serverHandlers(r)
 
 		port := config.Config.Server.ApiPort
 	        if os.Getenv("API_PORT") != "" {

+ 72 - 3
controllers/groupHttpController.go

@@ -3,6 +3,7 @@ package controller
 import (
     "gopkg.in/go-playground/validator.v9"
     "github.com/gravitl/netmaker/models"
+    "encoding/base64"
     "github.com/gravitl/netmaker/functions"
     "github.com/gravitl/netmaker/mongoconn"
     "time"
@@ -20,6 +21,7 @@ import (
 func groupHandlers(r *mux.Router) {
     r.HandleFunc("/api/groups", securityCheck(http.HandlerFunc(getGroups))).Methods("GET")
     r.HandleFunc("/api/groups", securityCheck(http.HandlerFunc(createGroup))).Methods("POST")
+    r.HandleFunc("/api/groups/{groupname}/keyupdate", securityCheck(http.HandlerFunc(keyUpdate))).Methods("POST")
     r.HandleFunc("/api/groups/{groupname}", securityCheck(http.HandlerFunc(getGroup))).Methods("GET")
     r.HandleFunc("/api/groups/{groupname}/numnodes", securityCheck(http.HandlerFunc(getGroupNodeNumber))).Methods("GET")
     r.HandleFunc("/api/groups/{groupname}", securityCheck(http.HandlerFunc(updateGroup))).Methods("PUT")
@@ -192,6 +194,59 @@ func getGroup(w http.ResponseWriter, r *http.Request) {
         json.NewEncoder(w).Encode(group)
 }
 
+func keyUpdate(w http.ResponseWriter, r *http.Request) {
+
+        w.Header().Set("Content-Type", "application/json")
+
+        var params = mux.Vars(r)
+
+        var group models.Group
+
+        group, err := functions.GetParentGroup(params["groupname"])
+        if err != nil {
+		return
+        }
+
+	group.KeyUpdateTimeStamp = time.Now().Unix()
+
+        collection := mongoconn.Client.Database("netmaker").Collection("groups")
+
+        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+
+        filter := bson.M{"nameid": params["groupname"]}
+
+        // prepare update model.
+        update := bson.D{
+                {"$set", bson.D{
+                        {"addressrange", group.AddressRange},
+                        {"displayname", group.DisplayName},
+                        {"defaultlistenport", group.DefaultListenPort},
+                        {"defaultpostup", group.DefaultPostUp},
+                        {"defaultpreup", group.DefaultPreUp},
+			{"defaultkeepalive", group.DefaultKeepalive},
+                        {"keyupdatetimestamp", group.KeyUpdateTimeStamp},
+                        {"defaultsaveconfig", group.DefaultSaveConfig},
+                        {"defaultinterface", group.DefaultInterface},
+                        {"nodeslastmodified", group.NodesLastModified},
+                        {"grouplastmodified", group.GroupLastModified},
+                        {"allowmanualsignup", group.AllowManualSignUp},
+                        {"defaultcheckininterval", group.DefaultCheckInInterval},
+                }},
+        }
+
+        errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&group)
+
+        defer cancel()
+
+        if errN != nil {
+                mongoconn.GetError(errN, w)
+                fmt.Println(errN)
+                return
+        }
+
+        json.NewEncoder(w).Encode(group)
+}
+
 //Update a group
 func updateGroup(w http.ResponseWriter, r *http.Request) {
 
@@ -404,6 +459,7 @@ func createGroup(w http.ResponseWriter, r *http.Request) {
 	group.SetDefaults()
         group.SetNodesLastModified()
         group.SetGroupLastModified()
+        group.KeyUpdateTimeStamp = time.Now().Unix()
 
 
         collection := mongoconn.Client.Database("netmaker").Collection("groups")
@@ -454,6 +510,18 @@ func createAccessKey(w http.ResponseWriter, r *http.Request) {
         if accesskey.Uses == 0 {
                 accesskey.Uses = 1
         }
+	gconf, errG := functions.GetGlobalConfig()
+        if errG != nil {
+                mongoconn.GetError(errG, w)
+                return
+        }
+
+
+	network := params["groupname"]
+	address := gconf.ServerGRPC + gconf.PortGRPC
+
+	accessstringdec := address + "." + network + "." + accesskey.Value
+	accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(accessstringdec))
 
 
 	group.AccessKeys = append(group.AccessKeys, accesskey)
@@ -483,7 +551,7 @@ func createAccessKey(w http.ResponseWriter, r *http.Request) {
                 mongoconn.GetError(errN, w)
                 return
         }
-	w.Write([]byte(accesskey.Value))
+	w.Write([]byte(accesskey.AccessString))
 }
 
 //pretty simple get
@@ -495,8 +563,8 @@ func getAccessKeys(w http.ResponseWriter, r *http.Request) {
         var params = mux.Vars(r)
 
         var group models.Group
-        var keys []models.DisplayKey
-
+        //var keys []models.DisplayKey
+	var keys []models.AccessKey
         collection := mongoconn.Client.Database("netmaker").Collection("groups")
 
         ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
@@ -522,6 +590,7 @@ func getAccessKeys(w http.ResponseWriter, r *http.Request) {
         json.NewEncoder(w).Encode(keys)
 }
 
+
 //delete key. Has to do a little funky logic since it's not a collection item
 func deleteAccessKey(w http.ResponseWriter, r *http.Request) {
 

+ 2 - 0
controllers/nodeGrpcController.go

@@ -171,7 +171,9 @@ func (s *NodeServiceServer) CheckIn(ctx context.Context, req *nodepb.CheckInReq)
                 Checkinresponse: &nodepb.CheckInResponse{
                         Success:  checkinresponse.Success,
                         Needpeerupdate:  checkinresponse.NeedPeerUpdate,
+                        Needdelete:  checkinresponse.NeedDelete,
                         Needconfigupdate:  checkinresponse.NeedConfigUpdate,
+                        Needkeyupdate:  checkinresponse.NeedKeyUpdate,
                         Nodemessage:  checkinresponse.NodeMessage,
                         Ispending:  checkinresponse.IsPending,
                 },

+ 2 - 2
controllers/nodeHttpController.go

@@ -408,7 +408,7 @@ func checkIn(w http.ResponseWriter, r *http.Request) {
 
         ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        filter := bson.M{"macaddress": params["macaddress"]}
+        filter := bson.M{"macaddress": params["macaddress"], "group": params["group"]}
 
 	//old code was inefficient, this is all we need.
 	time := time.Now().String()
@@ -570,7 +570,7 @@ func uncordonNode(w http.ResponseWriter, r *http.Request) {
         ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
         // Create filter
-        filter := bson.M{"macaddress": params["macaddress"]}
+	filter := bson.M{"macaddress": params["macaddress"], "group": params["group"]}
 
         node.SetLastModified()
 

+ 90 - 0
controllers/serverHttpController.go

@@ -0,0 +1,90 @@
+package controller
+
+import (
+    "github.com/gravitl/netmaker/models"
+    "github.com/gravitl/netmaker/serverctl"
+    "github.com/gravitl/netmaker/config"
+    "encoding/json"
+    "strings"
+    "net/http"
+    "github.com/gorilla/mux"
+)
+
+func serverHandlers(r *mux.Router) {
+    r.HandleFunc("/api/server/addnetwork/{network}", securityCheckServer(http.HandlerFunc(addNetwork))).Methods("POST")
+    r.HandleFunc("/api/server/removenetwork/{network}", securityCheckServer(http.HandlerFunc(removeNetwork))).Methods("DELETE")
+}
+
+//Security check is middleware for every function and just checks to make sure that its the master calling
+//Only admin should have access to all these group-level actions
+//or maybe some Users once implemented
+func securityCheckServer(next http.Handler) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var errorResponse = models.ErrorResponse{
+			Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
+		}
+
+		bearerToken := r.Header.Get("Authorization")
+
+		var hasBearer = true
+		var tokenSplit = strings.Split(bearerToken, " ")
+		var  authToken = ""
+
+		if len(tokenSplit) < 2 {
+			hasBearer = false
+		} else {
+			authToken = tokenSplit[1]
+		}
+		//all endpoints here require master so not as complicated
+		//still might not be a good  way of doing this
+		if !hasBearer || !authenticateMasterServer(authToken) {
+			errorResponse = models.ErrorResponse{
+				Code: http.StatusUnauthorized, Message: "W1R3: You are unauthorized to access this endpoint.",
+			}
+			returnErrorResponse(w, r, errorResponse)
+		} else {
+			next.ServeHTTP(w, r)
+		}
+	}
+}
+//Consider a more secure way of setting master key
+func authenticateMasterServer(tokenString string) bool {
+    if tokenString == config.Config.Server.MasterKey {
+        return true
+    }
+    return false
+}
+
+func removeNetwork(w http.ResponseWriter, r *http.Request) {
+        // Set header
+        w.Header().Set("Content-Type", "application/json")
+
+        // get params
+        var params = mux.Vars(r)
+
+        success, err := serverctl.RemoveNetwork(params["network"])
+
+        if err != nil || !success {
+                json.NewEncoder(w).Encode("Could not remove server from network " + params["network"])
+                return
+        }
+
+        json.NewEncoder(w).Encode("Server removed from network " + params["network"])
+}
+
+func addNetwork(w http.ResponseWriter, r *http.Request) {
+        // Set header
+        w.Header().Set("Content-Type", "application/json")
+
+        // get params
+        var params = mux.Vars(r)
+
+        success, err := serverctl.AddNetwork(params["network"])
+
+        if err != nil || !success {
+                json.NewEncoder(w).Encode("Could not add server to network " + params["network"])
+                return
+        }
+
+        json.NewEncoder(w).Encode("Server added to network " + params["network"])
+}

+ 1 - 1
docker-compose.yml

@@ -16,7 +16,7 @@ services:
     container_name: netmaker
     depends_on:
       - mongodb
-    image: gravitl/netmaker:v0.1
+    image: gravitl/netmaker:v0.1-hotfix
     ports:
       - "8081:8081"
       - "50051:50051"

+ 78 - 1
functions/helpers.go

@@ -24,6 +24,58 @@ import (
 
 //Takes in an arbitrary field and value for field and checks to see if any other
 //node has that value for the same field within the group
+
+func CreateServerToken(network string) (string, error) {
+
+        var group models.Group
+        var accesskey models.AccessKey
+
+        group, err := GetParentGroup(network)
+        if err != nil {
+                return "", err
+        }
+
+                accesskey.Name = GenKeyName()
+                accesskey.Value = GenKey()
+                accesskey.Uses = 1
+        gconf, errG := GetGlobalConfig()
+        if errG != nil {
+                return "", errG
+        }
+        address := "localhost" + gconf.PortGRPC
+
+        accessstringdec := address + "." + network + "." + accesskey.Value
+        accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(accessstringdec))
+
+        group.AccessKeys = append(group.AccessKeys, accesskey)
+
+        collection := mongoconn.Client.Database("netmaker").Collection("groups")
+
+        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+
+        // Create filter
+        filter := bson.M{"nameid": network}
+
+        // Read update model from body request
+        fmt.Println("Adding key to " + group.NameID)
+
+        // prepare update model.
+        update := bson.D{
+                {"$set", bson.D{
+                        {"accesskeys", group.AccessKeys},
+                }},
+        }
+
+        errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&group)
+
+        defer cancel()
+
+        if errN != nil {
+                return "", errN
+        }
+        return accesskey.AccessString, nil
+}
+
 func IsFieldUnique(group string,  field string, value string) bool {
 
 	var node models.Node
@@ -64,7 +116,7 @@ func GroupExists(name string) (bool, error) {
 
 	if err != nil {
 		if err == mongo.ErrNoDocuments {
-			return false, err
+			return false, nil
 		}
 		fmt.Println("ERROR RETRIEVING GROUP!")
 		fmt.Println(err)
@@ -377,6 +429,31 @@ func UniqueAddress(groupName string) (string, error){
 	return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", err1
 }
 
+//pretty simple get
+func GetGlobalConfig() ( models.GlobalConfig, error) {
+
+        filter := bson.M{}
+
+        var globalconf models.GlobalConfig
+
+        collection := mongoconn.Client.Database("netmaker").Collection("config")
+
+        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+
+        err := collection.FindOne(ctx, filter).Decode(&globalconf)
+
+        defer cancel()
+
+        if err != nil {
+                fmt.Println(err)
+                fmt.Println("Could not get global config")
+                return globalconf, err
+        }
+	return globalconf, err
+}
+
+
+
 //generate an access key value
 func GenKey() string {
 

+ 79 - 52
grpc/node.pb.go

@@ -23,6 +23,7 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 type LoginRequest struct {
 	Macaddress           string   `protobuf:"bytes,1,opt,name=macaddress,proto3" json:"macaddress,omitempty"`
 	Password             string   `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
+	Network              string   `protobuf:"bytes,3,opt,name=network,proto3" json:"network,omitempty"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 	XXX_unrecognized     []byte   `json:"-"`
 	XXX_sizecache        int32    `json:"-"`
@@ -67,6 +68,13 @@ func (m *LoginRequest) GetPassword() string {
 	return ""
 }
 
+func (m *LoginRequest) GetNetwork() string {
+	if m != nil {
+		return m.Network
+	}
+	return ""
+}
+
 type LoginResponse struct {
 	Accesstoken          string   `protobuf:"bytes,1,opt,name=accesstoken,proto3" json:"accesstoken,omitempty"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
@@ -311,6 +319,8 @@ type CheckInResponse struct {
 	Needconfigupdate     bool     `protobuf:"varint,3,opt,name=needconfigupdate,proto3" json:"needconfigupdate,omitempty"`
 	Nodemessage          string   `protobuf:"bytes,4,opt,name=nodemessage,proto3" json:"nodemessage,omitempty"`
 	Ispending            bool     `protobuf:"varint,5,opt,name=ispending,proto3" json:"ispending,omitempty"`
+	Needkeyupdate        bool     `protobuf:"varint,6,opt,name=needkeyupdate,proto3" json:"needkeyupdate,omitempty"`
+	Needdelete           bool     `protobuf:"varint,7,opt,name=needdelete,proto3" json:"needdelete,omitempty"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 	XXX_unrecognized     []byte   `json:"-"`
 	XXX_sizecache        int32    `json:"-"`
@@ -376,6 +386,20 @@ func (m *CheckInResponse) GetIspending() bool {
 	return false
 }
 
+func (m *CheckInResponse) GetNeedkeyupdate() bool {
+	if m != nil {
+		return m.Needkeyupdate
+	}
+	return false
+}
+
+func (m *CheckInResponse) GetNeeddelete() bool {
+	if m != nil {
+		return m.Needdelete
+	}
+	return false
+}
+
 type PeersResponse struct {
 	Publickey            string   `protobuf:"bytes,5,opt,name=publickey,proto3" json:"publickey,omitempty"`
 	Endpoint             string   `protobuf:"bytes,6,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
@@ -970,56 +994,59 @@ func init() {
 func init() { proto.RegisterFile("grpc/node.proto", fileDescriptor_d13bd996b67da4ef) }
 
 var fileDescriptor_d13bd996b67da4ef = []byte{
-	// 813 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0x4d, 0x6f, 0xf3, 0x44,
-	0x10, 0x56, 0xf2, 0x26, 0x4d, 0x32, 0x69, 0x9a, 0xbe, 0xdb, 0x16, 0xad, 0xac, 0xaa, 0x8a, 0x7c,
-	0x40, 0x29, 0xa2, 0x49, 0x29, 0x12, 0xe2, 0x86, 0x44, 0x91, 0x10, 0x08, 0x2a, 0x64, 0xc4, 0x85,
-	0xdb, 0xc6, 0x9e, 0xb8, 0x56, 0x9c, 0xdd, 0x8d, 0xd7, 0x4e, 0xd5, 0x5f, 0xc7, 0x89, 0x7f, 0xc4,
-	0x91, 0x03, 0xda, 0x5d, 0x3b, 0xfe, 0x68, 0x48, 0xfb, 0xf6, 0x96, 0x79, 0x76, 0xbe, 0xe7, 0x99,
-	0x89, 0x61, 0x1c, 0x26, 0xd2, 0x9f, 0x73, 0x11, 0xe0, 0x4c, 0x26, 0x22, 0x15, 0xa4, 0xa3, 0x7f,
-	0xbb, 0x3f, 0xc3, 0xf1, 0x2f, 0x22, 0x8c, 0xb8, 0x87, 0x9b, 0x0c, 0x55, 0x4a, 0xae, 0x00, 0xd6,
-	0xcc, 0x67, 0x41, 0x90, 0xa0, 0x52, 0xb4, 0x35, 0x69, 0x4d, 0x07, 0x5e, 0x05, 0x21, 0x0e, 0xf4,
-	0x25, 0x53, 0xea, 0x49, 0x24, 0x01, 0x6d, 0x9b, 0xd7, 0x9d, 0xec, 0x7e, 0x05, 0xa3, 0xdc, 0x97,
-	0x92, 0x82, 0x2b, 0x24, 0x13, 0x18, 0x32, 0xdf, 0x47, 0xa5, 0x52, 0xb1, 0x42, 0x9e, 0x7b, 0xab,
-	0x42, 0xee, 0x3f, 0x1d, 0xe8, 0x3c, 0x88, 0x00, 0xc9, 0x09, 0xb4, 0xa3, 0x20, 0xd7, 0x68, 0x47,
-	0x01, 0x21, 0xd0, 0xe1, 0x6c, 0x8d, 0x79, 0x0c, 0xf3, 0x9b, 0x50, 0xe8, 0x15, 0x89, 0x7d, 0x30,
-	0x70, 0x21, 0xea, 0xac, 0xe3, 0x48, 0xa5, 0xc8, 0xa5, 0x48, 0x52, 0xda, 0x99, 0xb4, 0xa6, 0x5d,
-	0xaf, 0x82, 0x90, 0x4b, 0x18, 0xc8, 0x6c, 0x11, 0x47, 0xfe, 0x0a, 0x9f, 0x69, 0xd7, 0xd8, 0x96,
-	0x80, 0xae, 0x09, 0x79, 0x20, 0x45, 0xc4, 0x53, 0x7a, 0x64, 0x6b, 0x2a, 0xe4, 0x46, 0x3f, 0x7a,
-	0x07, 0xfb, 0xd1, 0xaf, 0xf7, 0x43, 0x47, 0xd5, 0x3d, 0x0e, 0x13, 0x91, 0x49, 0x3a, 0xb0, 0x51,
-	0x77, 0x80, 0x7e, 0x8d, 0x94, 0x44, 0x1e, 0x44, 0x3c, 0xa4, 0x30, 0x69, 0x4d, 0xfb, 0x5e, 0x09,
-	0x90, 0xcf, 0xe0, 0x48, 0x0a, 0x95, 0x66, 0x92, 0x0e, 0x8d, 0x61, 0x2e, 0x91, 0x73, 0xe8, 0xca,
-	0x04, 0x33, 0x49, 0x8f, 0x0d, 0x6c, 0x05, 0xed, 0x6b, 0x85, 0x28, 0x59, 0x1c, 0x6d, 0x91, 0x8e,
-	0x4c, 0xf9, 0x25, 0xa0, 0x6b, 0x50, 0x6c, 0x8b, 0xbe, 0xe0, 0xcb, 0x28, 0xa4, 0x27, 0x26, 0x54,
-	0x05, 0xd1, 0xd6, 0x76, 0x26, 0xba, 0x3b, 0x63, 0x9b, 0xe7, 0x0e, 0x30, 0x79, 0xf2, 0x14, 0x93,
-	0x25, 0xf3, 0x91, 0x9e, 0xda, 0xd7, 0x1d, 0xa0, 0x47, 0x1c, 0x33, 0x95, 0xfa, 0x8f, 0xe8, 0xaf,
-	0x22, 0x4e, 0x3f, 0xda, 0x11, 0x57, 0x20, 0xe2, 0xc2, 0xb1, 0x16, 0xd7, 0x22, 0x88, 0x96, 0x11,
-	0x06, 0x94, 0x18, 0x95, 0x1a, 0x46, 0xa6, 0x30, 0xce, 0xd5, 0x8d, 0xe7, 0x2d, 0x8b, 0xe9, 0x99,
-	0xa9, 0xa2, 0x09, 0x1b, 0x6f, 0xc2, 0x67, 0x71, 0x31, 0x91, 0xf3, 0xdc, 0x5b, 0x05, 0xd3, 0x39,
-	0xe9, 0x6e, 0xf9, 0x8f, 0x8c, 0x87, 0xa8, 0xe8, 0x85, 0xcd, 0xa9, 0x02, 0xb9, 0x7f, 0xb5, 0x60,
-	0x7c, 0xaf, 0x3d, 0xff, 0x54, 0x92, 0x95, 0x42, 0x4f, 0x65, 0xa6, 0x6a, 0x43, 0xc3, 0xbe, 0x57,
-	0x88, 0xe4, 0x73, 0x38, 0xe1, 0x88, 0x81, 0x44, 0x4c, 0x32, 0x19, 0xb0, 0xd4, 0xb2, 0xb2, 0xef,
-	0x35, 0x50, 0xf2, 0x05, 0x9c, 0x6a, 0xc4, 0x76, 0x35, 0xd7, 0xfc, 0x60, 0x34, 0x5f, 0xe0, 0x3a,
-	0x47, 0x4d, 0x85, 0x35, 0x2a, 0xc5, 0x42, 0x34, 0x94, 0x1d, 0x78, 0x55, 0xa8, 0xce, 0x8f, 0x6e,
-	0x83, 0x1f, 0xee, 0xdf, 0x2d, 0x18, 0xfd, 0x86, 0x98, 0xa8, 0x5d, 0xfe, 0xef, 0xe7, 0xf8, 0xfb,
-	0xf7, 0xaa, 0x39, 0x8d, 0xde, 0x9e, 0x69, 0x1c, 0xe4, 0xa6, 0x3b, 0x87, 0xd1, 0x7d, 0x82, 0x2c,
-	0x45, 0x7d, 0x05, 0x3c, 0xdc, 0x90, 0x2b, 0x30, 0x87, 0xc9, 0xcc, 0x60, 0x78, 0x07, 0x33, 0x73,
-	0xb1, 0xcc, 0xa3, 0x3d, 0x58, 0x0d, 0x03, 0xf5, 0x16, 0x83, 0x3f, 0x4c, 0xcf, 0x3f, 0x21, 0x42,
-	0xd5, 0xe0, 0xf5, 0x08, 0xf7, 0x30, 0xf4, 0x90, 0x05, 0xa5, 0xff, 0xc3, 0x27, 0xf4, 0x1c, 0xba,
-	0xf6, 0x24, 0xd8, 0xdb, 0x66, 0x05, 0xf7, 0xa6, 0xea, 0xe4, 0xf5, 0x98, 0xbf, 0xc2, 0xe8, 0x07,
-	0x8c, 0xb1, 0x5a, 0xd5, 0xe1, 0xa8, 0x97, 0x30, 0x30, 0x81, 0x1e, 0xca, 0xab, 0x5a, 0x02, 0xee,
-	0x75, 0xdd, 0x9d, 0xfa, 0xff, 0x6d, 0xd0, 0xd5, 0xfe, 0x88, 0x69, 0xce, 0xbd, 0xf7, 0x56, 0xfb,
-	0x6d, 0xd5, 0x89, 0x22, 0xd7, 0xd0, 0xd5, 0x7b, 0xa4, 0xf2, 0x72, 0xcf, 0x6c, 0xb9, 0x35, 0x7e,
-	0x7b, 0x56, 0xc3, 0xfd, 0x12, 0x60, 0xb7, 0xb9, 0x9b, 0x37, 0xb4, 0xa9, 0xd4, 0x56, 0xe4, 0xbb,
-	0xdd, 0x99, 0x49, 0x72, 0xaf, 0xb9, 0xe1, 0x85, 0x35, 0x6c, 0x9c, 0x04, 0xaf, 0xa9, 0x7d, 0xf7,
-	0x6f, 0x1b, 0x86, 0xda, 0xfb, 0xef, 0x98, 0x6c, 0x23, 0x1f, 0xc9, 0x2d, 0x74, 0xcd, 0x3f, 0x1e,
-	0x21, 0xd6, 0x41, 0xf5, 0xaf, 0xd4, 0x39, 0xab, 0x61, 0xf9, 0x96, 0x7e, 0x03, 0x50, 0xd2, 0x97,
-	0xe4, 0x2a, 0xb5, 0x0d, 0x70, 0xf6, 0x80, 0x8a, 0xdc, 0x42, 0xbf, 0xa0, 0x07, 0xf9, 0x68, 0x15,
-	0x2a, 0x9c, 0x73, 0x5e, 0x40, 0x4a, 0x47, 0x2a, 0x69, 0x5c, 0x44, 0xaa, 0x6d, 0x82, 0xb3, 0x07,
-	0x34, 0x76, 0x25, 0x15, 0x0a, 0xbb, 0x1a, 0xd7, 0x9c, 0x3d, 0xa0, 0x22, 0x77, 0xd0, 0x2f, 0x46,
-	0x5a, 0x64, 0x58, 0xe1, 0x89, 0xf3, 0x02, 0x52, 0xb7, 0x2d, 0x72, 0x03, 0xbd, 0xbc, 0xe7, 0xe4,
-	0xb4, 0x31, 0x82, 0x8d, 0xd3, 0x44, 0xd4, 0xf7, 0xf3, 0x3f, 0x6f, 0x42, 0x21, 0xc2, 0x18, 0x67,
-	0xa1, 0x88, 0x19, 0x0f, 0x67, 0x22, 0x09, 0xe7, 0xe6, 0x6b, 0x66, 0x91, 0x2d, 0xe7, 0xe9, 0xb3,
-	0x44, 0x35, 0x5f, 0x71, 0xf1, 0xc4, 0xcd, 0x77, 0x8e, 0x5c, 0x2c, 0x8e, 0xcc, 0xe3, 0xd7, 0xff,
-	0x05, 0x00, 0x00, 0xff, 0xff, 0x04, 0x8b, 0xcd, 0xc5, 0xfd, 0x08, 0x00, 0x00,
+	// 850 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xdd, 0x6a, 0xe3, 0x46,
+	0x14, 0xc6, 0x5e, 0x3b, 0x96, 0x8f, 0xe3, 0x24, 0x3b, 0xc9, 0x96, 0x41, 0x84, 0x10, 0x44, 0x29,
+	0xd9, 0xd2, 0xc4, 0x69, 0x0a, 0xa5, 0x77, 0x85, 0xa6, 0x50, 0x0a, 0xed, 0x52, 0x54, 0x7a, 0xd3,
+	0xbb, 0x89, 0xe6, 0x44, 0x2b, 0xac, 0xcc, 0x8c, 0x35, 0x92, 0x43, 0x1e, 0xa0, 0x8f, 0xd6, 0x37,
+	0xea, 0x65, 0x2f, 0xca, 0xfc, 0xc8, 0xfa, 0x89, 0x9b, 0x64, 0x73, 0xe7, 0xf3, 0xcd, 0xf9, 0x3f,
+	0xdf, 0x39, 0x16, 0xec, 0xa7, 0x85, 0x4a, 0x16, 0x42, 0x72, 0xbc, 0x50, 0x85, 0x2c, 0x25, 0x19,
+	0x99, 0xdf, 0x11, 0x87, 0xdd, 0x5f, 0x64, 0x9a, 0x89, 0x18, 0x57, 0x15, 0xea, 0x92, 0x9c, 0x00,
+	0xdc, 0xb1, 0x84, 0x71, 0x5e, 0xa0, 0xd6, 0x74, 0x70, 0x3a, 0x38, 0x9b, 0xc6, 0x2d, 0x84, 0x84,
+	0x10, 0x28, 0xa6, 0xf5, 0xbd, 0x2c, 0x38, 0x1d, 0xda, 0xd7, 0x8d, 0x4c, 0x28, 0x4c, 0x04, 0x96,
+	0xf7, 0xb2, 0x58, 0xd2, 0x37, 0xf6, 0xa9, 0x16, 0xa3, 0xaf, 0x61, 0xee, 0xa3, 0x68, 0x25, 0x85,
+	0x46, 0x72, 0x0a, 0x33, 0x96, 0x24, 0xa8, 0x75, 0x29, 0x97, 0x28, 0x7c, 0x9c, 0x36, 0x14, 0xfd,
+	0x33, 0x82, 0xd1, 0x07, 0xc9, 0x91, 0xec, 0xc1, 0x30, 0xe3, 0x5e, 0x63, 0x98, 0x71, 0x42, 0x60,
+	0x24, 0xd8, 0x1d, 0xfa, 0xe8, 0xf6, 0xb7, 0x89, 0x5c, 0xa7, 0xec, 0x23, 0xd7, 0xf9, 0x9e, 0x00,
+	0xe4, 0x99, 0x2e, 0x51, 0x28, 0x59, 0x94, 0x74, 0x74, 0x3a, 0x38, 0x1b, 0xc7, 0x2d, 0x84, 0x1c,
+	0xc3, 0x54, 0x55, 0x37, 0x79, 0x96, 0x2c, 0xf1, 0x81, 0x8e, 0xad, 0x6d, 0x03, 0x98, 0x6a, 0x51,
+	0x70, 0x25, 0x33, 0x51, 0xd2, 0x1d, 0x57, 0x6d, 0x2d, 0xf7, 0x3a, 0x35, 0x79, 0xb2, 0x53, 0x41,
+	0xaf, 0x53, 0xc7, 0x30, 0x35, 0xdd, 0x4f, 0x0b, 0x59, 0x29, 0x3a, 0x75, 0x51, 0x37, 0x80, 0x79,
+	0xcd, 0xb4, 0x42, 0xc1, 0x33, 0x91, 0x52, 0x38, 0x1d, 0x9c, 0x05, 0x71, 0x03, 0x90, 0xcf, 0x60,
+	0x47, 0x49, 0x5d, 0x56, 0x8a, 0xce, 0xac, 0xa1, 0x97, 0xc8, 0x11, 0x8c, 0x55, 0x81, 0x95, 0xa2,
+	0xbb, 0x16, 0x76, 0x82, 0xf1, 0xb5, 0x44, 0x54, 0x2c, 0xcf, 0xd6, 0x48, 0xe7, 0xb6, 0xfc, 0x06,
+	0x30, 0x35, 0x68, 0xb6, 0xc6, 0x44, 0x8a, 0xdb, 0x2c, 0xa5, 0x7b, 0x36, 0x54, 0x0b, 0x31, 0xd6,
+	0x6e, 0x26, 0xa6, 0x3b, 0xfb, 0x2e, 0xcf, 0x0d, 0x60, 0xf3, 0x14, 0x25, 0x16, 0xb7, 0x2c, 0x41,
+	0x7a, 0xe0, 0x5e, 0x37, 0x80, 0x19, 0x71, 0xce, 0x74, 0x99, 0x7c, 0xc4, 0x64, 0x99, 0x09, 0xfa,
+	0xd6, 0x8d, 0xb8, 0x05, 0x91, 0x08, 0x76, 0x8d, 0x78, 0x27, 0x79, 0x76, 0x9b, 0x21, 0xa7, 0xc4,
+	0xaa, 0x74, 0x30, 0x72, 0x06, 0xfb, 0x5e, 0xdd, 0x7a, 0x5e, 0xb3, 0x9c, 0x1e, 0xda, 0x2a, 0xfa,
+	0xb0, 0xf5, 0x26, 0x13, 0x96, 0xd7, 0x13, 0x39, 0xf2, 0xde, 0x5a, 0x98, 0xc9, 0xc9, 0x74, 0x2b,
+	0xf9, 0xc8, 0x44, 0x8a, 0x9a, 0xbe, 0x73, 0x39, 0xb5, 0xa0, 0xe8, 0xaf, 0x21, 0xec, 0x5f, 0x1b,
+	0xcf, 0x3f, 0x37, 0x64, 0xa5, 0x30, 0xd1, 0x95, 0xad, 0xda, 0xd2, 0x30, 0x88, 0x6b, 0x91, 0x7c,
+	0x01, 0x7b, 0x02, 0x91, 0x2b, 0xc4, 0xa2, 0x52, 0x9c, 0x95, 0x8e, 0x95, 0x41, 0xdc, 0x43, 0xc9,
+	0x97, 0x70, 0x60, 0x10, 0xd7, 0x55, 0xaf, 0xf9, 0xc6, 0x6a, 0x3e, 0xc2, 0x4d, 0x8e, 0x86, 0x0a,
+	0x77, 0xa8, 0x35, 0x4b, 0xd1, 0x52, 0x76, 0x1a, 0xb7, 0xa1, 0x2e, 0x3f, 0xc6, 0x7d, 0x7e, 0x7c,
+	0x0e, 0x73, 0xe3, 0x73, 0x89, 0x0f, 0x3e, 0xd0, 0x8e, 0xd5, 0xe8, 0x82, 0x66, 0xf2, 0x06, 0xe0,
+	0x98, 0x63, 0x89, 0x96, 0xbd, 0x41, 0xdc, 0x42, 0xa2, 0xbf, 0x07, 0x30, 0xff, 0x0d, 0xb1, 0xd0,
+	0x9b, 0x2e, 0xbc, 0x7e, 0x53, 0x5e, 0xbf, 0x9d, 0xfd, 0x99, 0x4e, 0xb6, 0xcc, 0xf4, 0x49, 0x86,
+	0x47, 0x0b, 0x98, 0x5f, 0x17, 0xc8, 0x4a, 0x34, 0xb7, 0x24, 0xc6, 0x15, 0x39, 0x01, 0x7b, 0xf8,
+	0xec, 0x24, 0x67, 0x57, 0x70, 0x61, 0x2f, 0xa2, 0x7d, 0x74, 0x07, 0xb1, 0x67, 0xa0, 0x5f, 0x62,
+	0xf0, 0x87, 0xed, 0xe9, 0x27, 0x44, 0x68, 0x1b, 0x3c, 0x1f, 0xe1, 0x1a, 0x66, 0x31, 0x32, 0xde,
+	0xf8, 0x7f, 0xfa, 0x44, 0x1f, 0xc1, 0xd8, 0x1d, 0x16, 0x77, 0x21, 0x9d, 0x10, 0x9d, 0xb7, 0x9d,
+	0x3c, 0x1f, 0xf3, 0x57, 0x98, 0xff, 0x68, 0x99, 0xf0, 0xd2, 0xa8, 0xc7, 0x30, 0xb5, 0x81, 0x3e,
+	0x34, 0xb7, 0xb9, 0x01, 0xa2, 0xf7, 0x5d, 0x77, 0xfa, 0xff, 0x77, 0xca, 0x54, 0xfb, 0x13, 0x96,
+	0x9e, 0x7b, 0xaf, 0xad, 0xf6, 0xbb, 0xb6, 0x13, 0x4d, 0xde, 0xc3, 0xd8, 0x6c, 0xa3, 0xf6, 0xe5,
+	0x1e, 0xba, 0x72, 0x3b, 0xfc, 0x8e, 0x9d, 0x46, 0xf4, 0x15, 0xc0, 0x66, 0xff, 0x57, 0x2f, 0x68,
+	0x53, 0xa3, 0xad, 0xc9, 0xf7, 0x9b, 0x63, 0x55, 0x78, 0xaf, 0xde, 0xf0, 0x9d, 0x33, 0xec, 0x1d,
+	0x96, 0xb8, 0xaf, 0x7d, 0xf5, 0xef, 0x10, 0x66, 0xc6, 0xfb, 0xef, 0x58, 0xac, 0xb3, 0x04, 0xc9,
+	0x25, 0x8c, 0xed, 0xff, 0x26, 0x21, 0xce, 0x41, 0xfb, 0xaf, 0x3a, 0x3c, 0xec, 0x60, 0x7e, 0x4b,
+	0xbf, 0x05, 0x68, 0xe8, 0x4b, 0xbc, 0x4a, 0x67, 0x03, 0xc2, 0x2d, 0xa0, 0x26, 0x97, 0x10, 0xd4,
+	0xf4, 0x20, 0x6f, 0x9d, 0x42, 0x8b, 0x73, 0xe1, 0x23, 0x48, 0x9b, 0x48, 0x0d, 0x8d, 0xeb, 0x48,
+	0x9d, 0x4d, 0x08, 0xb7, 0x80, 0xd6, 0xae, 0xa1, 0x42, 0x6d, 0xd7, 0xe1, 0x5a, 0xb8, 0x05, 0xd4,
+	0xe4, 0x0a, 0x82, 0x7a, 0xa4, 0x75, 0x86, 0x2d, 0x9e, 0x84, 0x8f, 0x20, 0x7d, 0x39, 0x20, 0xe7,
+	0x30, 0xf1, 0x3d, 0x27, 0x07, 0xbd, 0x11, 0xac, 0xc2, 0x3e, 0xa2, 0x7f, 0x58, 0xfc, 0x79, 0x9e,
+	0x4a, 0x99, 0xe6, 0x78, 0x91, 0xca, 0x9c, 0x89, 0xf4, 0x42, 0x16, 0xe9, 0xc2, 0x7e, 0x2d, 0xdd,
+	0x54, 0xb7, 0x8b, 0xf2, 0x41, 0xa1, 0x5e, 0x2c, 0x85, 0xbc, 0x17, 0xf6, 0x3b, 0x4a, 0xdd, 0xdc,
+	0xec, 0xd8, 0xc7, 0x6f, 0xfe, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x28, 0x40, 0xb5, 0xd0, 0x5d, 0x09,
+	0x00, 0x00,
 }

+ 3 - 0
grpc/node.proto

@@ -15,6 +15,7 @@ service NodeService {
 message LoginRequest {
   string macaddress = 1;
   string password = 2;
+  string network = 3;
 }
 
 message LoginResponse { string accesstoken = 1; }
@@ -49,6 +50,8 @@ message CheckInResponse {
     bool needconfigupdate = 3;
     string nodemessage = 4;
     bool ispending = 5;
+    bool needkeyupdate = 6;
+    bool needdelete = 7;
 }
 
 message PeersResponse {

+ 155 - 2
main.go

@@ -5,10 +5,18 @@ package main
 
 import (
     "log"
+    "github.com/gravitl/netmaker/models"
     "github.com/gravitl/netmaker/controllers"
+    "github.com/gravitl/netmaker/serverctl"
+    "github.com/gravitl/netmaker/functions"
     "github.com/gravitl/netmaker/mongoconn"
     "github.com/gravitl/netmaker/config"
+    "go.mongodb.org/mongo-driver/bson"
     "fmt"
+    "time"
+    "net/http"
+    "errors"
+    "io/ioutil"
     "os"
     "net"
     "context"
@@ -18,16 +26,30 @@ import (
     nodepb "github.com/gravitl/netmaker/grpc"
     "google.golang.org/grpc"
 )
+
+var ServerGRPC string
+var PortGRPC string
+
 //Start MongoDB Connection and start API Request Handler
 func main() {
 	log.Println("Server starting...")
 	mongoconn.ConnectDatabase()
+	installserver := false
+	if config.Config.Server.CreateDefault {
+		created, err := createDefaultNetwork()
+		if err != nil {
+			fmt.Printf("Error creating default network: %v", err)
+		}
+		if created {
+			installserver = true
+		}
+	}
 
 	var waitgroup sync.WaitGroup
 
 	if config.Config.Server.AgentBackend {
 		waitgroup.Add(1)
-		go runGRPC(&waitgroup)
+		go runGRPC(&waitgroup, installserver)
 	}
 
 	if config.Config.Server.RestBackend {
@@ -42,7 +64,7 @@ func main() {
 }
 
 
-func runGRPC(wg *sync.WaitGroup) {
+func runGRPC(wg *sync.WaitGroup, installserver bool) {
 
 
 	defer wg.Done()
@@ -59,6 +81,27 @@ func runGRPC(wg *sync.WaitGroup) {
         if os.Getenv("GRPC_PORT") != "" {
 		grpcport = ":" + os.Getenv("GRPC_PORT")
         }
+	PortGRPC = grpcport
+	if os.Getenv("BACKEND_URL") == ""  {
+		if config.Config.Server.Host == "" {
+			ServerGRPC, _ = getPublicIP()
+		} else {
+			ServerGRPC = config.Config.Server.Host
+		}
+	} else {
+		ServerGRPC = os.Getenv("BACKEND_URL")
+	}
+	fmt.Println("GRPC Server set to: " + ServerGRPC)
+	fmt.Println("GRPC Port set to: " + PortGRPC)
+	var gconf models.GlobalConfig
+	gconf.ServerGRPC = ServerGRPC
+	gconf.PortGRPC = PortGRPC
+	gconf.Name = "netmaker"
+	err := setGlobalConfig(gconf)
+
+	if err != nil {
+	      log.Fatalf("Unable to set global config: %v", err)
+	}
 
 
 	listener, err := net.Listen("tcp", grpcport)
@@ -87,6 +130,20 @@ func runGRPC(wg *sync.WaitGroup) {
         }()
         fmt.Println("Agent Server succesfully started on port " + grpcport + " (gRPC)")
 
+	if installserver {
+			fmt.Println("Adding server to default network")
+                        success, err := serverctl.AddNetwork(config.Config.Server.DefaultNetName)
+                        if err != nil || !success {
+                                fmt.Printf("Error adding to default network: %v", err)
+				fmt.Println("Unable to add server to network. Continuing.")
+			} else {
+                                fmt.Println("Server successfully added to default network.")
+			}
+	}
+        fmt.Println("Setup complete. You are ready to begin using netmaker.")
+
+
+
         // Right way to stop the server using a SHUTDOWN HOOK
         // Create a channel to receive OS signals
         c := make(chan os.Signal)
@@ -108,6 +165,102 @@ func runGRPC(wg *sync.WaitGroup) {
         mongoconn.Client.Disconnect(context.TODO())
         fmt.Println("MongoDB connection closed.")
 }
+func setGlobalConfig(globalconf models.GlobalConfig) (error) {
+
+        collection := mongoconn.Client.Database("netmaker").Collection("config")
+        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+
+	_, err := functions.GetGlobalConfig()
+	if err != nil {
+		_, err := collection.InsertOne(ctx, globalconf)
+		defer cancel()
+		if err != nil {
+			return err
+		}
+	} else {
+		filter := bson.M{"name": "netmaker"}
+		update := bson.D{
+			{"$set", bson.D{
+				{"servergrpc", globalconf.ServerGRPC},
+				{"portgrpc", globalconf.PortGRPC},
+			}},
+		}
+		err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&globalconf)
+	}
+	return nil
+}
+
+func createDefaultNetwork() (bool, error) {
+
+	iscreated := false
+	exists, err := functions.GroupExists(config.Config.Server.DefaultNetName)
+
+	if exists || err != nil {
+		fmt.Println("Default group already exists")
+		fmt.Println("Skipping default group create")
+		return iscreated, err
+	} else {
+
+	var group models.Group
+
+	group.NameID = config.Config.Server.DefaultNetName
+	group.AddressRange = config.Config.Server.DefaultNetRange
+	group.DisplayName = config.Config.Server.DefaultNetName
+        group.SetDefaults()
+        group.SetNodesLastModified()
+        group.SetGroupLastModified()
+        group.KeyUpdateTimeStamp = time.Now().Unix()
+	allow := true
+	group.AllowManualSignUp = &allow
+
+	fmt.Println("Creating default group.")
+
+
+        collection := mongoconn.Client.Database("netmaker").Collection("groups")
+        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+
+
+        // insert our group into the group table
+        _, err = collection.InsertOne(ctx, group)
+        defer cancel()
+
+	}
+	if err == nil {
+		iscreated = true
+	}
+	return iscreated, err
+
+
+}
+
+
+func getPublicIP() (string, error) {
+
+        iplist := []string{"https://ifconfig.me", "http://api.ipify.org", "http://ipinfo.io/ip"}
+        endpoint := ""
+        var err error
+            for _, ipserver := range iplist {
+                resp, err := http.Get(ipserver)
+                if err != nil {
+                        continue
+                }
+                defer resp.Body.Close()
+                if resp.StatusCode == http.StatusOK {
+                        bodyBytes, err := ioutil.ReadAll(resp.Body)
+                        if err != nil {
+                                continue
+                        }
+                        endpoint = string(bodyBytes)
+                        break
+                }
+
+        }
+        if err == nil && endpoint == "" {
+                err =  errors.New("Public Address Not Found.")
+        }
+        return endpoint, err
+}
+
 
 func authServerUnaryInterceptor() grpc.ServerOption {
 	return grpc.UnaryInterceptor(controller.AuthServerUnaryInterceptor)

+ 1 - 0
models/group.go

@@ -19,6 +19,7 @@ type Group struct {
         DefaultListenPort      int32 `json:"defaultlistenport,omitempty" bson:"defaultlistenport,omitempty" validate:"omitempty,numeric,min=1024,max=65535"`
         DefaultPostUp  string `json:"defaultpostup" bson:"defaultpostup"`
         DefaultPreUp   string `json:"defaultpreup" bson:"defaultpreup"`
+        KeyUpdateTimeStamp      int64 `json:"keyupdatetimestamp" bson:"keyupdatetimestamp"`
         DefaultKeepalive int32 `json:"defaultkeepalive" bson:"defaultkeepalive" validate: "omitempty,numeric,max=1000"`
         DefaultSaveConfig      *bool `json:"defaultsaveconfig" bson:"defaultsaveconfig"`
 	AccessKeys	[]AccessKey `json:"accesskeys" bson:"accesskeys"`

+ 9 - 0
models/node.go

@@ -31,6 +31,8 @@ type Node struct {
 	AccessKey	string `json:"accesskey" bson:"accesskey"`
 	Interface	string `json:"interface" bson:"interface"`
 	LastModified	int64 `json:"lastmodified" bson:"lastmodified"`
+	KeyUpdateTimeStamp	int64 `json:"keyupdatetimestamp" bson:"keyupdatetimestamp"`
+	ExpirationDateTime	int64 `json:"expdatetime" bson:"expdatetime"`
 	LastPeerUpdate	int64 `json:"lastpeerupdate" bson:"lastpeerupdate"`
 	LastCheckIn	int64 `json:"lastcheckin" bson:"lastcheckin"`
 	MacAddress	string `json:"macaddress" bson:"macaddress" validate:"required,macaddress_valid,macaddress_unique"`
@@ -81,6 +83,11 @@ func(node *Node) SetLastPeerUpdate(){
         node.LastPeerUpdate = time.Now().Unix()
 }
 
+func(node *Node) SetExpirationDateTime(){
+        node.ExpirationDateTime = time.Unix(1<<63-62135596801, 999999999).Unix()
+}
+
+
 func(node *Node) SetDefaultName(){
     if node.Name == "" {
         nodeid := StringWithCharset(5, charset)
@@ -96,6 +103,8 @@ func(node *Node) SetDefaults() {
     //TODO: Maybe I should make Group a part of the node struct. Then we can just query the Group object for stuff.
     parentGroup, _ := node.GetGroup()
 
+    node.ExpirationDateTime = time.Unix(1<<63-62135596801, 999999999).Unix()
+
     if node.ListenPort == 0 {
         node.ListenPort = parentGroup.DefaultListenPort
     }

+ 10 - 0
models/structs.go

@@ -64,6 +64,7 @@ type SuccessResponse struct {
 type AccessKey struct {
     Name string `json:"name" bson:"name"`
     Value string `json:"value" bson:"value"`
+    AccessString string `json:"accessstring" bson:"accessstring"`
     Uses int `json:"uses" bson:"uses"`
 }
 
@@ -72,10 +73,19 @@ type DisplayKey struct {
     Uses int `json:"uses" bson:"uses"`
 }
 
+type GlobalConfig struct {
+    Name string `json:"name" bson:"name"`
+    PortGRPC string `json:"portgrpc" bson:"portgrpc"`
+    ServerGRPC string `json:"servergrpc" bson:"servergrpc"`
+}
+
+
 type CheckInResponse struct{
     Success bool `json:"success" bson:"success"`
     NeedPeerUpdate bool `json:"needpeerupdate" bson:"needpeerupdate"`
     NeedConfigUpdate bool `json:"needconfigupdate" bson:"needconfigupdate"`
+    NeedKeyUpdate bool `json:"needkeyupdate" bson:"needkeyupdate"`
+    NeedDelete bool `json:"needdelete" bson:"needdelete"`
     NodeMessage string `json:"nodemessage" bson:"nodemessage"`
     IsPending bool `json:"ispending" bson:"ispending"`
 }

+ 1 - 1
netclient-install.sh

@@ -7,7 +7,7 @@ set -e
 
 
 
-wget -O netclient https://github.com/gravitl/netmaker/releases/download/v0.1/netclient
+wget -O netclient https://github.com/gravitl/netmaker/releases/download/v0.1/netclient netclient
 chmod +x netclient
 sudo ./netclient -c install -s $SERVER_URL -g $NET_NAME -k $KEY
 rm -f netclient

+ 32 - 18
netclient/config/config.go

@@ -3,18 +3,20 @@ package config
 import (
 //  "github.com/davecgh/go-spew/spew"
   "os"
+  "errors"
   "fmt"
   "log"
   "gopkg.in/yaml.v3"
   //homedir "github.com/mitchellh/go-homedir"
 )
 
-var Config *ClientConfig
+//var Config *ClientConfig
 
 // Configurations exported
 type ClientConfig struct {
 	Server ServerConfig `yaml:"server"`
 	Node NodeConfig `yaml:"node"`
+	Network string
 }
 type ServerConfig struct {
         Address string `yaml:"address"`
@@ -41,7 +43,11 @@ type NodeConfig struct {
 }
 
 //reading in the env file
-func Write(config *ClientConfig) error{
+func Write(config *ClientConfig, network string) error{
+	if network == "" {
+		err := errors.New("No network provided. Exiting.")
+		return err
+	}
 	nofile := false
         //home, err := homedir.Dir()
         _, err := os.Stat("/etc/netclient") 
@@ -55,11 +61,11 @@ func Write(config *ClientConfig) error{
         if err != nil {
                 log.Fatal(err)
         }
-        file := fmt.Sprintf(home + "/.netconfig")
+        file := fmt.Sprintf(home + "/netconfig-" + network)
         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...")
+                //fmt.Println("Could not access " + home + "/netconfig,  proceeding...")
         }
         defer f.Close()
 
@@ -71,7 +77,7 @@ func Write(config *ClientConfig) error{
                 }
         } else {
 
-		newf, err := os.Create(home + "/.netconfig")
+		newf, err := os.Create(home + "/netconfig-" + network)
 		err = yaml.NewEncoder(newf).Encode(config)
 		defer newf.Close()
 		if err != nil {
@@ -82,7 +88,11 @@ func Write(config *ClientConfig) error{
 
         return err
 }
-func WriteServer(server string, accesskey string) error{
+func WriteServer(server string, accesskey string, network string) error{
+        if network == "" {
+                err := errors.New("No network provided. Exiting.")
+                return err
+        }
         nofile := false
         //home, err := homedir.Dir()
         _, err := os.Stat("/etc/netclient")
@@ -94,12 +104,12 @@ func WriteServer(server string, accesskey string) error{
         }
         home := "/etc/netclient"
 
-	file := fmt.Sprintf(home + "/.netconfig")
+	file := fmt.Sprintf(home + "/netconfig-" + network)
         //f, err := os.Open(file)
         f, err := os.OpenFile(file, os.O_CREATE|os.O_RDWR, 0666)
 	//f, err := ioutil.ReadFile(file)
         if err != nil {
-		fmt.Println("couldnt open netconfig")
+		fmt.Println("couldnt open netconfig-" + network)
 		fmt.Println(err)
                 nofile = true
 		//err = nil
@@ -111,7 +121,7 @@ func WriteServer(server string, accesskey string) error{
 	var cfg ClientConfig
 
         if !nofile {
-		fmt.Println("Writing to existing config file at " + home + "/.netconfig")
+		fmt.Println("Writing to existing config file at " + home + "/netconfig-" + network)
                 decoder := yaml.NewDecoder(f)
                 err = decoder.Decode(&cfg)
 		//err = yaml.Unmarshal(f, &cfg)
@@ -145,12 +155,12 @@ func WriteServer(server string, accesskey string) error{
                         return err
                 }
 	} else {
-                fmt.Println("Creating new config file at " + home + "/.netconfig")
+                fmt.Println("Creating new config file at " + home + "/netconfig-" + network)
 
                 cfg.Server.Address = server
                 cfg.Server.AccessKey = accesskey
 
-                newf, err := os.Create(home + "/.netconfig")
+                newf, err := os.Create(home + "/netconfig-" + network)
                 err = yaml.NewEncoder(newf).Encode(cfg)
                 defer newf.Close()
                 if err != nil {
@@ -168,7 +178,7 @@ func(config *ClientConfig) ReadConfig() {
 	nofile := false
 	//home, err := homedir.Dir()
 	home := "/etc/netclient"
-	file := fmt.Sprintf(home + "/.netconfig")
+	file := fmt.Sprintf(home + "/netconfig-" + config.Network)
 	//f, err := os.Open(file)
         f, err := os.OpenFile(file, os.O_RDONLY, 0666)
 	if err != nil {
@@ -194,12 +204,15 @@ func(config *ClientConfig) ReadConfig() {
 	}
 }
 
-
-func readConfig() *ClientConfig {
+func ReadConfig(network string) (*ClientConfig, error) {
+        if network == "" {
+                err := errors.New("No network provided. Exiting.")
+                return nil, err
+        }
 	nofile := false
 	//home, err := homedir.Dir()
 	home := "/etc/netclient"
-	file := fmt.Sprintf(home + "/.netconfig")
+	file := fmt.Sprintf(home + "/netconfig-" + network)
 	f, err := os.Open(file)
 	if err != nil {
 		nofile = true
@@ -213,13 +226,14 @@ func readConfig() *ClientConfig {
 		err = decoder.Decode(&cfg)
 		if err != nil {
 			fmt.Println("trouble decoding file")
-			log.Fatal(err)
+			return nil, err
 		}
 	}
-	return &cfg
+	return &cfg, err
 }
-
+/*
 func init() {
   Config = readConfig()
 }
+*/
 

+ 15 - 10
netclient/functions/auth.go

@@ -14,17 +14,17 @@ import (
 )
 
 // CreateJWT func will used to create the JWT while signing in and signing out
-func SetJWT(client nodepb.NodeServiceClient) (context.Context, error) {
+func SetJWT(client nodepb.NodeServiceClient, network string) (context.Context, error) {
 		//home, err := os.UserHomeDir()
 		home := "/etc/netclient"
-		tokentext, err := ioutil.ReadFile(home + "/.nettoken")
+		tokentext, err := ioutil.ReadFile(home + "/nettoken-"+network)
                 if err != nil {
 			fmt.Println("Error reading token. Logging in to retrieve new token.")
-			err = AutoLogin(client)
+			err = AutoLogin(client, network)
 			if err != nil {
                                 return nil, status.Errorf(codes.Unauthenticated, fmt.Sprintf("Something went wrong with Auto Login: %v", err))
                         }
-			tokentext, err = ioutil.ReadFile(home + "/.nettoken")
+			tokentext, err = ioutil.ReadFile(home + "/nettoken-"+network)
 			if err != nil {
 				return nil, status.Errorf(codes.Unauthenticated, fmt.Sprintf("Something went wrong: %v", err))
 			}
@@ -38,13 +38,18 @@ func SetJWT(client nodepb.NodeServiceClient) (context.Context, error) {
 		return ctx, nil
 }
 
-func AutoLogin(client nodepb.NodeServiceClient) error {
+func AutoLogin(client nodepb.NodeServiceClient, network string) error {
 	        //home, err := os.UserHomeDir()
 		home := "/etc/netclient"
-		nodecfg := config.Config.Node
-                login := &nodepb.LoginRequest{
-                        Password: nodecfg.Password,
-                        Macaddress: nodecfg.MacAddress,
+		//nodecfg := config.Config.Node
+                cfg, err := config.ReadConfig(network) 
+		if err != nil {
+			return err
+		}
+		login := &nodepb.LoginRequest{
+                        Password: cfg.Node.Password,
+                        Macaddress: cfg.Node.MacAddress,
+                        Network: network,
                 }
     // RPC call
                 res, err := client.Login(context.TODO(), login)
@@ -52,7 +57,7 @@ func AutoLogin(client nodepb.NodeServiceClient) error {
                         return err
                 }
                 tokenstring := []byte(res.Accesstoken)
-                err = ioutil.WriteFile(home + "/.nettoken", tokenstring, 0644)
+                err = ioutil.WriteFile(home + "/nettoken-"+network, tokenstring, 0644)
                 if err != nil {
                         return err
                 }

+ 286 - 68
netclient/functions/common.go

@@ -19,6 +19,7 @@ import (
         nodepb "github.com/gravitl/netmaker/grpc"
 	"golang.zx2c4.com/wireguard/wgctrl"
         "google.golang.org/grpc"
+	"encoding/base64"
 	"google.golang.org/grpc/metadata"
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 	//homedir "github.com/mitchellh/go-homedir"
@@ -28,8 +29,80 @@ var (
         wcclient nodepb.NodeServiceClient
 )
 
-func Install(accesskey string, password string, server string, group string, noauto bool) error {
+func ListPorts() error{
+	wgclient, err := wgctrl.New()
+	if err  != nil {
+		return err
+	}
+	devices, err := wgclient.Devices()
+        if err  != nil {
+                return err
+        }
+	fmt.Println("Here are your ports:")
+	 for _, i := range devices {
+		fmt.Println(i.ListenPort)
+	}
+	return err
+}
 
+func GetFreePort(rangestart int32) (int32, error){
+        wgclient, err := wgctrl.New()
+        if err  != nil {
+                return 0, err
+        }
+        devices, err := wgclient.Devices()
+        if err  != nil {
+                return 0, err
+        }
+	var portno int32
+	portno = 0
+	for  x := rangestart; x <= 60000; x++ {
+		conflict := false
+		for _, i := range devices {
+			if int32(i.ListenPort) == x {
+				conflict = true
+				break;
+			}
+		}
+		if conflict {
+			continue
+		}
+		portno = x
+		break
+	}
+        return portno, err
+}
+
+func Install(accesskey string, password string, server string, group string, noauto bool, accesstoken string,  inputname string) error {
+
+	tserver := ""
+	tnetwork := ""
+	tkey := ""
+
+	if FileExists("/etc/systemd/system/netclient-"+group+".timer") ||
+	   FileExists("/etc/netclient/netconfig-"+group) {
+		   err := errors.New("ALREADY_INSTALLED. Netclient appears to already be installed for network " + group + ". To re-install, please remove by executing 'sudo netclient -c remove -n " + group + "'. Then re-run the install command.")
+		return err
+	}
+
+	if accesstoken != "" && accesstoken != "badtoken" {
+		btoken, err := base64.StdEncoding.DecodeString(accesstoken)
+		if err  != nil {
+			log.Fatalf("Something went wrong decoding your token: %v", err)
+		}
+		token := string(btoken)
+		tokenvals := strings.Split(token, ".")
+		tserver = tokenvals[0]
+		tnetwork = tokenvals[1]
+		tkey = tokenvals[2]
+		server = tserver
+		group = tnetwork
+		accesskey = tkey
+		fmt.Println("Decoded values from token:")
+		fmt.Println("    Server: " + tserver)
+		fmt.Println("    Network: " + tnetwork)
+		fmt.Println("    Key: " + tkey)
+	}
         wgclient, err := wgctrl.New()
 
         if err != nil {
@@ -37,28 +110,38 @@ func Install(accesskey string, password string, server string, group string, noa
         }
         defer wgclient.Close()
 
-	nodecfg := config.Config.Node
-	servercfg := config.Config.Server
+	cfg, err := config.ReadConfig(group)
+        if err != nil {
+                log.Printf("No Config Yet. Will Write: %v", err)
+        }
+	nodecfg := cfg.Node
+	servercfg := cfg.Server
 	fmt.Println("SERVER SETTINGS:")
 
 	if server == "" {
-		if servercfg.Address == "" {
+		if servercfg.Address == "" && tserver == "" {
 			log.Fatal("no server provided")
 		} else {
                         server = servercfg.Address
 		}
 	}
+	if tserver != "" {
+		server = tserver
+	}
        fmt.Println("     Server: " + server)
 
 	if accesskey == "" {
-		if servercfg.AccessKey == "" {
+		if servercfg.AccessKey == "" && tkey == "" {
 			fmt.Println("no access key provided.Proceeding anyway.")
 		} else {
 			accesskey = servercfg.AccessKey
 		}
 	}
+	if tkey != "" {
+		accesskey = tkey
+	}
        fmt.Println("     AccessKey: " + accesskey)
-       err = config.WriteServer(server, accesskey)
+       err = config.WriteServer(server, accesskey, group)
         if err != nil {
 		fmt.Println("Error encountered while writing Server Config.")
                 return err
@@ -77,13 +160,16 @@ func Install(accesskey string, password string, server string, group string, noa
        fmt.Println("     Password: " + password)
 
         if group == "badgroup" {
-                if nodecfg.Group == "" {
+                if nodecfg.Group == "" && tnetwork == "" {
                         //create error here                
                         log.Fatal("no group provided")
                 } else {
 			group = nodecfg.Group
 		}
         }
+	if tnetwork != "" {
+		group =  tnetwork
+	}
        fmt.Println("     Group: " + group)
 
 	var macaddress string
@@ -152,6 +238,9 @@ func Install(accesskey string, password string, server string, group string, noa
         if nodecfg.Name != "" {
                 name = nodecfg.Name
         }
+	if inputname != "" && inputname != "noname" {
+		name = inputname
+	}
        fmt.Println("     Name: " + name)
 
 
@@ -169,7 +258,14 @@ func Install(accesskey string, password string, server string, group string, noa
 	if nodecfg.Port != 0 {
 		listenport = nodecfg.Port
 	}
-       fmt.Println("     Port: " + string(listenport))
+	if listenport == 0 {
+		listenport, err = GetFreePort(51821)
+		if err != nil {
+			fmt.Printf("Error retrieving port: %v", err)
+		}
+	}
+       fmt.Printf("     Port: %v", listenport)
+       fmt.Println("")
 
 	if nodecfg.PrivateKey != "" {
 		privkeystring = nodecfg.PrivateKey
@@ -278,19 +374,19 @@ func Install(accesskey string, password string, server string, group string, noa
 		fmt.Println("Awaiting approval from Admin before configuring WireGuard.")
 	        if !noauto {
 			fmt.Println("Configuring Netmaker Service.")
-			err = ConfigureSystemD()
+			err = ConfigureSystemD(group)
 			return err
 		}
 
 	}
 
-	peers, err := getPeers(node.Macaddress, node.Nodegroup, server)
+	peers, err := getPeers(node.Macaddress, group, server)
 
 	if err != nil {
                 return err
         }
 	fmt.Println("retrived peers, setting wireguard config.")
-	err = storePrivKey(privkeystring)
+	err = storePrivKey(privkeystring, group)
         if err != nil {
                 return err
         }
@@ -299,7 +395,7 @@ func Install(accesskey string, password string, server string, group string, noa
                 return err
         }
 	if !noauto {
-		err = ConfigureSystemD()
+		err = ConfigureSystemD(group)
 	}
         if err != nil {
                 return err
@@ -336,8 +432,16 @@ func getPublicIP() (string, error) {
 }
 
 func modConfig(node *nodepb.Node) error{
-	modconfig := config.Config
-	modconfig.ReadConfig()
+	group := node.Nodegroup
+	if group == "" {
+		return errors.New("No Group Provided")
+	}
+	//modconfig := config.Config
+	modconfig, err := config.ReadConfig(group)
+	//modconfig.ReadConfig()
+	if err != nil {
+		return err
+	}
 	nodecfg := modconfig.Node
 	if node.Name != ""{
 		nodecfg.Name = node.Name
@@ -376,7 +480,7 @@ func modConfig(node *nodepb.Node) error{
                 nodecfg.PostChanges = node.Postchanges
         }
 	modconfig.Node = nodecfg
-	err := config.Write(modconfig)
+	err = config.Write(modconfig, group)
 	return err
 }
 
@@ -395,15 +499,7 @@ func getMacAddr() ([]string, error) {
     }
     return as, nil
 }
-/*
-func read(macaddress string,  group string) error {
-	//this would be  used  for retrieving state as set by the server.
-}
-
-func checkLocalConfigChange() error {
 
-}
-*/
 
 func initWireguard(node *nodepb.Node, privkey string, peers []wgtypes.PeerConfig) error  {
 
@@ -417,8 +513,14 @@ func initWireguard(node *nodepb.Node, privkey string, peers []wgtypes.PeerConfig
         }
 
         wgclient, err := wgctrl.New()
-	modcfg := config.Config
-	modcfg.ReadConfig()
+	//modcfg := config.Config
+	//modcfg.ReadConfig()
+	modcfg, err := config.ReadConfig(node.Nodegroup)
+        if err != nil {
+                return err
+        }
+
+
 	nodecfg := modcfg.Node
 	fmt.Println("beginning local WG config")
 
@@ -535,16 +637,86 @@ func initWireguard(node *nodepb.Node, privkey string, peers []wgtypes.PeerConfig
 	return err
 }
 
-func setWGConfig() error {
-        servercfg := config.Config.Server
-        nodecfg := config.Config.Node
-        node := getNode()
+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)
+
+	fmt.Println("Authenticating with GRPC Server")
+        ctx, err = SetJWT(wcclient, network)
+        if err != nil {
+                fmt.Printf("Failed to authenticate: %v", err)
+                return err
+        }
+        fmt.Println("Authenticated")
+
+
+	node := 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 = modConfig(&node)
+        if err != nil {
+                return err
+        }
+
+
+	postnode := 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 := getNode(network)
 
 	peers, err := getPeers(node.Macaddress, nodecfg.Group, servercfg.Address)
         if err != nil {
                 return err
         }
-	privkey, err := retrievePrivKey()
+	privkey, err := retrievePrivKey(network)
         if err != nil {
                 return err
         }
@@ -557,14 +729,14 @@ func setWGConfig() error {
 	return err
 }
 
-func storePrivKey(key string) error{
+func storePrivKey(key string, network string) error{
 	d1 := []byte(key)
-	err := ioutil.WriteFile("/root/.wckey", d1, 0644)
+	err := ioutil.WriteFile("/etc/netclient/wgkey-" + network, d1, 0644)
 	return err
 }
 
-func retrievePrivKey() (string, error) {
-	dat, err := ioutil.ReadFile("/root/.wckey")
+func retrievePrivKey(network string) (string, error) {
+	dat, err := ioutil.ReadFile("/etc/netclient/wgkey-" + network)
 	return string(dat), err
 }
 
@@ -612,10 +784,14 @@ func getPrivateAddr() (string, error) {
 }
 
 
-func CheckIn() error {
-	node := getNode()
-        nodecfg := config.Config.Node
-	servercfg := config.Config.Server
+func CheckIn(network string) error {
+	node := 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
@@ -661,13 +837,17 @@ func CheckIn() error {
                         return err
                         log.Fatalf("Error: %v", err)
                 }
-                err = setWGConfig()
+                err = setWGConfig(network)
                 if err != nil {
                         return err
                         log.Fatalf("Error: %v", err)
                 }
-	        node = getNode()
-		nodecfg = config.Config.Node
+	        node = getNode(network)
+		cfg, err := config.ReadConfig(network)
+		if err != nil {
+			return err
+		}
+		nodecfg = cfg.Node
 	}
 
 
@@ -683,7 +863,7 @@ func CheckIn() error {
 
         ctx := context.Background()
         fmt.Println("Authenticating with GRPC Server")
-        ctx, err = SetJWT(wcclient)
+        ctx, err = SetJWT(wcclient, network)
         if err != nil {
                 fmt.Printf("Failed to authenticate: %v", err)
 		return err
@@ -702,7 +882,7 @@ func CheckIn() error {
         )
         if err != nil {
         if  checkinres != nil && checkinres.Checkinresponse.Ispending {
-                fmt.Println("Node is in pending status. Waiting for Admin approval of  node before making furtherupdates.")
+                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)
@@ -710,11 +890,11 @@ func CheckIn() error {
         }
 	fmt.Println("Checked in.")
 	if  checkinres.Checkinresponse.Ispending {
-		fmt.Println("Node is in pending status. Waiting for Admin approval of  node before making furtherupdates.")
+		fmt.Println("Node is in pending status. Waiting for Admin approval of  node before making further updates.")
 		return err
 	}
 
-                newinterface := getNode().Interface
+                newinterface := getNode(network).Interface
                 readreq := &nodepb.ReadNodeReq{
                         Macaddress: node.Macaddress,
                         Group: node.Nodegroup,
@@ -736,7 +916,7 @@ func CheckIn() error {
                                 fmt.Println("ERROR DELETING INTERFACE: " + currentiface)
                         }
                 }
-                err = setWGConfig()
+                err = setWGConfig(network)
 		}
 
 	if checkinres.Checkinresponse.Needconfigupdate {
@@ -756,7 +936,7 @@ func CheckIn() error {
 			return err
                         log.Fatalf("Error: %v", err)
                 }
-                err = setWGConfig()
+                err = setWGConfig(network)
                 if err != nil {
 			return err
                         log.Fatalf("Error: %v", err)
@@ -765,7 +945,7 @@ func CheckIn() error {
 	} else if nodecfg.PostChanges == "true" {
                 fmt.Println("Node has requested to update remote config.")
                 fmt.Println("Posting local config to remote server.")
-		postnode := getNode()
+		postnode := getNode(network)
 
 		req := &nodepb.UpdateNodeReq{
                                Node: &postnode,
@@ -781,29 +961,47 @@ func CheckIn() error {
 			return err
                         log.Fatalf("Error: %v", err)
                 }
-		err = setWGConfig()
+		err = 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 = 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 = setWGConfig()
+                err = 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 := Remove(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 = setWGConfig()
+                err = setWGKeyConfig(network, servercfg.Address)
                 if err != nil {
                         return err
                         log.Fatalf("Error: %v", err)
@@ -829,9 +1027,13 @@ func needInterfaceUpdate(ctx context.Context, mac string, group string, iface st
 		return iface != oldiface, oldiface, err
 }
 
-func getNode() nodepb.Node {
-	modcfg := config.Config
-	modcfg.ReadConfig()
+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
 
@@ -856,10 +1058,14 @@ func getNode() nodepb.Node {
 
 
 
-func Remove() error {
+func Remove(network string) error {
         //need to  implement checkin on server side
-        servercfg := config.Config.Server
-        node := config.Config.Node
+        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)
 
 
@@ -875,7 +1081,7 @@ func Remove() error {
 
         ctx := context.Background()
         fmt.Println("Authenticating with GRPC Server")
-        ctx, err = SetJWT(wcclient)
+        ctx, err = SetJWT(wcclient, network)
         if err != nil {
                 //return err
                 log.Printf("Failed to authenticate: %v", err)
@@ -900,11 +1106,11 @@ func Remove() error {
 	}
 	}
 	}
-	err = WipeLocal()
+	err = WipeLocal(network)
 	if err != nil {
                 log.Printf("Unable to wipe local config: %v", err)
 	}
-	err =  RemoveSystemDServices()
+	err =  RemoveSystemDServices(network)
         if err != nil {
                 return err
                 log.Printf("Unable to remove systemd services: %v", err)
@@ -915,20 +1121,30 @@ func Remove() error {
 	return nil
 }
 
-func WipeLocal() error{
-        nodecfg := config.Config.Node
+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"
-	err := os.Remove(home + "/.netconfig")
+	err = os.Remove(home + "/netconfig-" + network)
+        if  err  !=  nil {
+                fmt.Println(err)
+        }
+        err = os.Remove(home + "/nettoken-" + network)
         if  err  !=  nil {
                 fmt.Println(err)
         }
-        err = os.Remove(home + "/.nettoken")
+
+        err = os.Remove(home + "/wgkey-" + network)
         if  err  !=  nil {
                 fmt.Println(err)
         }
+
         ipExec, err := exec.LookPath("ip")
 
 	if ifacename != "" {
@@ -967,9 +1183,11 @@ func getPeers(macaddress string, group string, server string) ([]wgtypes.PeerCon
         //need to  implement checkin on server side
 	var peers []wgtypes.PeerConfig
 	var wcclient nodepb.NodeServiceClient
-        modcfg := config.Config
-        modcfg.ReadConfig()
-        nodecfg := modcfg.Node
+        cfg, err := config.ReadConfig(group)
+        if err != nil {
+		log.Fatalf("Issue retrieving config for network: " + group +  ". Please investigate: %v", err)
+        }
+        nodecfg := cfg.Node
 	keepalive := nodecfg.KeepAlive
 	keepalivedur, err := time.ParseDuration(strconv.FormatInt(int64(keepalive), 10) + "s")
         if err != nil {
@@ -992,7 +1210,7 @@ func getPeers(macaddress string, group string, server string) ([]wgtypes.PeerCon
         }
         ctx := context.Background()
 	fmt.Println("Authenticating with GRPC Server")
-	ctx, err = SetJWT(wcclient)
+	ctx, err = SetJWT(wcclient, group)
         if err != nil {
 		fmt.Println("Failed to authenticate.")
                 return peers, err

+ 82 - 38
netclient/functions/local.go

@@ -11,7 +11,16 @@ import (
         "os/exec"
 )
 
-func ConfigureSystemD() error {
+
+func FileExists(f string) bool {
+    info, err := os.Stat(f)
+    if os.IsNotExist(err) {
+        return false
+    }
+    return !info.IsDir()
+}
+
+func ConfigureSystemD(network string) error {
 	/*
 	path, err := os.Getwd()
 	if err != nil {
@@ -36,17 +45,23 @@ func ConfigureSystemD() error {
                 return err
         }
 
+	if !FileExists("/usr/local/bin/netclient") {
+		os.Symlink("/etc/netclient/netclient","/usr/local/bin/netclient")
+	/*
 	_, err = copy(binarypath, "/usr/local/bin/netclient")
 	if err != nil {
 		log.Println(err)
 		return err
 	}
+	*/
+	}
+	if !FileExists("/etc/netclient/netclient") {
         _, err = copy(binarypath, "/etc/netclient/netclient")
         if err != nil {
                 log.Println(err)
                 return err
         }
-
+	}
 
 
 	systemservice := `[Unit]
@@ -54,8 +69,8 @@ Description=Regularly checks for updates in peers and local config
 Wants=netclient.timer
 
 [Service]
-Type=oneshot
-ExecStart=/etc/netclient/netclient -c checkin
+Type=simple
+ExecStart=/etc/netclient/netclient -c checkin -n %i
 
 [Install]
 WantedBy=multi-user.target
@@ -63,45 +78,62 @@ WantedBy=multi-user.target
 
 	systemtimer := `[Unit]
 Description=Calls the Netmaker Mesh Client Service
-Requires=netclient.service
+
+`
+systemtimer = systemtimer + "Requires=netclient@"+network+".service"
+
+systemtimer = systemtimer +
+`
 
 [Timer]
-Unit=netclient.service
+
+`
+systemtimer = systemtimer + "Unit=netclient@"+network+".service"
+
+systemtimer = systemtimer +
+`
+
 OnCalendar=*:*:0/30
 
 [Install]
 WantedBy=timers.target
 `
 
+
 	servicebytes := []byte(systemservice)
 	timerbytes := []byte(systemtimer)
 
-	err = ioutil.WriteFile("/etc/systemd/system/netclient.service", servicebytes, 0644)
+	if !FileExists("/etc/systemd/system/[email protected]") {
+	err = ioutil.WriteFile("/etc/systemd/system/[email protected]", servicebytes, 0644)
         if err != nil {
                 log.Println(err)
                 return err
         }
+	}
 
-        err = ioutil.WriteFile("/etc/systemd/system/netclient.timer", timerbytes, 0644)
+        if !FileExists("/etc/systemd/system/netclient-"+network+".timer") {
+        err = ioutil.WriteFile("/etc/systemd/system/netclient-"+network+".timer", timerbytes, 0644)
         if err != nil {
                 log.Println(err)
                 return err
         }
-
+	}
         sysExec, err := exec.LookPath("systemctl")
 
         cmdSysEnableService := &exec.Cmd {
                 Path: sysExec,
-                Args: []string{ sysExec, "enable", "netclient.service" },
+                Args: []string{ sysExec, "enable", "netclient@.service" },
                 Stdout: os.Stdout,
                 Stderr: os.Stdout,
         }
+	/*
         cmdSysStartService := &exec.Cmd {
                 Path: sysExec,
-                Args: []string{ sysExec, "start", "netclient.service"},
+                Args: []string{ sysExec, "start", "netclient@.service"},
                 Stdout: os.Stdout,
                 Stderr: os.Stdout,
         }
+	*/
         cmdSysDaemonReload := &exec.Cmd {
                 Path: sysExec,
                 Args: []string{ sysExec, "daemon-reload"},
@@ -110,25 +142,20 @@ WantedBy=timers.target
         }
         cmdSysEnableTimer := &exec.Cmd {
                 Path: sysExec,
-                Args: []string{ sysExec, "enable", "netclient.timer" },
+                Args: []string{ sysExec, "enable", "netclient-"+network+".timer" },
                 Stdout: os.Stdout,
                 Stderr: os.Stdout,
         }
         cmdSysStartTimer := &exec.Cmd {
                 Path: sysExec,
-		Args: []string{ sysExec, "start", "netclient.timer"},
+		Args: []string{ sysExec, "start", "netclient-"+network+".timer"},
                 Stdout: os.Stdout,
                 Stderr: os.Stdout,
         }
 
         err = cmdSysEnableService.Run()
         if  err  !=  nil {
-                fmt.Println("Error enabling netclient.service. Please investigate.")
-                fmt.Println(err)
-        }
-        err = cmdSysStartService.Run()
-        if  err  !=  nil {
-                fmt.Println("Error starting netclient.service. Please investigate.")
+                fmt.Println("Error enabling [email protected]. Please investigate.")
                 fmt.Println(err)
         }
         err = cmdSysDaemonReload.Run()
@@ -143,24 +170,38 @@ WantedBy=timers.target
         }
         err = cmdSysStartTimer.Run()
         if  err  !=  nil {
-                fmt.Println("Error starting netclient.timer. Please investigate.")
+                fmt.Println("Error starting netclient-"+network+".timer. Please investigate.")
                 fmt.Println(err)
         }
 	return nil
 }
 
-func RemoveSystemDServices() error {
+func isOnlyService(network string) (bool, error) {
+	isonly := false
+	files, err := filepath.Glob("/etc/netclient/netconfig-*")
+	if err != nil {
+		return isonly, err
+	}
+	count := len(files)
+	if count  == 0 {
+		isonly = true
+	}
+	return isonly, err
+
+}
+
+func RemoveSystemDServices(network string) error {
         sysExec, err := exec.LookPath("systemctl")
 
-        cmdSysStopService := &exec.Cmd {
-                Path: sysExec,
-                Args: []string{ sysExec, "stop", "netclient.service" },
-                Stdout: os.Stdout,
-                Stderr: os.Stdout,
-        }
+
+	fullremove, err := isOnlyService(network)
+	if err != nil {
+		fmt.Println(err)
+	}
+
         cmdSysDisableService := &exec.Cmd {
                 Path: sysExec,
-                Args: []string{ sysExec, "disable", "netclient.service"},
+                Args: []string{ sysExec, "disable", "netclient@.service"},
                 Stdout: os.Stdout,
                 Stderr: os.Stdout,
         }
@@ -178,40 +219,43 @@ func RemoveSystemDServices() error {
         }
         cmdSysStopTimer := &exec.Cmd {
                 Path: sysExec,
-                Args: []string{ sysExec, "stop", "netclient.timer" },
+                Args: []string{ sysExec, "stop", "netclient-"+network+".timer" },
                 Stdout: os.Stdout,
                 Stderr: os.Stdout,
         }
         cmdSysDisableTimer := &exec.Cmd {
                 Path: sysExec,
-                Args: []string{ sysExec, "disable", "netclient.timer"},
+                Args: []string{ sysExec, "disable", "netclient-"+network+".timer"},
                 Stdout: os.Stdout,
                 Stderr: os.Stdout,
         }
 
-        err = cmdSysStopService.Run()
+        //err = cmdSysStopService.Run()
         if  err  !=  nil {
-                fmt.Println("Error stopping netclient.service. Please investigate.")
+                fmt.Println("Error stopping netclient@.service. Please investigate.")
                 fmt.Println(err)
         }
+	if fullremove {
         err = cmdSysDisableService.Run()
         if  err  !=  nil {
-                fmt.Println("Error disabling netclient.service. Please investigate.")
+                fmt.Println("Error disabling netclient@.service. Please investigate.")
                 fmt.Println(err)
         }
+	}
         err = cmdSysStopTimer.Run()
         if  err  !=  nil {
-                fmt.Println("Error stopping netclient.timer. Please investigate.")
+                fmt.Println("Error stopping netclient-"+network+".timer. Please investigate.")
                 fmt.Println(err)
         }
         err = cmdSysDisableTimer.Run()
         if  err  !=  nil {
-                fmt.Println("Error disabling netclient.timer. Please investigate.")
+                fmt.Println("Error disabling netclient-"+network+".timer. Please investigate.")
                 fmt.Println(err)
         }
-
-	err = os.Remove("/etc/systemd/system/netclient.service")
-	err = os.Remove("/etc/systemd/system/netclient.timer")
+	if fullremove {
+	err = os.Remove("/etc/systemd/system/[email protected]")
+	}
+	err = os.Remove("/etc/systemd/system/netclient-"+network+".timer")
 	if err != nil {
                 fmt.Println("Error removing file. Please investigate.")
                 fmt.Println(err)

+ 42 - 8
netclient/main.go

@@ -35,8 +35,10 @@ var (
 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.")
-	tgroup := flag.String("g", "badgroup", "The node group you are attempting to join.")
+	tnetwork := flag.String("n", "nonetwork", "The node group 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.")
 	tnoforward := flag.Bool("nf", false, "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")
@@ -70,11 +72,24 @@ func main() {
 	}
 
         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()
@@ -93,25 +108,31 @@ func main() {
 			}
 
 			fmt.Println("Beginning agent installation.")
-			err := functions.Install(*taccesskey, *tpassword, *tserver, *tgroup, *tnoauto)
+			err := functions.Install(*taccesskey, *tpassword, *tserver, *tnetwork, *tnoauto, *taccesstoken, *tname)
 			if err != nil {
+				if !strings.Contains(err.Error(), "ALREADY_INSTALLED") {
 				fmt.Println("Error installing: ", err)
 				fmt.Println("Cleaning up (uninstall)")
-				err = functions.Remove()
+				err = functions.Remove(*tnetwork)
 				if err != nil {
                                         fmt.Println("Error uninstalling: ", err)
                                         fmt.Println("Wiping local.")
-					err = functions.WipeLocal()
+					err = functions.WipeLocal(*tnetwork)
 					if err != nil {
 						fmt.Println("Error removing artifacts: ", err)
 					}
-                                        err = functions.RemoveSystemDServices()
+                                        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()
@@ -126,16 +147,25 @@ func main() {
                                 fmt.Println("Error installing service: ", err)
                                 os.Exit(1)
                         }
+		*/
 		case "checkin":
-			fmt.Println("Beginning node check in.")
-			err := functions.CheckIn()
+                        if *tnetwork == "nonetwork" || *tnetwork == "" {
+                                fmt.Println("Required, '-n'. No network provided. Exiting.")
+                                os.Exit(1)
+                        }
+			fmt.Println("Beginning node check in for group " + *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()
+			err := functions.Remove(*tnetwork)
                         if err != nil {
 					/*
                                         fmt.Println("Error uninstalling: ", err)
@@ -152,6 +182,10 @@ func main() {
                                 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")
 }

+ 0 - 10
netclient/test/delscript.sh

@@ -1,10 +0,0 @@
-#!/bin/bash
-sudo ip link del wc-skynet
-
-curl -X DELETE -H "Authorization: Bearer secretkey" -H 'Content-Type: application/json' localhost:8081/api/skynet/nodes/8c:89:a5:03:f0:d7 | jq
-
-sudo cp /root/.netconfig.bkup /root/.netconfig
-sudo rm /root/.nettoken
-sudo go run ./main.go remove
-
-sudo wg show

+ 80 - 0
serverctl/serverctl.go

@@ -0,0 +1,80 @@
+package serverctl
+
+import (
+        "fmt"
+  "github.com/gravitl/netmaker/functions"
+	"io"
+	"net/http"
+        "os"
+        "os/exec"
+)
+
+
+func DownloadNetclient() error {
+
+	// Get the data
+	resp, err := http.Get("https://github.com/gravitl/netmaker/releases/download/develop/netclient")
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+
+	// Create the file
+	out, err := os.Create("/etc/netclient/netclient")
+	if err != nil {
+		return err
+	}
+	defer out.Close()
+
+	// Write the body to file
+	_, err = io.Copy(out, resp.Body)
+	return err
+}
+func RemoveNetwork(network string) (bool, error) {
+	_, err := os.Stat("/etc/netclient/netclient")
+        if err != nil {
+		return false, err
+	}
+        cmdoutput, err := exec.Command("/etc/netclient/netclient","-c","remove","-n",network).Output()
+        if err != nil {
+                fmt.Println(string(cmdoutput))
+                return false, err
+        }
+        fmt.Println("Server removed from network " + network)
+        return true, err
+
+}
+
+func AddNetwork(network string) (bool, error) {
+	_, err := os.Stat("/etc/netclient")
+        if os.IsNotExist(err) {
+                os.Mkdir("/etc/netclient", 744)
+        } else if err != nil {
+                fmt.Println("couldnt find or create /etc/netclient")
+                return false, err
+        }
+	token, err := functions.CreateServerToken(network)
+        if err != nil {
+                return false, err
+        }
+        _, err = os.Stat("/etc/netclient/netclient")
+	if os.IsNotExist(err) {
+		err = DownloadNetclient()
+		if err != nil {
+			return false, err
+		}
+	}
+        err = os.Chmod("/etc/netclient/netclient", 0755)
+        if err != nil {
+                return false, err
+        }
+	cmdoutput, err := exec.Command("/etc/netclient/netclient","-c","install","-t",token,"-name","netmaker").Output()
+	if err != nil {
+	        fmt.Println(string(cmdoutput))
+                return false, err
+        }
+	fmt.Println("Server added to network " + network)
+	return true, err
+}
+
+