From 5b955f2201be2a62864dad6fb118e4e65a577308 Mon Sep 17 00:00:00 2001 From: afeiszli Date: Tue, 6 Apr 2021 12:33:01 -0400 Subject: [PATCH 1/3] returning string for access key --- controllers/groupHttpController.go | 2 +- main.go | 8 ++++ serverctl/serverctl.go | 75 ++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 serverctl/serverctl.go diff --git a/controllers/groupHttpController.go b/controllers/groupHttpController.go index 44c0b980..d824aea9 100644 --- a/controllers/groupHttpController.go +++ b/controllers/groupHttpController.go @@ -551,7 +551,7 @@ func createAccessKey(w http.ResponseWriter, r *http.Request) { mongoconn.GetError(errN, w) return } - w.Write([]byte(accesskey.Value)) + w.Write([]byte(accesskey.AccessString)) } //pretty simple get diff --git a/main.go b/main.go index 3211d7f9..2e1fcb76 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "log" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/controllers" + //"github.com/gravitl/netmaker/serverctl" "github.com/gravitl/netmaker/functions" "github.com/gravitl/netmaker/mongoconn" "github.com/gravitl/netmaker/config" @@ -38,6 +39,13 @@ func main() { err := createDefaultNetwork() if err != nil { fmt.Printf("Error creating default network: %v", err) + } else { + /* + err = serverctl.NetworkAdd(config.Config.Server.DefaultNetName) + if err != nil { + fmt.Printf("Error adding to default network: %v", err) + } + */ } } diff --git a/serverctl/serverctl.go b/serverctl/serverctl.go new file mode 100644 index 00000000..d72632d9 --- /dev/null +++ b/serverctl/serverctl.go @@ -0,0 +1,75 @@ +package serverctl + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" +) + + +func fileExists(f string) bool { + info, err := os.Stat(f) + if os.IsNotExist(err) { + return false + } + notisdir := !info.IsDir() + return notisdir +} + + +func installScript() error { + + + installScript := `#!/bin/sh +set -e + +[ -z "$SERVER_URL" ] && echo "Need to set SERVER_URL" && exit 1; +[ -z "$NET_NAME" ] && echo "Need to set NET_NAME" && exit 1; +[ -z "$KEY" ] && KEY=nokey; + + + +wget -O netclient https://github.com/gravitl/netmaker/releases/download/develop/netclient +chmod +x netclient +sudo ./netclient -c install -s $SERVER_URL -g $NET_NAME -k $KEY +rm -f netclient +` + + installbytes := []byte(installScript) + + err := ioutil.WriteFile("/etc/netclient/netclient-install.sh", installbytes, 0755) + if err != nil { + log.Println(err) + return err + } + return err +} + + +func NetworkAdd(network string) error { + _, err := os.Stat("/etc/netclient") + if os.IsNotExist(err) { + os.Mkdir("/etc/netclient", 744) + } else if err != nil { + fmt.Println("couldnt find or create /etc/netclient") + return err + } + if !fileExists("/etc/netclient/netclient-install.sh") { + err = installScript() + if err != nil { + log.Println(err) + return err + } + } + + cmdoutput, err := exec.Command("/bin/sh", "/etc/netclient/netclient-install.sh").Output() + if err != nil { + fmt.Printf("Error installing netclient: %s", err) + } + fmt.Println(cmdoutput) + return err +} + + From 92440745b2c7ebf310e8d56d2a195d837f3edac1 Mon Sep 17 00:00:00 2001 From: Matthew R Kasun Date: Tue, 6 Apr 2021 17:44:11 -0400 Subject: [PATCH 2/3] github actions to build/publish dockerfiles on push --- .github/workflows/publish-docker-latest.yml | 33 +++++++++++++++++++ .github/workflows/publish-docker.yml | 35 +++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 .github/workflows/publish-docker-latest.yml create mode 100644 .github/workflows/publish-docker.yml diff --git a/.github/workflows/publish-docker-latest.yml b/.github/workflows/publish-docker-latest.yml new file mode 100644 index 00000000..2aae2ce7 --- /dev/null +++ b/.github/workflows/publish-docker-latest.yml @@ -0,0 +1,33 @@ +name: Publish Docker + +on: + push: + branches: + - 'master' +jobs: + docker: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - + name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + platforms: linux/amd64, linux/arm64 + push: true + tags: gravitl/netmaker:latest diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml new file mode 100644 index 00000000..5942bf11 --- /dev/null +++ b/.github/workflows/publish-docker.yml @@ -0,0 +1,35 @@ +name: Publish Docker + +on: + push: + branches: + - 'arm-docker' + - 'develop' + - +jobs: + docker: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - + name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + platforms: linux/amd64, linux/arm64 + push: true + tags: gravitl/netmaker:dev From f3f2140f23fbc8b040195a15728bfb2e5a75ca70 Mon Sep 17 00:00:00 2001 From: afeiszli Date: Tue, 6 Apr 2021 19:13:34 -0400 Subject: [PATCH 3/3] add remove server from networks with api. Add to default network by default. --- controllers/common.go | 1 + controllers/controller.go | 1 + controllers/serverHttpController.go | 90 ++++++++++++++++++++++++++ functions/helpers.go | 52 ++++++++++++++++ main.go | 45 ++++++++----- netclient/functions/common.go | 5 +- netclient/main.go | 3 +- serverctl/serverctl.go | 97 +++++++++++++++-------------- 8 files changed, 230 insertions(+), 64 deletions(-) create mode 100644 controllers/serverHttpController.go diff --git a/controllers/common.go b/controllers/common.go index d43fa7d8..237ea87b 100644 --- a/controllers/common.go +++ b/controllers/common.go @@ -117,6 +117,7 @@ func ValidateNode(operation string, groupName string, node models.Node) error { return err } + func UpdateNode(nodechange models.Node, node models.Node) (models.Node, error) { //Question: Is there a better way of doing this than a bunch of "if" statements? probably... //Eventually, lets have a better way to check if any of the fields are filled out... diff --git a/controllers/controller.go b/controllers/controller.go index bcf28378..338f765b 100644 --- a/controllers/controller.go +++ b/controllers/controller.go @@ -29,6 +29,7 @@ func HandleRESTRequests(wg *sync.WaitGroup) { userHandlers(r) groupHandlers(r) fileHandlers(r) + serverHandlers(r) port := config.Config.Server.ApiPort if os.Getenv("API_PORT") != "" { diff --git a/controllers/serverHttpController.go b/controllers/serverHttpController.go new file mode 100644 index 00000000..88374534 --- /dev/null +++ b/controllers/serverHttpController.go @@ -0,0 +1,90 @@ +package controller + +import ( + "github.com/gravitl/netmaker/models" + "github.com/gravitl/netmaker/serverctl" + "github.com/gravitl/netmaker/config" + "encoding/json" + "strings" + "net/http" + "github.com/gorilla/mux" +) + +func serverHandlers(r *mux.Router) { + r.HandleFunc("/api/server/addnetwork/{network}", securityCheckServer(http.HandlerFunc(addNetwork))).Methods("POST") + r.HandleFunc("/api/server/removenetwork/{network}", securityCheckServer(http.HandlerFunc(removeNetwork))).Methods("DELETE") +} + +//Security check is middleware for every function and just checks to make sure that its the master calling +//Only admin should have access to all these group-level actions +//or maybe some Users once implemented +func securityCheckServer(next http.Handler) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var errorResponse = models.ErrorResponse{ + Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.", + } + + bearerToken := r.Header.Get("Authorization") + + var hasBearer = true + var tokenSplit = strings.Split(bearerToken, " ") + var authToken = "" + + if len(tokenSplit) < 2 { + hasBearer = false + } else { + authToken = tokenSplit[1] + } + //all endpoints here require master so not as complicated + //still might not be a good way of doing this + if !hasBearer || !authenticateMasterServer(authToken) { + errorResponse = models.ErrorResponse{ + Code: http.StatusUnauthorized, Message: "W1R3: You are unauthorized to access this endpoint.", + } + returnErrorResponse(w, r, errorResponse) + } else { + next.ServeHTTP(w, r) + } + } +} +//Consider a more secure way of setting master key +func authenticateMasterServer(tokenString string) bool { + if tokenString == config.Config.Server.MasterKey { + return true + } + return false +} + +func removeNetwork(w http.ResponseWriter, r *http.Request) { + // Set header + w.Header().Set("Content-Type", "application/json") + + // get params + var params = mux.Vars(r) + + success, err := serverctl.RemoveNetwork(params["network"]) + + if err != nil || !success { + json.NewEncoder(w).Encode("Could not remove server from network " + params["network"]) + return + } + + json.NewEncoder(w).Encode("Server removed from network " + params["network"]) +} + +func addNetwork(w http.ResponseWriter, r *http.Request) { + // Set header + w.Header().Set("Content-Type", "application/json") + + // get params + var params = mux.Vars(r) + + success, err := serverctl.AddNetwork(params["network"]) + + if err != nil || !success { + json.NewEncoder(w).Encode("Could not add server to network " + params["network"]) + return + } + + json.NewEncoder(w).Encode("Server added to network " + params["network"]) +} diff --git a/functions/helpers.go b/functions/helpers.go index d7a3a3fc..8a594a21 100644 --- a/functions/helpers.go +++ b/functions/helpers.go @@ -24,6 +24,58 @@ import ( //Takes in an arbitrary field and value for field and checks to see if any other //node has that value for the same field within the group + +func CreateServerToken(network string) (string, error) { + + var group models.Group + var accesskey models.AccessKey + + group, err := GetParentGroup(network) + if err != nil { + return "", err + } + + accesskey.Name = GenKeyName() + accesskey.Value = GenKey() + accesskey.Uses = 1 + gconf, errG := GetGlobalConfig() + if errG != nil { + return "", errG + } + address := "localhost" + gconf.PortGRPC + + accessstringdec := address + "." + network + "." + accesskey.Value + accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(accessstringdec)) + + group.AccessKeys = append(group.AccessKeys, accesskey) + + collection := mongoconn.Client.Database("netmaker").Collection("groups") + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + + // Create filter + filter := bson.M{"nameid": network} + + // Read update model from body request + fmt.Println("Adding key to " + group.NameID) + + // prepare update model. + update := bson.D{ + {"$set", bson.D{ + {"accesskeys", group.AccessKeys}, + }}, + } + + errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&group) + + defer cancel() + + if errN != nil { + return "", errN + } + return accesskey.AccessString, nil +} + func IsFieldUnique(group string, field string, value string) bool { var node models.Node diff --git a/main.go b/main.go index 2e1fcb76..ee873ef7 100644 --- a/main.go +++ b/main.go @@ -7,7 +7,7 @@ import ( "log" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/controllers" - //"github.com/gravitl/netmaker/serverctl" + "github.com/gravitl/netmaker/serverctl" "github.com/gravitl/netmaker/functions" "github.com/gravitl/netmaker/mongoconn" "github.com/gravitl/netmaker/config" @@ -34,18 +34,14 @@ var PortGRPC string func main() { log.Println("Server starting...") mongoconn.ConnectDatabase() - + installserver := false if config.Config.Server.CreateDefault { - err := createDefaultNetwork() + created, err := createDefaultNetwork() if err != nil { fmt.Printf("Error creating default network: %v", err) - } else { - /* - err = serverctl.NetworkAdd(config.Config.Server.DefaultNetName) - if err != nil { - fmt.Printf("Error adding to default network: %v", err) - } - */ + } + if created { + installserver = true } } @@ -53,7 +49,7 @@ func main() { if config.Config.Server.AgentBackend { waitgroup.Add(1) - go runGRPC(&waitgroup) + go runGRPC(&waitgroup, installserver) } if config.Config.Server.RestBackend { @@ -68,7 +64,7 @@ func main() { } -func runGRPC(wg *sync.WaitGroup) { +func runGRPC(wg *sync.WaitGroup, installserver bool) { defer wg.Done() @@ -134,6 +130,20 @@ func runGRPC(wg *sync.WaitGroup) { }() fmt.Println("Agent Server succesfully started on port " + grpcport + " (gRPC)") + if installserver { + fmt.Println("Adding server to default network") + success, err := serverctl.AddNetwork(config.Config.Server.DefaultNetName) + if err != nil || !success { + fmt.Printf("Error adding to default network: %v", err) + fmt.Println("Unable to add server to network. Continuing.") + } else { + fmt.Println("Server successfully added to default network.") + } + } + fmt.Println("Setup complete. You are ready to begin using netmaker.") + + + // Right way to stop the server using a SHUTDOWN HOOK // Create a channel to receive OS signals c := make(chan os.Signal) @@ -180,15 +190,15 @@ func setGlobalConfig(globalconf models.GlobalConfig) (error) { return nil } -func createDefaultNetwork() error { - +func createDefaultNetwork() (bool, error) { + iscreated := false exists, err := functions.GroupExists(config.Config.Server.DefaultNetName) if exists || err != nil { fmt.Println("Default group already exists") fmt.Println("Skipping default group create") - return err + return iscreated, err } else { var group models.Group @@ -215,7 +225,10 @@ func createDefaultNetwork() error { defer cancel() } - return err + if err == nil { + iscreated = true + } + return iscreated, err } diff --git a/netclient/functions/common.go b/netclient/functions/common.go index b27d48f0..fd32f501 100644 --- a/netclient/functions/common.go +++ b/netclient/functions/common.go @@ -29,7 +29,7 @@ var ( wcclient nodepb.NodeServiceClient ) -func Install(accesskey string, password string, server string, group string, noauto bool, accesstoken string) error { +func Install(accesskey string, password string, server string, group string, noauto bool, accesstoken string, inputname string) error { tserver := "" @@ -189,6 +189,9 @@ func Install(accesskey string, password string, server string, group string, noa if nodecfg.Name != "" { name = nodecfg.Name } + if inputname != "" && inputname != "noname" { + name = inputname + } fmt.Println(" Name: " + name) diff --git a/netclient/main.go b/netclient/main.go index c24e60e6..f69a9dc8 100644 --- a/netclient/main.go +++ b/netclient/main.go @@ -36,6 +36,7 @@ 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 group you are attempting to join.") tnoauto := flag.Bool("na", false, "No auto mode. If true, netmclient will not be installed as a system service and you will have to retrieve updates manually via checkin command.") @@ -100,7 +101,7 @@ func main() { } fmt.Println("Beginning agent installation.") - err := functions.Install(*taccesskey, *tpassword, *tserver, *tnetwork, *tnoauto, *taccesstoken) + err := functions.Install(*taccesskey, *tpassword, *tserver, *tnetwork, *tnoauto, *taccesstoken, *tname) if err != nil { fmt.Println("Error installing: ", err) fmt.Println("Cleaning up (uninstall)") diff --git a/serverctl/serverctl.go b/serverctl/serverctl.go index d72632d9..7ab218bc 100644 --- a/serverctl/serverctl.go +++ b/serverctl/serverctl.go @@ -2,74 +2,79 @@ package serverctl import ( "fmt" - "io/ioutil" - "log" + "github.com/gravitl/netmaker/functions" + "io" + "net/http" "os" "os/exec" ) -func fileExists(f string) bool { - info, err := os.Stat(f) - if os.IsNotExist(err) { - return false - } - notisdir := !info.IsDir() - return notisdir -} +func DownloadNetclient() error { + // Get the data + resp, err := http.Get("https://github.com/gravitl/netmaker/releases/download/develop/netclient") + if err != nil { + return err + } + defer resp.Body.Close() -func installScript() error { + // Create the file + out, err := os.Create("/etc/netclient/netclient") + if err != nil { + return err + } + defer out.Close() - - installScript := `#!/bin/sh -set -e - -[ -z "$SERVER_URL" ] && echo "Need to set SERVER_URL" && exit 1; -[ -z "$NET_NAME" ] && echo "Need to set NET_NAME" && exit 1; -[ -z "$KEY" ] && KEY=nokey; - - - -wget -O netclient https://github.com/gravitl/netmaker/releases/download/develop/netclient -chmod +x netclient -sudo ./netclient -c install -s $SERVER_URL -g $NET_NAME -k $KEY -rm -f netclient -` - - installbytes := []byte(installScript) - - err := ioutil.WriteFile("/etc/netclient/netclient-install.sh", installbytes, 0755) - if err != nil { - log.Println(err) - return err - } + // Write the body to file + _, err = io.Copy(out, resp.Body) return err } +func RemoveNetwork(network string) (bool, error) { + _, err := os.Stat("/etc/netclient/netclient") + if err != nil { + return false, err + } + cmdoutput, err := exec.Command("/etc/netclient/netclient","-c","remove","-n",network).Output() + if err != nil { + fmt.Println(string(cmdoutput)) + return false, err + } + fmt.Println("Server removed from network " + network) + return true, err +} -func NetworkAdd(network string) error { +func AddNetwork(network string) (bool, error) { _, err := os.Stat("/etc/netclient") if os.IsNotExist(err) { os.Mkdir("/etc/netclient", 744) } else if err != nil { fmt.Println("couldnt find or create /etc/netclient") - return err + return false, err } - if !fileExists("/etc/netclient/netclient-install.sh") { - err = installScript() + token, err := functions.CreateServerToken(network) if err != nil { - log.Println(err) - return err + return false, err } + _, err = os.Stat("/etc/netclient/netclient") + if os.IsNotExist(err) { + err = DownloadNetclient() + if err != nil { + return false, err + } } - - cmdoutput, err := exec.Command("/bin/sh", "/etc/netclient/netclient-install.sh").Output() + err = os.Chmod("/etc/netclient/netclient", 0755) + if err != nil { + return false, err + } + cmdoutput, err := exec.Command("/etc/netclient/netclient","-c","install","-t",token,"-name","netmaker").Output() if err != nil { - fmt.Printf("Error installing netclient: %s", err) - } - fmt.Println(cmdoutput) - return err + fmt.Println(string(cmdoutput)) + return false, err + } + fmt.Println("Server added to network " + network) + return true, err }