diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4a337df2..d1b7151f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,6 +5,8 @@ on: jobs: tests: + env: + DATABASE: rqlite runs-on: ubuntu-latest services: rqlite: @@ -18,3 +20,6 @@ jobs: - name: run tests run: | go test -p 1 ./... -v + env: + DATABASE: rqlite + CLIENT_MODE: "off" \ No newline at end of file diff --git a/controllers/networkHttpController.go b/controllers/networkHttpController.go index bc87a9e6..c0aa326d 100644 --- a/controllers/networkHttpController.go +++ b/controllers/networkHttpController.go @@ -364,16 +364,6 @@ func createNetwork(w http.ResponseWriter, r *http.Request) { returnErrorResponse(w, r, formatError(err, "badrequest")) return } - if servercfg.IsClientMode() { - success, err := serverctl.AddNetwork(network.NetID) - if err != nil || !success { - if err == nil { - err = errors.New("Failed to add server to network " + network.DisplayName) - } - returnErrorResponse(w, r, formatError(err, "internal")) - return - } - } functions.PrintUserLog(r.Header.Get("user"), "created network "+network.NetID, 1) w.WriteHeader(http.StatusOK) //json.NewEncoder(w).Encode(result) @@ -400,7 +390,18 @@ func CreateNetwork(network models.Network) error { return err } - return nil + if servercfg.IsClientMode() { + var success bool + success, err = serverctl.AddNetwork(network.NetID) + if err != nil || !success { + DeleteNetwork(network.NetID) + if err == nil { + err = errors.New("Failed to add server to network " + network.DisplayName) + } + } + } + + return err } // BEGIN KEY MANAGEMENT SECTION diff --git a/database/database.go b/database/database.go index aa9b46ba..b2c28f8c 100644 --- a/database/database.go +++ b/database/database.go @@ -2,7 +2,9 @@ package database import ( "encoding/json" + "time" "errors" + "log" "github.com/gravitl/netmaker/servercfg" ) @@ -37,14 +39,23 @@ func getCurrentDB() map[string]interface{} { case "sqlite": return SQLITE_FUNCTIONS default: - return RQLITE_FUNCTIONS + return SQLITE_FUNCTIONS } } func InitializeDatabase() error { - - if err := getCurrentDB()[INIT_DB].(func() error)(); err != nil { - return err + log.Println("connecting to",servercfg.GetDB()) + tperiod := time.Now().Add(10 * time.Second) + for { + if err := getCurrentDB()[INIT_DB].(func() error)(); err != nil { + log.Println("unable to connect to db, retrying . . .") + if time.Now().After(tperiod) { + return err + } + } else { + break + } + time.Sleep(2 * time.Second) } createTables() return nil diff --git a/netclient/functions/checkin.go b/netclient/functions/checkin.go index 3986f930..d03f38e3 100644 --- a/netclient/functions/checkin.go +++ b/netclient/functions/checkin.go @@ -1,7 +1,6 @@ package functions import ( - "crypto/tls" "encoding/json" "errors" "log" @@ -18,7 +17,6 @@ import ( "github.com/gravitl/netmaker/netclient/wireguard" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "google.golang.org/grpc" - "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" //homedir "github.com/mitchellh/go-homedir" ) @@ -168,14 +166,8 @@ func Pull(network string, manual bool) (*models.Node, error) { return nil, err } } - - var requestOpts grpc.DialOption - requestOpts = grpc.WithInsecure() - if cfg.Server.GRPCSSL == "on" { - h2creds := credentials.NewTLS(&tls.Config{NextProtos: []string{"h2"}}) - requestOpts = grpc.WithTransportCredentials(h2creds) - } - conn, err := grpc.Dial(servercfg.GRPCAddress, requestOpts) + conn, err := grpc.Dial(cfg.Server.GRPCAddress, + netclientutils.GRPCRequestOpts(cfg.Server.GRPCSSL)) if err != nil { log.Println("Cant dial GRPC server:", err) return nil, err @@ -253,17 +245,11 @@ func Push(network string) error { postnode := cfg.Node // always set the OS on client postnode.OS = runtime.GOOS - servercfg := cfg.Server var header metadata.MD var wcclient nodepb.NodeServiceClient - var requestOpts grpc.DialOption - requestOpts = grpc.WithInsecure() - if cfg.Server.GRPCSSL == "on" { - h2creds := credentials.NewTLS(&tls.Config{NextProtos: []string{"h2"}}) - requestOpts = grpc.WithTransportCredentials(h2creds) - } - conn, err := grpc.Dial(servercfg.GRPCAddress, requestOpts) + conn, err := grpc.Dial(cfg.Server.GRPCAddress, + netclientutils.GRPCRequestOpts(cfg.Server.GRPCSSL)) if err != nil { log.Println("Cant dial GRPC server:", err) return err diff --git a/netclient/functions/common.go b/netclient/functions/common.go index e954edfe..291873ac 100644 --- a/netclient/functions/common.go +++ b/netclient/functions/common.go @@ -2,7 +2,6 @@ package functions import ( "context" - "crypto/tls" "encoding/json" "errors" "fmt" @@ -20,7 +19,6 @@ import ( "github.com/gravitl/netmaker/netclient/netclientutils" "golang.zx2c4.com/wireguard/wgctrl" "google.golang.org/grpc" - "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" ) @@ -121,13 +119,8 @@ func LeaveNetwork(network string) error { node := cfg.Node var wcclient nodepb.NodeServiceClient - var requestOpts grpc.DialOption - requestOpts = grpc.WithInsecure() - if cfg.Server.GRPCSSL == "on" { - h2creds := credentials.NewTLS(&tls.Config{NextProtos: []string{"h2"}}) - requestOpts = grpc.WithTransportCredentials(h2creds) - } - conn, err := grpc.Dial(servercfg.GRPCAddress, requestOpts) + conn, err := grpc.Dial(cfg.Server.GRPCAddress, + netclientutils.GRPCRequestOpts(cfg.Server.GRPCSSL)) if err != nil { log.Printf("Unable to establish client connection to "+servercfg.GRPCAddress+": %v", err) } else { diff --git a/netclient/functions/join.go b/netclient/functions/join.go index 59d5d31b..7c37db07 100644 --- a/netclient/functions/join.go +++ b/netclient/functions/join.go @@ -2,12 +2,10 @@ package functions import ( "context" - "crypto/tls" "encoding/json" "errors" "fmt" "log" - "net" nodepb "github.com/gravitl/netmaker/grpc" "github.com/gravitl/netmaker/models" @@ -17,10 +15,8 @@ import ( "github.com/gravitl/netmaker/netclient/netclientutils" "github.com/gravitl/netmaker/netclient/server" "github.com/gravitl/netmaker/netclient/wireguard" - "golang.zx2c4.com/wireguard/wgctrl" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "google.golang.org/grpc" - "google.golang.org/grpc/credentials" ) func JoinNetwork(cfg config.ClientConfig, privateKey string) error { @@ -30,90 +26,40 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error { err := errors.New("ALREADY_INSTALLED. Netclient appears to already be installed for " + cfg.Network + ". To re-install, please remove by executing 'sudo netclient leave -n " + cfg.Network + "'. Then re-run the install command.") return err } - log.Println("attempting to join " + cfg.Network + " at " + cfg.Server.GRPCAddress) + + netclientutils.Log("attempting to join " + cfg.Network + " at " + cfg.Server.GRPCAddress) 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.Network == "" { return errors.New("no network provided") } - if cfg.Node.LocalRange != "" { - if cfg.Node.LocalAddress == "" { - log.Println("local vpn, getting local address from range: " + cfg.Node.LocalRange) - 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.LocalRange != "" && cfg.Node.LocalAddress == "" { + log.Println("local vpn, getting local address from range: " + cfg.Node.LocalRange) + cfg.Node.LocalAddress = getLocalIP(cfg.Node) } if cfg.Node.Password == "" { cfg.Node.Password = netclientutils.GenPass() } auth.StoreSecret(cfg.Node.Password, cfg.Node.Network) + + // set endpoint if blank. set to local if local net, retrieve from function if not if cfg.Node.Endpoint == "" { if cfg.Node.IsLocal == "yes" && cfg.Node.LocalAddress != "" { cfg.Node.Endpoint = cfg.Node.LocalAddress } else { cfg.Node.Endpoint, err = netclientutils.GetPublicIP() - if err != nil { - fmt.Println("Error setting cfg.Node.Endpoint.") - return err - } + + } + if err != nil || cfg.Node.Endpoint == "" { + netclientutils.Log("Error setting cfg.Node.Endpoint.") + return err } } + // Generate and set public/private WireGuard Keys if privateKey == "" { wgPrivatekey, err := wgtypes.GeneratePrivateKey() if err != nil { @@ -123,25 +69,22 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error { cfg.Node.PublicKey = wgPrivatekey.PublicKey().String() } + // Find and set node MacAddress if cfg.Node.MacAddress == "" { macs, err := netclientutils.GetMacAddr() if err != nil { return err } else if len(macs) == 0 { - log.Fatal() + log.Fatal("could not retrieve mac address") } else { cfg.Node.MacAddress = macs[0] } } var wcclient nodepb.NodeServiceClient - var requestOpts grpc.DialOption - requestOpts = grpc.WithInsecure() - if cfg.Server.GRPCSSL == "on" { - h2creds := credentials.NewTLS(&tls.Config{NextProtos: []string{"h2"}}) - requestOpts = grpc.WithTransportCredentials(h2creds) - } - conn, err := grpc.Dial(cfg.Server.GRPCAddress, requestOpts) + + conn, err := grpc.Dial(cfg.Server.GRPCAddress, + netclientutils.GRPCRequestOpts(cfg.Server.GRPCSSL)) if err != nil { log.Fatalf("Unable to establish client connection to "+cfg.Server.GRPCAddress+": %v", err) @@ -175,6 +118,7 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error { return err } + // Create node on server res, err := wcclient.CreateNode( context.TODO(), &nodepb.Object{ @@ -193,17 +137,14 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error { return err } - if node.ListenPort == 0 { - node.ListenPort, err = netclientutils.GetFreePort(51821) - if err != nil { - fmt.Printf("Error retrieving port: %v", err) - } + // get free port based on returned default listen port + node.ListenPort, err = netclientutils.GetFreePort(node.ListenPort) + if err != nil { + fmt.Printf("Error retrieving port: %v", err) } - - if node.DNSOn == "yes" { - cfg.Node.DNSOn = "yes" - } - if !(cfg.Node.IsLocal == "yes") && node.IsLocal == "yes" && node.LocalRange != "" { + + // safety check. If returned node from server is local, but not currently configured as local, set to local addr + if cfg.Node.IsLocal != "yes" && node.IsLocal == "yes" && node.LocalRange != "" { node.LocalAddress, err = netclientutils.GetLocalIP(node.LocalRange) if err != nil { return err @@ -220,9 +161,15 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error { return err } + // pushing any local changes to server before starting wireguard + err = Push(cfg.Network) + if err != nil { + return err + } + if node.IsPending == "yes" { - fmt.Println("Node is marked as PENDING.") - fmt.Println("Awaiting approval from Admin before configuring WireGuard.") + netclientutils.Log("Node is marked as PENDING.") + netclientutils.Log("Awaiting approval from Admin before configuring WireGuard.") if cfg.Daemon != "off" { if netclientutils.IsWindows() { // handle daemon here.. @@ -233,15 +180,16 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error { return err } } - log.Println("retrieving remote peers") + + netclientutils.Log("retrieving remote peers") peers, hasGateway, gateways, err := server.GetPeers(node.MacAddress, cfg.Network, cfg.Server.GRPCAddress, node.IsDualStack == "yes", node.IsIngressGateway == "yes") if err != nil && !netclientutils.IsEmptyRecord(err) { - log.Println("failed to retrieve peers", err) + netclientutils.Log("failed to retrieve peers") return err } - log.Println("starting wireguard") + netclientutils.Log("starting wireguard") err = wireguard.InitWireguard(&node, privateKey, peers, hasGateway, gateways) if err != nil { return err diff --git a/netclient/functions/joinutils.go b/netclient/functions/joinutils.go new file mode 100644 index 00000000..10e32fbd --- /dev/null +++ b/netclient/functions/joinutils.go @@ -0,0 +1,62 @@ +package functions + +import ( + "github.com/gravitl/netmaker/models" + "net" +) + + +func getLocalIP(node models.Node) string{ + + var local string + + ifaces, err := net.Interfaces() + if err != nil { + return local + } + _, localrange, err := net.ParseCIDR(node.LocalRange) + if err != nil { + return local + } + + 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 local + } + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + if !found { + ip = v.IP + local = ip.String() + if node.IsLocal == "yes" { + found = localrange.Contains(ip) + } else { + found = true + } + } + case *net.IPAddr: + if !found { + ip = v.IP + local = ip.String() + if node.IsLocal == "yes" { + found = localrange.Contains(ip) + + } else { + found = true + } + } + } + } + } + return local +} diff --git a/netclient/main.go b/netclient/main.go index 00694f92..3b5688ce 100644 --- a/netclient/main.go +++ b/netclient/main.go @@ -10,7 +10,7 @@ import ( "os/signal" "strconv" "syscall" - + "runtime/debug" "github.com/gravitl/netmaker/netclient/command" "github.com/gravitl/netmaker/netclient/config" "github.com/gravitl/netmaker/netclient/local" @@ -318,6 +318,8 @@ func main() { }, } + setGarbageCollection() + if netclientutils.IsWindows() { ncwindows.InitWindows() } else { @@ -364,3 +366,10 @@ func main() { } } } + +func setGarbageCollection(){ + _, gcset := os.LookupEnv("GOGC"); + if !gcset { + debug.SetGCPercent(netclientutils.DEFAULT_GC_PERCENT) + } +} \ No newline at end of file diff --git a/netclient/netclientutils/netclientutils.go b/netclient/netclientutils/netclientutils.go index e1393633..d912b338 100644 --- a/netclient/netclientutils/netclientutils.go +++ b/netclient/netclientutils/netclientutils.go @@ -13,7 +13,9 @@ import ( "strconv" "strings" "time" - + "crypto/tls" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" "golang.zx2c4.com/wireguard/wgctrl" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) @@ -23,6 +25,8 @@ const NO_DB_RECORDS = "could not find any records" const LINUX_APP_DATA_PATH = "/etc/netclient" const WINDOWS_APP_DATA_PATH = "C:\\ProgramData\\Netclient" const WINDOWS_SVC_NAME = "netclient" +const NETCLIENT_DEFAULT_PORT = 51821 +const DEFAULT_GC_PERCENT = 10 func Log(message string) { log.SetFlags(log.Flags() &^ (log.Llongfile | log.Lshortfile)) @@ -200,6 +204,9 @@ func GetLocalIP(localrange string) (string, error) { } func GetFreePort(rangestart int32) (int32, error) { + if rangestart == 0 { + rangestart = NETCLIENT_DEFAULT_PORT + } wgclient, err := wgctrl.New() if err != nil { return 0, err @@ -208,9 +215,7 @@ func GetFreePort(rangestart int32) (int32, error) { if err != nil { return 0, err } - var portno int32 - portno = 0 - for x := rangestart; x <= 60000; x++ { + for x := rangestart; x <= 65535; x++ { conflict := false for _, i := range devices { if int32(i.ListenPort) == x { @@ -221,10 +226,9 @@ func GetFreePort(rangestart int32) (int32, error) { if conflict { continue } - portno = x - break + return int32(x), nil } - return portno, err + return rangestart, err } // == OS PATH FUNCTIONS == @@ -255,3 +259,13 @@ func GetNetclientPathSpecific() string { return LINUX_APP_DATA_PATH + "/" } } + +func GRPCRequestOpts(isSecure string) grpc.DialOption { + var requestOpts grpc.DialOption + requestOpts = grpc.WithInsecure() + if isSecure == "on" { + h2creds := credentials.NewTLS(&tls.Config{NextProtos: []string{"h2"}}) + requestOpts = grpc.WithTransportCredentials(h2creds) + } + return requestOpts +} diff --git a/netclient/server/grpc.go b/netclient/server/grpc.go index 887c8cf1..870a7464 100644 --- a/netclient/server/grpc.go +++ b/netclient/server/grpc.go @@ -1,7 +1,6 @@ package server import ( - "crypto/tls" "encoding/json" "log" "net" @@ -12,25 +11,20 @@ import ( nodepb "github.com/gravitl/netmaker/grpc" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/netclient/auth" + "github.com/gravitl/netmaker/netclient/netclientutils" "github.com/gravitl/netmaker/netclient/config" "github.com/gravitl/netmaker/netclient/local" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "google.golang.org/grpc" - "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" ) func getGrpcClient(cfg *config.ClientConfig) (nodepb.NodeServiceClient, error) { - servercfg := cfg.Server var wcclient nodepb.NodeServiceClient // == GRPC SETUP == - var requestOpts grpc.DialOption - requestOpts = grpc.WithInsecure() - if cfg.Server.GRPCSSL == "on" { - h2creds := credentials.NewTLS(&tls.Config{NextProtos: []string{"h2"}}) - requestOpts = grpc.WithTransportCredentials(h2creds) - } - conn, err := grpc.Dial(servercfg.GRPCAddress, requestOpts) + conn, err := grpc.Dial(cfg.Server.GRPCAddress, + netclientutils.GRPCRequestOpts(cfg.Server.GRPCSSL)) + if err != nil { return nil, err } @@ -83,13 +77,8 @@ func RemoveNetwork(network string) error { log.Println("Deleting remote node with MAC: " + node.MacAddress) var wcclient nodepb.NodeServiceClient - var requestOpts grpc.DialOption - requestOpts = grpc.WithInsecure() - if cfg.Server.GRPCSSL == "on" { - h2creds := credentials.NewTLS(&tls.Config{NextProtos: []string{"h2"}}) - requestOpts = grpc.WithTransportCredentials(h2creds) - } - conn, err := grpc.Dial(servercfg.GRPCAddress, requestOpts) + conn, err := grpc.Dial(cfg.Server.GRPCAddress, + netclientutils.GRPCRequestOpts(cfg.Server.GRPCSSL)) if err != nil { log.Printf("Unable to establish client connection to "+servercfg.GRPCAddress+": %v", err) //return err @@ -147,13 +136,9 @@ func GetPeers(macaddress string, network string, server string, dualstack bool, log.Fatalf("Issue with format of keepalive value. Please update netconfig: %v", err) } - requestOpts := grpc.WithInsecure() - if cfg.Server.GRPCSSL == "on" { - h2creds := credentials.NewTLS(&tls.Config{NextProtos: []string{"h2"}}) - requestOpts = grpc.WithTransportCredentials(h2creds) - } + conn, err := grpc.Dial(cfg.Server.GRPCAddress, + netclientutils.GRPCRequestOpts(cfg.Server.GRPCSSL)) - conn, err := grpc.Dial(server, requestOpts) if err != nil { log.Fatalf("Unable to establish client connection to localhost:50051: %v", err) } @@ -296,14 +281,8 @@ func GetExtPeers(macaddress string, network string, server string, dualstack boo } nodecfg := cfg.Node - requestOpts := grpc.WithInsecure() - if cfg.Server.GRPCSSL == "on" { - h2creds := credentials.NewTLS(&tls.Config{NextProtos: []string{"h2"}}) - requestOpts = grpc.WithTransportCredentials(h2creds) - } - - conn, err := grpc.Dial(server, requestOpts) - + conn, err := grpc.Dial(cfg.Server.GRPCAddress, + netclientutils.GRPCRequestOpts(cfg.Server.GRPCSSL)) if err != nil { log.Fatalf("Unable to establish client connection to localhost:50051: %v", err) } diff --git a/netclient/wireguard/kernel.go b/netclient/wireguard/kernel.go index 022ffdb8..55b30139 100644 --- a/netclient/wireguard/kernel.go +++ b/netclient/wireguard/kernel.go @@ -84,7 +84,6 @@ func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig } var nodeport int nodeport = int(node.ListenPort) - conf := wgtypes.Config{} if nodecfg.UDPHolePunch == "yes" && nodecfg.IsServer == "no" && @@ -127,14 +126,6 @@ func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig return err } - devices, err := wgclient.Devices() - if err != nil { - log.Println("no devices found:", err) - } - for _, d := range devices { - log.Println("found wg device:", d.Name) - } - _, err = wgclient.Device(ifacename) if err != nil { if os.IsNotExist(err) { @@ -144,7 +135,7 @@ func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig log.Fatalf("Unknown config error: %v", err) } } - + err = wgclient.ConfigureDevice(ifacename, conf) if err != nil { if os.IsNotExist(err) { diff --git a/servercfg/serverconf.go b/servercfg/serverconf.go index 7fa4c9bb..377e16b6 100644 --- a/servercfg/serverconf.go +++ b/servercfg/serverconf.go @@ -80,10 +80,10 @@ func GetVersion() string { return version } func GetDB() string { - database := "rqlite" - if os.Getenv("DATABASE") == "sqlite" { + database := "sqlite" + if os.Getenv("DATABASE") == "rqlite" { database = os.Getenv("DATABASE") - } else if config.Config.Server.Database == "sqlite" { + } else if config.Config.Server.Database == "rqlite" { database = config.Config.Server.Database } return database