diff --git a/config/config.go b/config/config.go index 847b8e431..aee2e7f8d 100644 --- a/config/config.go +++ b/config/config.go @@ -31,6 +31,7 @@ var Config *EnvironmentConfig type EnvironmentConfig struct { Server ServerConfig `yaml:"server"` MongoConn MongoConnConfig `yaml:"mongoconn"` + WG WG `yaml:"wg"` } // ServerConfig : @@ -48,6 +49,18 @@ type ServerConfig struct { 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 { User string `yaml:"user"` Pass string `yaml:"pass"` diff --git a/controllers/.serverClient.go.swp b/controllers/.serverClient.go.swp new file mode 100644 index 000000000..5c8a189a9 Binary files /dev/null and b/controllers/.serverClient.go.swp differ diff --git a/controllers/common.go b/controllers/common.go index ad6df1dd4..fa69b7001 100644 --- a/controllers/common.go +++ b/controllers/common.go @@ -58,6 +58,7 @@ func GetPeersList(networkName string) ([]models.PeersResponse, error) { return peers, err } + func GetExtPeersList(networkName string, macaddress string) ([]models.ExtPeersResponse, error) { var peers []models.ExtPeersResponse diff --git a/controllers/externalHttpController.go.backup b/controllers/externalHttpController.go.backup deleted file mode 100644 index f21eba26f..000000000 --- a/controllers/externalHttpController.go.backup +++ /dev/null @@ -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.") -} diff --git a/controllers/networkHttpController.go b/controllers/networkHttpController.go index ba7285aff..26d6f489a 100644 --- a/controllers/networkHttpController.go +++ b/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}/keys", securityCheck(http.HandlerFunc(createAccessKey))).Methods("POST") 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") } @@ -640,6 +641,31 @@ func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models 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 func getAccessKeys(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") diff --git a/controllers/nodeGrpcController.go b/controllers/nodeGrpcController.go index 32f3e5948..dd0d3354f 100644 --- a/controllers/nodeGrpcController.go +++ b/controllers/nodeGrpcController.go @@ -73,6 +73,57 @@ func (s *NodeServiceServer) ReadNode(ctx context.Context, req *nodepb.ReadNodeRe 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) { // Get the protobuf node type from the protobuf request type // Essentially doing req.Node to access the struct with a nil check diff --git a/controllers/nodeHttpController.go b/controllers/nodeHttpController.go index 0d28829e5..589651c63 100644 --- a/controllers/nodeHttpController.go +++ b/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}/approve", authorize(true, "master", http.HandlerFunc(uncordonNode))).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}/authenticate", authenticate).Methods("POST") diff --git a/controllers/serverClient.go b/controllers/serverClient.go new file mode 100644 index 000000000..7f8bfccf6 --- /dev/null +++ b/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) +} diff --git a/functions/helpers.go b/functions/helpers.go index d668da1ca..13c9ee7eb 100644 --- a/functions/helpers.go +++ b/functions/helpers.go @@ -9,11 +9,11 @@ import ( "encoding/base64" "errors" "fmt" + "log" "math/rand" "net" "strings" "time" - "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/mongoconn" "github.com/gravitl/netmaker/servercfg" @@ -81,6 +81,48 @@ func CreateServerToken(netID string) (string, error) { 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 { var node models.Node @@ -344,6 +386,31 @@ func IsKeyValid(networkname string, keyvalue string) bool { 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 //This just gets a network object from a network name //Should probably just be GetNetwork. kind of a dumb name. diff --git a/go.mod b/go.mod index ed37ef3a2..15d5e4d10 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/gravitl/netmaker go 1.15 require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/go-playground/validator/v10 v10.5.0 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/stretchr/testify v1.6.1 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 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect diff --git a/go.sum b/go.sum index fee764ed9..0f6aaef21 100644 --- a/go.sum +++ b/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/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/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/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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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.2.2/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/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.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 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/go.mod h1:O7M6gUTPeMF+vsa4c4Ipx3JDkOYrruB1Wry8QRsMcw8= 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/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= 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-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-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-20191003212358-c178f38b412c/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/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.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= diff --git a/grpc/node.pb.go b/grpc/node.pb.go index 59dd7d876..7af5ef11c 100644 --- a/grpc/node.pb.go +++ b/grpc/node.pb.go @@ -646,6 +646,101 @@ func (m *ExtPeersResponse) GetKeepalive() int32 { 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 { Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -657,7 +752,7 @@ func (m *CreateNodeReq) Reset() { *m = CreateNodeReq{} } func (m *CreateNodeReq) String() string { return proto.CompactTextString(m) } func (*CreateNodeReq) ProtoMessage() {} func (*CreateNodeReq) Descriptor() ([]byte, []int) { - return fileDescriptor_d13bd996b67da4ef, []int{6} + return fileDescriptor_d13bd996b67da4ef, []int{7} } 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 (*CreateNodeRes) ProtoMessage() {} func (*CreateNodeRes) Descriptor() ([]byte, []int) { - return fileDescriptor_d13bd996b67da4ef, []int{7} + return fileDescriptor_d13bd996b67da4ef, []int{8} } 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 (*UpdateNodeReq) ProtoMessage() {} func (*UpdateNodeReq) Descriptor() ([]byte, []int) { - return fileDescriptor_d13bd996b67da4ef, []int{8} + return fileDescriptor_d13bd996b67da4ef, []int{9} } 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 (*UpdateNodeRes) ProtoMessage() {} func (*UpdateNodeRes) Descriptor() ([]byte, []int) { - return fileDescriptor_d13bd996b67da4ef, []int{9} + return fileDescriptor_d13bd996b67da4ef, []int{10} } 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 (*ReadNodeReq) ProtoMessage() {} func (*ReadNodeReq) Descriptor() ([]byte, []int) { - return fileDescriptor_d13bd996b67da4ef, []int{10} + return fileDescriptor_d13bd996b67da4ef, []int{11} } 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 (*ReadNodeRes) ProtoMessage() {} func (*ReadNodeRes) Descriptor() ([]byte, []int) { - return fileDescriptor_d13bd996b67da4ef, []int{11} + return fileDescriptor_d13bd996b67da4ef, []int{12} } 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 (*DeleteNodeReq) ProtoMessage() {} func (*DeleteNodeReq) Descriptor() ([]byte, []int) { - return fileDescriptor_d13bd996b67da4ef, []int{12} + return fileDescriptor_d13bd996b67da4ef, []int{13} } 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 (*DeleteNodeRes) ProtoMessage() {} func (*DeleteNodeRes) Descriptor() ([]byte, []int) { - return fileDescriptor_d13bd996b67da4ef, []int{13} + return fileDescriptor_d13bd996b67da4ef, []int{14} } 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 (*GetPeersReq) ProtoMessage() {} func (*GetPeersReq) Descriptor() ([]byte, []int) { - return fileDescriptor_d13bd996b67da4ef, []int{14} + return fileDescriptor_d13bd996b67da4ef, []int{15} } 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 (*GetExtPeersReq) ProtoMessage() {} func (*GetExtPeersReq) Descriptor() ([]byte, []int) { - return fileDescriptor_d13bd996b67da4ef, []int{15} + return fileDescriptor_d13bd996b67da4ef, []int{16} } 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 (*GetPeersRes) ProtoMessage() {} func (*GetPeersRes) Descriptor() ([]byte, []int) { - return fileDescriptor_d13bd996b67da4ef, []int{16} + return fileDescriptor_d13bd996b67da4ef, []int{17} } 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 (*GetExtPeersRes) ProtoMessage() {} func (*GetExtPeersRes) Descriptor() ([]byte, []int) { - return fileDescriptor_d13bd996b67da4ef, []int{17} + return fileDescriptor_d13bd996b67da4ef, []int{18} } 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 (*CheckInReq) ProtoMessage() {} func (*CheckInReq) Descriptor() ([]byte, []int) { - return fileDescriptor_d13bd996b67da4ef, []int{18} + return fileDescriptor_d13bd996b67da4ef, []int{19} } 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 (*CheckInRes) ProtoMessage() {} func (*CheckInRes) Descriptor() ([]byte, []int) { - return fileDescriptor_d13bd996b67da4ef, []int{19} + return fileDescriptor_d13bd996b67da4ef, []int{20} } func (m *CheckInRes) XXX_Unmarshal(b []byte) error { @@ -1231,6 +1326,7 @@ func init() { proto.RegisterType((*CheckInResponse)(nil), "node.CheckInResponse") proto.RegisterType((*PeersResponse)(nil), "node.PeersResponse") proto.RegisterType((*ExtPeersResponse)(nil), "node.ExtPeersResponse") + proto.RegisterType((*Client)(nil), "node.Client") proto.RegisterType((*CreateNodeReq)(nil), "node.CreateNodeReq") proto.RegisterType((*CreateNodeRes)(nil), "node.CreateNodeRes") proto.RegisterType((*UpdateNodeReq)(nil), "node.UpdateNodeReq") @@ -1250,69 +1346,74 @@ func init() { func init() { proto.RegisterFile("grpc/node.proto", fileDescriptor_d13bd996b67da4ef) } 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, } diff --git a/grpc/node.proto b/grpc/node.proto index c85614333..4d83dc18c 100644 --- a/grpc/node.proto +++ b/grpc/node.proto @@ -84,6 +84,17 @@ message ExtPeersResponse { 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 { Node node = 1; // Node id blank } diff --git a/main.go b/main.go index b4621cf7a..c6ded15c2 100644 --- a/main.go +++ b/main.go @@ -59,6 +59,16 @@ func main() { 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 //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 @@ -113,7 +123,7 @@ func runGRPC(wg *sync.WaitGroup, installserver bool) { listener, err := net.Listen("tcp", ":"+grpcport) // Handle errors if any 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( diff --git a/models/.serverclient.go.swp b/models/.serverclient.go.swp new file mode 100644 index 000000000..8f7aaf2c4 Binary files /dev/null and b/models/.serverclient.go.swp differ diff --git a/models/network.go b/models/network.go index ab943b429..b56e7a84f 100644 --- a/models/network.go +++ b/models/network.go @@ -31,6 +31,9 @@ type Network struct { AllowManualSignUp *bool `json:"allowmanualsignup" bson:"allowmanualsignup"` IsLocal *bool `json:"islocal" bson:"islocal"` 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"` //can't have min=1 with omitempty 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 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" + } } diff --git a/models/serverclient.go b/models/serverclient.go new file mode 100644 index 000000000..e1154f7d4 --- /dev/null +++ b/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"` +} diff --git a/models/wglink.go b/models/wglink.go new file mode 100644 index 000000000..34ae3474a --- /dev/null +++ b/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 +} diff --git a/netclient/functions/auth.go b/netclient/auth/auth.go similarity index 96% rename from netclient/functions/auth.go rename to netclient/auth/auth.go index 6a2f3c9fe..c144c3794 100644 --- a/netclient/functions/auth.go +++ b/netclient/auth/auth.go @@ -1,4 +1,4 @@ -package functions +package auth import ( "github.com/gravitl/netmaker/netclient/config" @@ -19,7 +19,6 @@ func SetJWT(client nodepb.NodeServiceClient, network string) (context.Context, e home := "/etc/netclient" tokentext, err := ioutil.ReadFile(home + "/nettoken-"+network) if err != nil { - fmt.Println("Error reading token. Logging in to retrieve new token.") err = AutoLogin(client, network) if err != nil { return nil, status.Errorf(codes.Unauthenticated, fmt.Sprintf("Something went wrong with Auto Login: %v", err)) diff --git a/netclient/command/commands.go b/netclient/command/commands.go new file mode 100644 index 000000000..ac7ebcf90 --- /dev/null +++ b/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 +} diff --git a/netclient/config/config.go b/netclient/config/config.go index ecdc2dfb2..f44c50333 100644 --- a/netclient/config/config.go +++ b/netclient/config/config.go @@ -1,22 +1,24 @@ package config 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 { Server ServerConfig `yaml:"server"` Node NodeConfig `yaml:"node"` - Network string + Network string `yaml:"network"` + Daemon string `yaml:"daemon"` + OperatingSystem string `yaml:"operatingsystem"` } type ServerConfig struct { Address string `yaml:"address"` @@ -32,11 +34,11 @@ type NodeConfig struct { LocalAddress string `yaml:"localaddress"` WGAddress string `yaml:"wgaddress"` 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"` LocalRange string `yaml:"localrange"` PostUp string `yaml:"postup"` @@ -56,8 +58,6 @@ func Write(config *ClientConfig, network string) error{ err := errors.New("No network provided. Exiting.") return err } - nofile := false - //home, err := homedir.Dir() _, err := os.Stat("/etc/netclient") if os.IsNotExist(err) { os.Mkdir("/etc/netclient", 744) @@ -71,29 +71,12 @@ func Write(config *ClientConfig, network string) error{ } 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...") - } 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 } 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) { 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-" + network) f, err := os.Open(file) + if err != nil { nofile = true } @@ -239,3 +364,12 @@ func ReadConfig(network string) (*ClientConfig, error) { } return &cfg, err } + +func FileExists(f string) bool { + info, err := os.Stat(f) + if os.IsNotExist(err) { + return false + } + return !info.IsDir() +} + diff --git a/netclient/functions/.register.go.swp b/netclient/functions/.register.go.swp new file mode 100644 index 000000000..d4bba3a4d Binary files /dev/null and b/netclient/functions/.register.go.swp differ diff --git a/netclient/functions/checkin.go b/netclient/functions/checkin.go new file mode 100644 index 000000000..cf3eb7d11 --- /dev/null +++ b/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 +} + diff --git a/netclient/functions/common.go b/netclient/functions/common.go index 5c3dcf486..f46a27662 100644 --- a/netclient/functions/common.go +++ b/netclient/functions/common.go @@ -10,15 +10,14 @@ import ( "log" "net" "os" - "strconv" "os/exec" "github.com/gravitl/netmaker/netclient/config" + "github.com/gravitl/netmaker/netclient/local" + "github.com/gravitl/netmaker/netclient/auth" 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" ) @@ -70,892 +69,92 @@ func GetFreePort(rangestart int32) (int32, error){ return portno, err } -func Install(accesskey string, password string, server string, network string, noauto bool, accesstoken string, inputname string, pubip string, dnsoff bool, ipforward string) error { - - tserver := "" - tnetwork := "" - tkey := "" - trange := "" - var localrange *net.IPNet - islocal := false - if FileExists("/etc/systemd/system/netclient-"+network+".timer") || - FileExists("/etc/netclient/netconfig-"+network) { - err := errors.New("ALREADY_INSTALLED. Netclient appears to already be installed for network " + network + ". To re-install, please remove by executing 'sudo netclient -c remove -n " + network + "'. 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] - trange = tokenvals[3] - printrange := "" - if server == "localhost:50051" { - server = tserver - } - if network == "nonetwork" { - network = tnetwork - } - if accesskey == "badkey" { - accesskey = tkey - } - fmt.Println(trange) - if trange != "" { - fmt.Println("This is a local network. Proceeding with local address as endpoint.") - islocal = true - _, localrange, err = net.ParseCIDR(trange) - if err == nil { - printrange = localrange.String() - } else { - //localrange = "" - } - } else { - printrange = "Not a local network. Will use public address for endpoint." - } - - fmt.Println("Decoded values from token:") - fmt.Println(" Server: " + server) - fmt.Println(" Network: " + network) - fmt.Println(" Key: " + accesskey) - fmt.Println(" Local Range: " + printrange) - } - - wgclient, err := wgctrl.New() - - if err != nil { - log.Fatalf("failed to open client: %v", err) - } - defer wgclient.Close() - - cfg, err := config.ReadConfig(network) - if err != nil { - log.Printf("No Config Yet. Will Write: %v", err) - } - nodecfg := cfg.Node - servercfg := cfg.Server - fmt.Println("SERVER SETTINGS:") - - nodecfg.DNSOff = dnsoff - nodecfg.IPForwarding = ipforward - - if server == "" { - if servercfg.Address == "" && tserver == "" { - log.Fatal("no server provided") - } else { - server = servercfg.Address - } - } - fmt.Println(" Server: " + server) - - if accesskey == "" { - if servercfg.AccessKey == "" && tkey == "" { - fmt.Println("no access key provided.Proceeding anyway.") - } else { - accesskey = servercfg.AccessKey - } - } - fmt.Println(" AccessKey: " + accesskey) - err = config.WriteServer(server, accesskey, network) - if err != nil { - fmt.Println("Error encountered while writing Server Config.") - return err - } - - - fmt.Println("NODE REQUESTING SETTINGS:") - if password == "" { - if nodecfg.Password == "" { - //create error here - log.Fatal("no password provided") - } else { - password = nodecfg.Password - } - } - fmt.Println(" Password: " + password) - - if network == "badnetwork" { - if nodecfg.Network == "" && tnetwork == "" { - //create error here - log.Fatal("no network provided") - } else { - network = nodecfg.Network - } - } - fmt.Println(" Network: " + network) - - var macaddress string - var localaddress string - var listenport int32 - var keepalive int32 - var publickey wgtypes.Key - var privatekey wgtypes.Key - var privkeystring string - var endpoint string - var postup string - var postdown string - var name string - var wginterface string - - if nodecfg.LocalAddress == "" { - ifaces, err := net.Interfaces() - 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 islocal { - found = localrange.Contains(ip) - } else { - found = true - } - } - case *net.IPAddr: - if !found { - ip = v.IP - local = ip.String() - if islocal { - found = localrange.Contains(ip) - - } else { - found = true - } - } - } - } - } - localaddress = local - } else { - localaddress = nodecfg.LocalAddress - } - fmt.Println(" Local Address: " + localaddress) - - if pubip != "" && pubip != "nopubip" { - endpoint = pubip - } else { - if nodecfg.Endpoint == "" { - if islocal && localaddress != "" { - endpoint = localaddress - fmt.Println("Endpoint is local. Setting to address: " + endpoint) - } else { - - endpoint, err = getPublicIP() - if err != nil { - fmt.Println("Error setting endpoint.") - return err - } - fmt.Println("Endpoint is public. Setting to address: " + endpoint) - } - } else { - endpoint = nodecfg.Endpoint - fmt.Println("Endpoint set in config. Setting to address: " + endpoint) - } - } - fmt.Println(" Endpoint: " + endpoint) - - - if nodecfg.Name != "" { - name = nodecfg.Name - } - if inputname != "" && inputname != "noname" { - name = inputname - } - fmt.Println(" Name: " + name) - - - if nodecfg.Interface != "" { - wginterface = nodecfg.Interface - } - fmt.Println(" Interface: " + wginterface) - - if nodecfg.PostUp != "" { - postup = nodecfg.PostUp - } - fmt.Println(" PostUp: " + postup) - - if nodecfg.PostDown!= "" { - postdown = nodecfg.PostDown - } - fmt.Println(" PostDown: " + postdown) - - - if nodecfg.KeepAlive != 0 { - keepalive = nodecfg.KeepAlive - } - fmt.Println(" KeepAlive: " + wginterface) - - - if nodecfg.Port != 0 { - listenport = nodecfg.Port - } - 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 - privatekey, err := wgtypes.ParseKey(nodecfg.PrivateKey) - if err != nil { - log.Fatal(err) - } - if nodecfg.PublicKey != "" { - publickey, err = wgtypes.ParseKey(nodecfg.PublicKey) - if err != nil { - log.Fatal(err) - } - } else { - publickey = privatekey.PublicKey() - } - } else { - privatekey, err := wgtypes.GeneratePrivateKey() - if err != nil { - log.Fatal(err) - } - privkeystring = privatekey.String() - publickey = privatekey.PublicKey() - } - - if nodecfg.MacAddress != "" { - macaddress = nodecfg.MacAddress - } else { - macs, err := getMacAddr() - if err != nil { - return err - } else if len(macs) == 0 { - log.Fatal() - } else { - macaddress = macs[0] - } - } - fmt.Println(" Mac Address: " + macaddress) - fmt.Println(" Private Key: " + privatekey.String()) - fmt.Println(" Public Key: " + publickey.String()) - - - var wcclient nodepb.NodeServiceClient - var requestOpts grpc.DialOption - requestOpts = grpc.WithInsecure() - conn, err := grpc.Dial(server, requestOpts) - if err != nil { - log.Fatalf("Unable to establish client connection to localhost:50051: %v", err) - } - wcclient = nodepb.NewNodeServiceClient(conn) - - postnode := &nodepb.Node{ - Password: password, - Macaddress: macaddress, - Accesskey: accesskey, - Nodenetwork: network, - Listenport: listenport, - Postup: postup, - Postdown: postdown, - Keepalive: keepalive, - Localaddress: localaddress, - Interface: wginterface, - Publickey: publickey.String(), - Name: name, - Endpoint: endpoint, - } - - fmt.Println("Writing node settings to netconfig file.") - err = 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 - fmt.Println("Setting local config from server response") - if err != nil { - return err - } - - fmt.Println("NODE RECIEVED 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 && !nodecfg.DNSOff { - nodecfg.DNSOff = true - } - if !islocal && node.Islocal && node.Localrange != "" { - fmt.Println("Resetting local settings for local network.") - node.Localaddress, err = getLocalIP(node.Localrange) - if err != nil { - return err - } - node.Endpoint = node.Localaddress - } - - err = 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 !noauto { - fmt.Println("Configuring Netmaker Service.") - err = ConfigureSystemD(network) - return err - } - } - - peers, hasGateway, gateways, err := getPeers(node.Macaddress, network, server, node.Isdualstack, node.Isingressgateway) - - if err != nil { - return err - } - fmt.Println("retrived peers, setting wireguard config.") - err = storePrivKey(privkeystring, network) - if err != nil { - return err - } - err = initWireguard(node, privkeystring, peers, hasGateway, gateways) - if err != nil { - return err - } - if !noauto { - err = ConfigureSystemD(network) - } - if err != nil { - return err - } - - return err -} - -func getLocalIP(localrange string) (string, error) { - _, localRange, err := net.ParseCIDR(localrange) - if err != nil { - return "", err - } - ifaces, err := net.Interfaces() - 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() - found = localRange.Contains(ip) - } - case *net.IPAddr: - if !found { - ip = v.IP - local = ip.String() - found = localRange.Contains(ip) - } - } - } - } - if !found || local == "" { - return "", errors.New("Failed to find local IP in range " + localrange) - } - return local, nil -} - -func getPublicIP() (string, error) { - - iplist := []string{"http://ip.client.gravitl.com","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 modConfig(node *nodepb.Node) error{ - network := node.Nodenetwork - if network == "" { - return errors.New("No Network Provided") - } - modconfig, err := config.ReadConfig(network) - if err != nil { - return err - } - 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.DNSOff = node.Dnsoff - } - if node.Isdualstack == true { - nodecfg.IsDualStack = true - } - nodecfg.IsIngressGateway = node.Isingressgateway - if node.Localrange != "" && node.Islocal { - nodecfg.IsLocal = true - nodecfg.LocalRange = node.Localrange - } - modconfig.Node = nodecfg - err = config.Write(modconfig, network) - return err -} - - -func getMacAddr() ([]string, error) { - ifas, err := net.Interfaces() - if err != nil { - return nil, err - } - var as []string - for _, ifa := range ifas { - a := ifa.HardwareAddr.String() - if a != "" { - as = append(as, a) - } - } - return as, nil -} - - -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 - fmt.Println("beginning local WG config") - - - if err != nil { - log.Fatalf("failed to open client: %v", err) - } - defer wgclient.Close() - - fmt.Println("setting local settings") - - 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) - - fmt.Println("setting WG config from node and peers") - - //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) - } - } - - fmt.Println("configuring WG device") - - 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.DNSOff != true { - - _, 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 = 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 = 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 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 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) +func getLocalIP(localrange string) (string, error) { + _, localRange, err := net.ParseCIDR(localrange) if err != nil { - return err + return "", err } - servercfg := cfg.Server - nodecfg := cfg.Node - node := getNode(network) - - peers, hasGateway, gateways, err := getPeers(node.Macaddress, nodecfg.Network, servercfg.Address, node.Isdualstack, node.Isingressgateway) + ifaces, err := net.Interfaces() if err != nil { - return err + return "", err } - privkey, err := retrievePrivKey(network) - if err != nil { - return err + var local string + found := false + for _, i := range ifaces { + if i.Flags&net.FlagUp == 0 { + continue // interface down } - - err = initWireguard(&node, privkey, peers, hasGateway, gateways) + if i.Flags&net.FlagLoopback != 0 { + continue // loopback interface + } + addrs, err := i.Addrs() if err != nil { - return err + return "", err } - - 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() + found = localRange.Contains(ip) + } + case *net.IPAddr: + if !found { + ip = v.IP + local = ip.String() + found = localRange.Contains(ip) + } + } + } + } + if !found || local == "" { + return "", errors.New("Failed to find local IP in range " + localrange) + } + return local, nil } -func storePrivKey(key string, network string) error{ - d1 := []byte(key) - err := ioutil.WriteFile("/etc/netclient/wgkey-" + network, d1, 0644) - return err +func getPublicIP() (string, error) { + + iplist := []string{"http://ip.client.gravitl.com","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 retrievePrivKey(network string) (string, error) { - dat, err := ioutil.ReadFile("/etc/netclient/wgkey-" + network) - return string(dat), err +func getMacAddr() ([]string, error) { + ifas, err := net.Interfaces() + if err != nil { + return nil, err + } + var as []string + for _, ifa := range ifas { + a := ifa.HardwareAddr.String() + if a != "" { + as = append(as, a) + } + } + return as, nil } func getPrivateAddr() (string, error) { @@ -1001,274 +200,6 @@ func getPrivateAddr() (string, error) { return local, err } - -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 - 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.RoamingOff { - if !nodecfg.IsLocal { - 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 := modConfig(&node) - if err != nil { - return err - log.Fatalf("Error: %v", err) - } - err = setWGConfig(network) - if err != nil { - return err - log.Fatalf("Error: %v", err) - } - node = 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 = 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 := 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 = 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 = modConfig(readres.Node) - if err != nil { - return err - log.Fatalf("Error: %v", err) - } - err = 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 := 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 = modConfig(res.Node) - if err != nil { - return err - log.Fatalf("Error: %v", err) - } - 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(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 = setWGKeyConfig(network, servercfg.Address) - if err != nil { - return err - log.Fatalf("Error: %v", err) - } - } - } - return nil -} - func needInterfaceUpdate(ctx context.Context, mac string, network string, iface string) (bool, string, error) { var header metadata.MD req := &nodepb.ReadNodeReq{ @@ -1285,7 +216,7 @@ func needInterfaceUpdate(ctx context.Context, mac string, network string, iface return iface != oldiface, oldiface, err } -func getNode(network string) nodepb.Node { +func GetNode(network string) nodepb.Node { modcfg, err := config.ReadConfig(network) if err != nil { @@ -1309,15 +240,27 @@ func getNode(network string) nodepb.Node { node.Macaddress = nodecfg.MacAddress node.Endpoint = nodecfg.Endpoint node.Password = nodecfg.Password - node.Dnsoff = nodecfg.DNSOff - node.Isdualstack = nodecfg.IsDualStack - node.Isingressgateway = nodecfg.IsIngressGateway + if nodecfg.DNS == "on" { + node.Dnsoff = true + } else { + node.Dnsoff = false + } + 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 Remove(network string) error { +func LeaveNetwork(network string) error { //need to implement checkin on server side cfg, err := config.ReadConfig(network) if err != nil { @@ -1325,8 +268,6 @@ func Remove(network string) error { } servercfg := cfg.Server node := cfg.Node - fmt.Println("Deleting remote node with MAC: " + node.MacAddress) - var wcclient nodepb.NodeServiceClient var requestOpts grpc.DialOption @@ -1334,99 +275,37 @@ func Remove(network string) error { 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) + wcclient = nodepb.NewNodeServiceClient(conn) - ctx := context.Background() - fmt.Println("Authenticating with GRPC Server") - ctx, err = SetJWT(wcclient, network) - if err != nil { - //return err + ctx := context.Background() + ctx, err = auth.SetJWT(wcclient, network) + if err != nil { 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) - } - } + } else { + 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("delete node " + node.MacAddress + "from remote server on network " + node.Network) + } + } } - err = WipeLocal(network) + err = local.WipeLocal(network) if err != nil { log.Printf("Unable to wipe local config: %v", err) } - err = 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 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-" + network) - if err != nil { - fmt.Println(err) - } - err = os.Remove(home + "/nettoken-" + network) - if err != nil { - fmt.Println(err) - } - - err = os.Remove(home + "/wgkey-" + network) - if err != nil { - fmt.Println(err) - } - - 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()) - } - } - } + err = local.RemoveSystemDServices(network) return err - } func DeleteInterface(ifacename string, postdown string) error{ @@ -1444,7 +323,7 @@ func DeleteInterface(ifacename string, postdown string) error{ } if postdown != "" { runcmds := strings.Split(postdown, "; ") - err = runCmds(runcmds) + err = local.RunCmds(runcmds) if err != nil { fmt.Println("Error encountered running PostDown: " + err.Error()) } diff --git a/netclient/functions/install.go b/netclient/functions/install.go new file mode 100644 index 000000000..9fe887b7c --- /dev/null +++ b/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 +} + diff --git a/netclient/functions/join.go b/netclient/functions/join.go new file mode 100644 index 000000000..64e86f725 --- /dev/null +++ b/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 +} diff --git a/netclient/functions/register.go b/netclient/functions/register.go new file mode 100644 index 000000000..7d817fc38 --- /dev/null +++ b/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 +} diff --git a/netclient/functions/local.go b/netclient/local/local.go similarity index 80% rename from netclient/functions/local.go rename to netclient/local/local.go index d055ccbdf..e3182e9ee 100644 --- a/netclient/functions/local.go +++ b/netclient/local/local.go @@ -1,8 +1,9 @@ -package functions +package local import ( //"github.com/davecgh/go-spew/spew" - "fmt" + "github.com/gravitl/netmaker/netclient/config" + "fmt" "io/ioutil" "path/filepath" "io" @@ -12,6 +13,19 @@ import ( "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 { info, err := os.Stat(f) @@ -92,7 +106,7 @@ Wants=netclient.timer [Service] Type=simple -ExecStart=/etc/netclient/netclient -c checkin -n %i +ExecStart=/etc/netclient/netclient checkin -n %i [Install] WantedBy=multi-user.target @@ -213,38 +227,11 @@ func RemoveSystemDServices(network string) error { fmt.Println(err) } - cmdSysDisableService := exec.Command("systemctl","disable","netclient@.service")/* &exec.Cmd { - Path: sysExec, - Args: []string{ sysExec, "disable", "netclient@.service"}, - 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","netclient@.service") + 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 { fmt.Println("Error stopping netclient@.service. Please investigate.") 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) { sourceFileStat, err := os.Stat(src) if err != nil { diff --git a/netclient/main.go b/netclient/main.go index a1ceffaf9..432ab0a61 100644 --- a/netclient/main.go +++ b/netclient/main.go @@ -1,55 +1,318 @@ package main 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/exec" - "strconv" - "strings" + "github.com/gravitl/netmaker/netclient/command" + "github.com/gravitl/netmaker/netclient/config" "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() + 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") 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) + } } diff --git a/netclient/main.go.old b/netclient/main.go.old new file mode 100644 index 000000000..d1865fa67 --- /dev/null +++ b/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") +} diff --git a/netclient/functions/peers.go b/netclient/server/grpc.go similarity index 71% rename from netclient/functions/peers.go rename to netclient/server/grpc.go index d17a8bafe..fc32f4762 100644 --- a/netclient/functions/peers.go +++ b/netclient/server/grpc.go @@ -1,23 +1,132 @@ -package functions +package server 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" "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 hasGateway := false 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() conn, err := grpc.Dial(server, requestOpts) if err != nil { @@ -49,8 +157,7 @@ func getPeers(macaddress string, network string, server string, dualstack bool, Network: network, } ctx := context.Background() - fmt.Println("Authenticating with GRPC Server") - ctx, err = SetJWT(wcclient, network) + ctx, err = auth.SetJWT(wcclient, network) if err != nil { fmt.Println("Failed to authenticate.") return peers, hasGateway, gateways, err @@ -63,7 +170,6 @@ func getPeers(macaddress string, network string, server string, dualstack bool, fmt.Println(err) return nil, hasGateway, gateways, err } - fmt.Println("Parsing peers response") for { res, err := stream.Recv() // 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 { - fmt.Println("Peer is self. Skipping") continue } if nodecfg.Endpoint == res.Peers.Endpoint { - fmt.Println("Peer is self. Skipping") continue } @@ -149,8 +253,7 @@ func getPeers(macaddress string, network string, server string, dualstack bool, } if isIngressGateway { - fmt.Println("Adding external peers...") - extPeers, err := getExtPeers(macaddress, network, server, dualstack) + extPeers, err := GetExtPeers(macaddress, network, server, dualstack) if err == nil { peers = append(peers, extPeers...) 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("Finished parsing peers response") 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 wcclient nodepb.NodeServiceClient cfg, err := config.ReadConfig(network) @@ -186,8 +288,7 @@ func getExtPeers(macaddress string, network string, server string, dualstack boo Network: network, } ctx := context.Background() - fmt.Println("Authenticating with GRPC Server") - ctx, err = SetJWT(wcclient, network) + ctx, err = auth.SetJWT(wcclient, network) if err != nil { fmt.Println("Failed to authenticate.") return peers, err @@ -200,7 +301,6 @@ func getExtPeers(macaddress string, network string, server string, dualstack boo fmt.Println(err) return nil, err } - fmt.Println("Parsing peers response") for { res, err := stream.Recv() // 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 { - fmt.Println("Peer is self. Skipping") continue } diff --git a/netclient/wireguard/kernel.go b/netclient/wireguard/kernel.go new file mode 100644 index 000000000..a249392c2 --- /dev/null +++ b/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 +} diff --git a/scripts/runmongo.sh b/scripts/runmongo.sh new file mode 100644 index 000000000..438b8eb74 --- /dev/null +++ b/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 diff --git a/servercfg/serverconf.go b/servercfg/serverconf.go index effe638f5..fc7a35521 100644 --- a/servercfg/serverconf.go +++ b/servercfg/serverconf.go @@ -47,6 +47,21 @@ func GetConfig() config.ServerConfig { 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 { serverhost := "127.0.0.1" if os.Getenv("SERVER_HTTP_HOST") != "" { @@ -67,15 +82,16 @@ func GetAPIPort() string { } return apiport } + 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 } func GetGRPCPort() string { diff --git a/servercfg/wireguardconf.go b/servercfg/wireguardconf.go new file mode 100644 index 000000000..e6dfe6f25 --- /dev/null +++ b/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 +} + diff --git a/serverctl/serverctl.go b/serverctl/serverctl.go index 7a4cd6e4f..221daf4f9 100644 --- a/serverctl/serverctl.go +++ b/serverctl/serverctl.go @@ -6,6 +6,8 @@ import ( "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/mongoconn" "github.com/gravitl/netmaker/servercfg" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo/options" "io" "time" "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 { /* // Get the data diff --git a/serverctl/wireguard.go b/serverctl/wireguard.go new file mode 100644 index 000000000..b1d184dc8 --- /dev/null +++ b/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 +}