From 80532f6eb8c0d08fae9048129432bfa2373f7a56 Mon Sep 17 00:00:00 2001 From: George Shaw Date: Fri, 6 Apr 2018 15:37:35 -0500 Subject: [PATCH 1/2] added checks to bundle creation process, preventing most common errors, referencing issue #135. removed system package and instead using package github.com/george-e-shaw-iv/nixtools --- pkg/{system => api/bundle}/constants.go | 2 +- pkg/api/bundle/create.go | 97 +++++++++++-- pkg/api/bundle/delete.go | 15 +- pkg/api/ssh/addkey.go | 11 +- pkg/api/ssh/deletekey.go | 11 +- pkg/api/ssh/getkeys.go | 11 +- pkg/database/settings.go | 54 ++++++- pkg/emailer/emailer.go | 4 + pkg/system/keys.go | 73 ---------- pkg/system/user.go | 178 ------------------------ 10 files changed, 178 insertions(+), 278 deletions(-) rename pkg/{system => api/bundle}/constants.go (99%) delete mode 100644 pkg/system/keys.go delete mode 100644 pkg/system/user.go diff --git a/pkg/system/constants.go b/pkg/api/bundle/constants.go similarity index 99% rename from pkg/system/constants.go rename to pkg/api/bundle/constants.go index a4a734a..0d66be3 100644 --- a/pkg/system/constants.go +++ b/pkg/api/bundle/constants.go @@ -1,4 +1,4 @@ -package system +package bundle const DEFAULT_INDEX = ` diff --git a/pkg/api/bundle/create.go b/pkg/api/bundle/create.go index b553d6c..4bd0ddd 100644 --- a/pkg/api/bundle/create.go +++ b/pkg/api/bundle/create.go @@ -13,7 +13,7 @@ import ( "github.com/Ennovar/gPanel/pkg/emailer" "github.com/Ennovar/gPanel/pkg/encryption" "github.com/Ennovar/gPanel/pkg/gpaccount" - "github.com/Ennovar/gPanel/pkg/system" + "github.com/george-e-shaw-iv/nixtools" ) func Create(res http.ResponseWriter, req *http.Request, logger *log.Logger, bundles map[string]*gpaccount.Controller) bool { @@ -37,7 +37,7 @@ func Create(res http.ResponseWriter, req *http.Request, logger *log.Logger, bund return false } - // Check if account port is in use by system + /* Check if account port is in use by system */ check, err := net.Listen("tcp", ":"+strconv.Itoa(createBundleRequestData.AccPort)) if err != nil { logger.Println(req.URL.Path + "::" + "a service is already listening on port " + strconv.Itoa(createBundleRequestData.AccPort) + "::" + err.Error()) @@ -46,7 +46,7 @@ func Create(res http.ResponseWriter, req *http.Request, logger *log.Logger, bund } check.Close() - // Check if public port is in use by system + /* Check if public port is in use by system */ check, err = net.Listen("tcp", ":"+strconv.Itoa(createBundleRequestData.PubPort)) if err != nil { logger.Println(req.URL.Path + "::" + "a service is already listening on port " + strconv.Itoa(createBundleRequestData.PubPort) + "::" + err.Error()) @@ -55,7 +55,7 @@ func Create(res http.ResponseWriter, req *http.Request, logger *log.Logger, bund } check.Close() - // Check if public/account ports are in use by another bundle + /* Check if public/account ports are in use by another bundle */ err = nil for k, v := range bundles { if k == createBundleRequestData.Name { @@ -78,11 +78,33 @@ func Create(res http.ResponseWriter, req *http.Request, logger *log.Logger, bund return false } - //check mail creds exist and work - //check admin settings are set - //check if can use username - //if those pass, then continue with bundle creation + /* Check if admin settings are set and valid */ + ds, err := database.Open("server/" + database.DB_SETTINGS) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + err = ds.CheckAdminSettings() + if err != nil { + ds.Close() + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusBadRequest) + return false + } + ds.Close() + + /* Check if bundle username can be used within the system */ + if userExists := nixtools.UserExists(createBundleRequestData.Name); userExists { + logger.Println(req.URL.Path + "::username already exists under that bundle name within the system") + http.Error(res, err.Error(), http.StatusBadRequest) + return false + } + + /* IF ALL CHECKS PASSED, start the bundle creation process */ + + /* Create the bundle directory within the gPanel directory */ newBundle := "bundles/" + createBundleRequestData.Name err = os.Mkdir(newBundle, 0777) if err != nil { @@ -91,6 +113,7 @@ func Create(res http.ResponseWriter, req *http.Request, logger *log.Logger, bund return false } + /* Create the log folder within the new bundle directory */ err = os.Mkdir(newBundle+"/logs", 0777) if err != nil { logger.Println(req.URL.Path + "::" + err.Error()) @@ -98,13 +121,15 @@ func Create(res http.ResponseWriter, req *http.Request, logger *log.Logger, bund return false } - ds, err := database.Open(newBundle + "/" + database.DB_MAIN) + /* Create and open the newly created bundle's main datastore */ + ds, err = database.Open(newBundle + "/" + database.DB_MAIN) if err != nil { logger.Println(req.URL.Path + "::" + err.Error()) http.Error(res, err.Error(), http.StatusInternalServerError) return false } + /* Put the new bundle's ports within the datastore */ var databaseBundlePorts struct { Account int `json:"account"` Public int `json:"public"` @@ -114,16 +139,19 @@ func Create(res http.ResponseWriter, req *http.Request, logger *log.Logger, bund err = ds.Put(database.BUCKET_PORTS, []byte("bundle_ports"), databaseBundlePorts) if err != nil { + ds.Close() logger.Println(req.URL.Path + "::" + err.Error()) http.Error(res, err.Error(), http.StatusInternalServerError) return false } + /* Create the default bundle username and password */ var defaultBundleUser database.Struct_Users var tempPass = encryption.RandomString(16) defaultBundleUser.Pass, err = encryption.HashPassword(tempPass) if err != nil { + ds.Close() logger.Println(req.URL.Path + "::" + err.Error()) http.Error(res, err.Error(), http.StatusInternalServerError) return false @@ -133,19 +161,46 @@ func Create(res http.ResponseWriter, req *http.Request, logger *log.Logger, bund err = ds.Put(database.BUCKET_USERS, []byte("root"), defaultBundleUser) if err != nil { + ds.Close() logger.Println(req.URL.Path + "::" + err.Error()) http.Error(res, err.Error(), http.StatusInternalServerError) return false } ds.Close() - err, err2 := system.CreateBundleUser(createBundleRequestData.Name) + /* Create a system user for the new bundle */ + sysUser, err := nixtools.GetUser(createBundleRequestData.Name, true) if err != nil { - logger.Println(req.URL.Path + "::" + err.Error() + " AND " + err2.Error()) - http.Error(res, err2.Error(), http.StatusInternalServerError) + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) return false } + /* Initialize SSH access for the new user and give the root account access */ + err = sysUser.InitSSH(true) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + + /* Create document_root within new system user's home directory */ + err = sysUser.CreateDirectory("document_root", 0777) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + + /* Write the default index within the document_root */ + err = sysUser.WriteFile("document_root/index.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777, []byte(DEFAULT_INDEX)) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + + /* Add newly created bundle to list of current bundles (run-time list) */ bundles[createBundleRequestData.Name], err = gpaccount.New(newBundle+"/", createBundleRequestData.Name, databaseBundlePorts.Account, databaseBundlePorts.Public) if err != nil { @@ -154,9 +209,22 @@ func Create(res http.ResponseWriter, req *http.Request, logger *log.Logger, bund return false } - _ = bundles[createBundleRequestData.Name].Start() - _ = bundles[createBundleRequestData.Name].Public.Start() + /* Start the account server and public server of the newly created bundle */ + err = bundles[createBundleRequestData.Name].Start() + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + err = bundles[createBundleRequestData.Name].Public.Start() + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + + /* Get SMTP and Admin general settings for use in the email to be sent to new bundle designated email */ ds, err = database.Open("server/" + database.DB_SETTINGS) if err != nil { logger.Println(req.URL.Path + "::" + err.Error()) @@ -182,6 +250,7 @@ func Create(res http.ResponseWriter, req *http.Request, logger *log.Logger, bund return false } + /* Send email with information about system and bundle account to designated bundle email AND gPanel admin */ mail, err := emailer.New(smtpSettings.Type, emailer.Credentials{ Username: smtpSettings.Username, Password: smtpSettings.Password, diff --git a/pkg/api/bundle/delete.go b/pkg/api/bundle/delete.go index e58ecf7..c6b8eb9 100644 --- a/pkg/api/bundle/delete.go +++ b/pkg/api/bundle/delete.go @@ -9,7 +9,7 @@ import ( "github.com/Ennovar/gPanel/pkg/database" "github.com/Ennovar/gPanel/pkg/gpaccount" - "github.com/Ennovar/gPanel/pkg/system" + "github.com/george-e-shaw-iv/nixtools" ) func Delete(res http.ResponseWriter, req *http.Request, logger *log.Logger, bundles map[string]*gpaccount.Controller, dir string) bool { @@ -63,10 +63,17 @@ func Delete(res http.ResponseWriter, req *http.Request, logger *log.Logger, bund delete(bundles, rqData.Name) - err, err2 := system.DeleteBundleUser(rqData.Name) + sysUser, err := nixtools.GetUser(rqData.Name, false) if err != nil { - logger.Println(req.URL.Path + "::" + err.Error() + " AND " + err2.Error()) - http.Error(res, err2.Error(), http.StatusInternalServerError) + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + + err = sysUser.Delete(true, false) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) return false } diff --git a/pkg/api/ssh/addkey.go b/pkg/api/ssh/addkey.go index 792cd9b..bc13776 100644 --- a/pkg/api/ssh/addkey.go +++ b/pkg/api/ssh/addkey.go @@ -6,7 +6,7 @@ import ( "net/http" "strconv" - "github.com/Ennovar/gPanel/pkg/system" + "github.com/george-e-shaw-iv/nixtools" ) func AddKey(res http.ResponseWriter, req *http.Request, logger *log.Logger) bool { @@ -28,7 +28,14 @@ func AddKey(res http.ResponseWriter, req *http.Request, logger *log.Logger) bool return false } - if err = system.AddAuthorizedKey(requestData.Username, requestData.PublicKey); err != nil { + sysUser, err := nixtools.GetUser(requestData.Username, false) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusBadRequest) + return false + } + + if err = sysUser.AddAuthorizedKey(requestData.PublicKey); err != nil { logger.Println(req.URL.Path + "::" + err.Error()) http.Error(res, err.Error(), http.StatusInternalServerError) return false diff --git a/pkg/api/ssh/deletekey.go b/pkg/api/ssh/deletekey.go index 865278f..e8ff4e1 100644 --- a/pkg/api/ssh/deletekey.go +++ b/pkg/api/ssh/deletekey.go @@ -6,7 +6,7 @@ import ( "net/http" "strconv" - "github.com/Ennovar/gPanel/pkg/system" + "github.com/george-e-shaw-iv/nixtools" ) func DeleteKey(res http.ResponseWriter, req *http.Request, logger *log.Logger) bool { @@ -28,7 +28,14 @@ func DeleteKey(res http.ResponseWriter, req *http.Request, logger *log.Logger) b return false } - if err = system.DeleteAuthorizedKey(requestData.Username, requestData.PublicKey); err != nil { + sysUser, err := nixtools.GetUser(requestData.Username, false) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusBadRequest) + return false + } + + if err = sysUser.DeleteAuthorizedKey(requestData.PublicKey); err != nil { logger.Println(req.URL.Path + "::" + err.Error()) http.Error(res, err.Error(), http.StatusInternalServerError) return false diff --git a/pkg/api/ssh/getkeys.go b/pkg/api/ssh/getkeys.go index 5d13c33..66ba888 100644 --- a/pkg/api/ssh/getkeys.go +++ b/pkg/api/ssh/getkeys.go @@ -6,7 +6,7 @@ import ( "net/http" "strconv" - "github.com/Ennovar/gPanel/pkg/system" + "github.com/george-e-shaw-iv/nixtools" ) func GetKeys(res http.ResponseWriter, req *http.Request, logger *log.Logger) bool { @@ -27,8 +27,15 @@ func GetKeys(res http.ResponseWriter, req *http.Request, logger *log.Logger) boo return false } + sysUser, err := nixtools.GetUser(requestData.Username, false) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusBadRequest) + return false + } + var keys []string - if keys, err = system.GetAuthorizedKeys(requestData.Username); err != nil { + if keys, err = sysUser.GetAuthorizedKeys(true); err != nil { logger.Println(req.URL.Path + "::" + err.Error()) http.Error(res, err.Error(), http.StatusInternalServerError) return false diff --git a/pkg/database/settings.go b/pkg/database/settings.go index 355a6da..e8e2864 100644 --- a/pkg/database/settings.go +++ b/pkg/database/settings.go @@ -1,5 +1,13 @@ package database +import ( + "encoding/json" + "errors" + + "github.com/Ennovar/gPanel/pkg/emailer" + "github.com/boltdb/bolt" +) + type Struct_SMTP struct { Type string `json:"type"` Username string `json:"username"` @@ -9,6 +17,48 @@ type Struct_SMTP struct { } type Struct_Admin struct { - Name string `json:"name"` + Name string `json:"name"` Email string `json:"email"` -} \ No newline at end of file +} + +// Function CheckAdminSettings makes sure that the +// admin settings are set and are valid. +func (ds *Datastore) CheckAdminSettings() (rerr error) { + rerr = ds.handle.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(BUCKET_GENERAL)) + smtp := b.Get([]byte("smtp")) + + var smtpCreds Struct_SMTP + err := json.Unmarshal(smtp, &smtpCreds) + if err != nil { + return err + } + + _, err = emailer.New(smtpCreds.Type, emailer.Credentials{ + Username: smtpCreds.Username, + Password: smtpCreds.Password, + Server: smtpCreds.Server, + Port: smtpCreds.Port, + }) + + if err != nil { + return err + } + + a := b.Get([]byte("admin")) + + var admin Struct_Admin + err = json.Unmarshal(a, &admin) + if err != nil { + return err + } + + if len(admin.Email) == 0 || len(admin.Name) == 0 { + return errors.New("admin name and email settings are empty") + } + + return nil + }) + + return +} diff --git a/pkg/emailer/emailer.go b/pkg/emailer/emailer.go index 70e43bb..0f05c74 100644 --- a/pkg/emailer/emailer.go +++ b/pkg/emailer/emailer.go @@ -72,3 +72,7 @@ func (e *Emailer) SendCustom(to string, msg []byte) error { return nil } + +func (e *Emailer) Test() { + +} diff --git a/pkg/system/keys.go b/pkg/system/keys.go deleted file mode 100644 index 480af86..0000000 --- a/pkg/system/keys.go +++ /dev/null @@ -1,73 +0,0 @@ -package system - -import ( - "io/ioutil" - "os" - "strings" -) - -func AddAuthorizedKey(username, key string) error { - f, err := os.OpenFile("/home/"+username+"/.ssh/authorized_keys", os.O_APPEND|os.O_WRONLY, 0644) - if err != nil { - return err - } - - if _, err = f.WriteString(key + "\n"); err != nil { - return err - } - - return nil -} - -func DeleteAuthorizedKey(username, key string) error { - old, err := ioutil.ReadFile("/home/" + username + "/.ssh/authorized_keys") - if err != nil { - return err - } - - lines := strings.Split(string(old), "\n") - - for i, line := range lines { - if strings.Contains(line, key) { - lines = append(lines[:i], lines[i+1:]...) - break - } - } - - new := strings.Join(lines, "\n") - - f, err := os.OpenFile("/home/"+username+"/.ssh/authorized_keys", os.O_TRUNC|os.O_WRONLY, 0644) - if err != nil { - return err - } - - if _, err = f.WriteString(new); err != nil { - return err - } - - return nil -} - -func GetAuthorizedKeys(username string) ([]string, error) { - f, err := ioutil.ReadFile("/home/" + username + "/.ssh/authorized_keys") - if err != nil { - return nil, err - } - - // Remove empty strings - raw := strings.Split(string(f), "\n") - var clean []string - - for _, each := range raw { - if each != "" { - clean = append(clean, each) - } - } - - // Remove root key - if len(clean) == 1 { - return nil, nil - } - - return clean[1:], nil -} diff --git a/pkg/system/user.go b/pkg/system/user.go deleted file mode 100644 index c6f0d61..0000000 --- a/pkg/system/user.go +++ /dev/null @@ -1,178 +0,0 @@ -package system - -import ( - "bytes" - "errors" - "fmt" - "os" - "os/exec" -) - -func CreateBundleUser(username string) (error, error) { - var cerr bytes.Buffer - var err error - - adduserArgs := []string{ - "--disabled-password", - "--gecos", - "", - username, - } - - keygenArgs := []string{ - "-t", - "rsa", - "-N", - "", - "-f", - "/home/" + username + "/.ssh/id_rsa", - } - - // Add the user - cmd := exec.Command("adduser", adduserArgs...) - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - // Create the .ssh folder for said user - cmd = exec.Command("mkdir", "/home/"+username+"/.ssh") - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - // Create authorized_keys file - cmd = exec.Command("touch", "/home/"+username+"/.ssh/authorized_keys") - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - // Put root public key into authorized_keys file - cmd = exec.Command("cp", "/root/.ssh/id_rsa.pub", "/home/"+username+"/.ssh/authorized_keys") - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - // Create the host key-pair for said user - cmd = exec.Command("ssh-keygen", keygenArgs...) - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - // Create document root for said user - cmd = exec.Command("mkdir", "/home/"+username+"/document_root") - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - // Add default index page - f, err := os.OpenFile("/home/"+username+"/document_root/index.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777) - if err != nil { - return err, nil - } - defer f.Close() - - fmt.Fprintln(f, DEFAULT_INDEX) - - /* OWNERSHIP AND FILE PERMISSIONS START */ - cmd = exec.Command("chmod", "700", "/home/"+username+"/.ssh") - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - cmd = exec.Command("chmod", "600", "/home/"+username+"/.ssh/id_rsa") - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - cmd = exec.Command("chmod", "644", "/home/"+username+"/.ssh/id_rsa.pub") - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - cmd = exec.Command("chmod", "644", "/home/"+username+"/.ssh/authorized_keys") - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - cmd = exec.Command("chown", username+":", "/home/"+username+"/.ssh") - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - cmd = exec.Command("chown", username+":", "/home/"+username+"/.ssh/id_rsa") - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - cmd = exec.Command("chown", username+":", "/home/"+username+"/.ssh/id_rsa.pub") - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - cmd = exec.Command("chown", username+":", "/home/"+username+"/.ssh/authorized_keys") - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - cmd = exec.Command("chown", username+":", "/home/"+username+"/document_root") - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - /* OWNERSHIP AND FILE PERMISSIONS END */ - - return nil, nil -} - -func DeleteBundleUser(username string) (error, error) { - var cerr bytes.Buffer - var err error - - // Delete the user and try to remove all files associated - cmd := exec.Command("deluser", "--quiet", username) - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - // Forcefully remove the users home directory if it has not already been done - // (sometimes deluser doesn't do its job even with the flag) - cmd = exec.Command("rm", "-rf", "/home/"+username) - cmd.Stderr = &cerr - - if err = cmd.Run(); err != nil { - return err, errors.New(cerr.String()) - } - - return nil, nil -} From 5e7b10cad7e0352ff047ab7dc87305916a8a3d32 Mon Sep 17 00:00:00 2001 From: George Shaw Date: Fri, 6 Apr 2018 15:38:19 -0500 Subject: [PATCH 2/2] added package github.com/george-e-shaw-iv/nixtools to dep --- Gopkg.lock | 15 +- .../george-e-shaw-iv/nixtools/.github/LICENSE | 21 ++ .../nixtools/.github/README.md | 4 + .../george-e-shaw-iv/nixtools/.gitignore | 16 + .../george-e-shaw-iv/nixtools/nixtools.go | 10 + .../george-e-shaw-iv/nixtools/ssh.go | 215 +++++++++++++ .../george-e-shaw-iv/nixtools/users.go | 284 ++++++++++++++++++ 7 files changed, 563 insertions(+), 2 deletions(-) create mode 100644 vendor/github.com/george-e-shaw-iv/nixtools/.github/LICENSE create mode 100644 vendor/github.com/george-e-shaw-iv/nixtools/.github/README.md create mode 100644 vendor/github.com/george-e-shaw-iv/nixtools/.gitignore create mode 100644 vendor/github.com/george-e-shaw-iv/nixtools/nixtools.go create mode 100644 vendor/github.com/george-e-shaw-iv/nixtools/ssh.go create mode 100644 vendor/github.com/george-e-shaw-iv/nixtools/users.go diff --git a/Gopkg.lock b/Gopkg.lock index 2709ec5..f0edc31 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -13,10 +13,21 @@ revision = "dbeaa9332f19a944acb5736b4456cfcc02140e29" version = "v3.1.0" +[[projects]] + branch = "master" + name = "github.com/george-e-shaw-iv/nixtools" + packages = ["."] + revision = "a3be4bd706b46bc571b4b34981b06089c6c7158d" + [[projects]] branch = "master" name = "golang.org/x/crypto" - packages = ["acme","acme/autocert","bcrypt","blowfish"] + packages = [ + "acme", + "acme/autocert", + "bcrypt", + "blowfish" + ] revision = "2509b142fb2b797aa7587dad548f113b2c0f20ce" [[projects]] @@ -28,6 +39,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "bc99bfe3269f7f4c24a0166bbfe5fd0cafdee6c803274787bff0ac01b8cb4ed6" + inputs-digest = "95bd56d22992e0d2f4bb1959e7b1b38f5c9716c6c643eff6b6da090d29fb1245" solver-name = "gps-cdcl" solver-version = 1 diff --git a/vendor/github.com/george-e-shaw-iv/nixtools/.github/LICENSE b/vendor/github.com/george-e-shaw-iv/nixtools/.github/LICENSE new file mode 100644 index 0000000..6329853 --- /dev/null +++ b/vendor/github.com/george-e-shaw-iv/nixtools/.github/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 George Shaw + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/george-e-shaw-iv/nixtools/.github/README.md b/vendor/github.com/george-e-shaw-iv/nixtools/.github/README.md new file mode 100644 index 0000000..6ece8fb --- /dev/null +++ b/vendor/github.com/george-e-shaw-iv/nixtools/.github/README.md @@ -0,0 +1,4 @@ +# nixtools +A golang toolkit for linux system operations. + +The GoDoc reference for `george-e-shaw-iv/nixtools` is located [here](https://godoc.org/github.com/george-e-shaw-iv/nixtools). \ No newline at end of file diff --git a/vendor/github.com/george-e-shaw-iv/nixtools/.gitignore b/vendor/github.com/george-e-shaw-iv/nixtools/.gitignore new file mode 100644 index 0000000..da2ea25 --- /dev/null +++ b/vendor/github.com/george-e-shaw-iv/nixtools/.gitignore @@ -0,0 +1,16 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib +nixtools + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ +.idea/ diff --git a/vendor/github.com/george-e-shaw-iv/nixtools/nixtools.go b/vendor/github.com/george-e-shaw-iv/nixtools/nixtools.go new file mode 100644 index 0000000..901de06 --- /dev/null +++ b/vendor/github.com/george-e-shaw-iv/nixtools/nixtools.go @@ -0,0 +1,10 @@ +// Package nixtools is a toolkit for various linux +// system operations. +package nixtools + +// Type user is a struct that has functions attached to +// it that require a username to properly carry out. +type User struct { + ID int + Name string +} \ No newline at end of file diff --git a/vendor/github.com/george-e-shaw-iv/nixtools/ssh.go b/vendor/github.com/george-e-shaw-iv/nixtools/ssh.go new file mode 100644 index 0000000..d649a9e --- /dev/null +++ b/vendor/github.com/george-e-shaw-iv/nixtools/ssh.go @@ -0,0 +1,215 @@ +package nixtools + +import ( + "os/exec" + "bytes" + "errors" + "os" + "io/ioutil" + "strings" +) + +// Function InitSSH creates the necessary folders, +// files, and generates a default key-pair for the +// given user. If parameter rootHasAccess is set +// to true then the public key of the root (sudo) user +// will be copied into the authorized_keys file of +// the user. +func (u *User) InitSSH(rootHasAccess bool) (error) { + var stderr bytes.Buffer + + keygenArgs := []string{ + "-t", + "rsa", + "-N", + "", + "-f", + "/home/" + u.Name + "/.ssh/id_rsa", + } + + // Create the .ssh folder for said user + cmd := exec.Command("mkdir", "/home/"+u.Name+"/.ssh") + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + + // Create authorized_keys file + cmd = exec.Command("touch", "/home/"+u.Name+"/.ssh/authorized_keys") + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + + if rootHasAccess { + // Put root public key into authorized_keys file + cmd = exec.Command("cp", "/root/.ssh/id_rsa.pub", "/home/"+u.Name+"/.ssh/authorized_keys") + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + } + + // Create the default key-pair for said user + cmd = exec.Command("ssh-keygen", keygenArgs...) + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + + /* OWNERSHIP AND FILE PERMISSIONS START */ + cmd = exec.Command("chmod", "700", "/home/"+u.Name+"/.ssh") + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + + cmd = exec.Command("chmod", "600", "/home/"+u.Name+"/.ssh/id_rsa") + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + + cmd = exec.Command("chmod", "644", "/home/"+u.Name+"/.ssh/id_rsa.pub") + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + + cmd = exec.Command("chmod", "644", "/home/"+u.Name+"/.ssh/authorized_keys") + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + + cmd = exec.Command("chown", u.Name+":", "/home/"+u.Name+"/.ssh") + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + + cmd = exec.Command("chown", u.Name+":", "/home/"+u.Name+"/.ssh/id_rsa") + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + + cmd = exec.Command("chown", u.Name+":", "/home/"+u.Name+"/.ssh/id_rsa.pub") + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + + cmd = exec.Command("chown", u.Name+":", "/home/"+u.Name+"/.ssh/authorized_keys") + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + /* OWNERSHIP AND FILE PERMISSIONS END */ + + return nil +} + +// Function AddAuthorizedKey adds a new public key to +// a given user's authorized_keys file. +func (u *User) AddAuthorizedKey(key string) error { + f, err := os.OpenFile("/home/"+u.Name+"/.ssh/authorized_keys", os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + return err + } + + if _, err = f.WriteString(key + "\n"); err != nil { + return err + } + + return nil +} + +// Function DeleteAuthorizedKey removes a public key +// that is already in the authorized_keys file of +// a given user. +func (u *User) DeleteAuthorizedKey(key string) error { + old, err := ioutil.ReadFile("/home/" + u.Name + "/.ssh/authorized_keys") + if err != nil { + return err + } + + lines := strings.Split(string(old), "\n") + + for i, line := range lines { + if strings.Contains(line, key) { + lines = append(lines[:i], lines[i+1:]...) + break + } + } + + new := strings.Join(lines, "\n") + + f, err := os.OpenFile("/home/"+u.Name+"/.ssh/authorized_keys", os.O_TRUNC|os.O_WRONLY, 0644) + if err != nil { + return err + } + + if _, err = f.WriteString(new); err != nil { + return err + } + + return nil +} + +// Function GetAuthorizedKeys will return a slice +// of strings that contains all of the public keys +// within a given user's authorized_keys file. +// If the parameter removeRootKey is set to true the +// public key of the current root user of the system, +// if found within the file, will not be placed within +// the slice of strings. +func (u *User) GetAuthorizedKeys(removeRootKey bool) ([]string, error) { + f, err := ioutil.ReadFile("/home/" + u.Name + "/.ssh/authorized_keys") + if err != nil { + return nil, err + } + + // Remove empty strings + raw := strings.Split(string(f), "\n") + var clean []string + + for _, each := range raw { + if each != "" { + clean = append(clean, each) + } + } + + // Remove root key if necessary + if removeRootKey { + if len(clean) == 1 { + return nil, nil + } + + key, err := ioutil.ReadFile("/root/.ssh/id_rsa.pub") + if err != nil { + return nil, err + } + + for k, v := range clean { + if v == string(key) { + clean = append(clean[:k], clean[k+1:]...) + break + } + } + } + + return clean, nil +} \ No newline at end of file diff --git a/vendor/github.com/george-e-shaw-iv/nixtools/users.go b/vendor/github.com/george-e-shaw-iv/nixtools/users.go new file mode 100644 index 0000000..fdfeda2 --- /dev/null +++ b/vendor/github.com/george-e-shaw-iv/nixtools/users.go @@ -0,0 +1,284 @@ +package nixtools + +import ( + "os/exec" + "bytes" + "errors" + "strconv" + "io" + "os" + "strings" +) + +// Function GetUser takes either an ID or Username +// as an identifier and returns a type User associated +// with the given identifier. createIfNotExist parameter +// can only be utilized if the function is given a +// username, not an ID. +func GetUser(identifier interface{}, createIfNotExist bool) (*User, error) { + var u User + var err error + + if id, ok := identifier.(int); ok { + u.ID = id + + u.Name, err = getUsername(id) + if err == nil { + return &u, nil + } + + return nil, err + } else if name, ok := identifier.(string); ok { + u.Name = name + + u.ID, err = getUserID(name) + if err == nil { + return &u, nil + } + + if createIfNotExist { + u.ID, err = createUser(name) + if err != nil { + return nil, err + } + + return &u, nil + } + + return nil, err + } + + return nil, errors.New("identifier must be a valid user id (int) or username (string)") +} + +// Function CreateDirectory attempts to create a directory along +// with all of its parent directories (if needed) relative to the +// user's home directory. The perm parameter is in the format of linux +// file permissions (e.g. 0664). +func (u *User) CreateDirectory(dirPath string, perm os.FileMode) (error) { + return os.MkdirAll("/home/"+u.Name+"/"+dirPath, perm) +} + +// Function WriteFile writes to a file with a given mode that +// is set by flags (flag parameter) that are defined within the +// os package. The filePath is relative to the current user's +// home directory. The perm parameter is in the format of linux +// file permissions (e.g. 0664). +func (u *User) WriteFile(filePath string, flag int, perm os.FileMode, content []byte) (error) { + f, err := os.OpenFile("/home/"+u.Name+"/"+filePath, flag, perm) + if err != nil { + return err + } + defer f.Close() + + n, err := f.Write(content) + if err != nil { + return err + } + + if n != len(content) { + f.Close() + _ = os.Remove("/home/"+u.Name+"/"+filePath) + + return errors.New("full length of content was not able to be written, file was attempted to be deleted") + } + + return nil +} + +// Function DeleteFileOrDirectory attempts to delete a named +// file or directory relative to the user's home directory. +func (u *User) DeleteFileOrDirectory(path string) (error) { + return os.RemoveAll("/home/"+u.Name+"/"+path) +} + +// Function Delete will attempt to delete the given user +// attached to the struct. deleteOwnedFiles parameter +// will remove all files owned by the user and +// removeHome parameter will delete the home directory +// of the user. Warning: parameter deleteOwnedFiles will +// cause the function to take awhile to execute, consider +// using a goroutine to prevent your program from stalling. +func (u *User) Delete(removeHome, deleteOwnedFiles bool) (error) { + var stderr bytes.Buffer + var args = []string{ + "--quiet", + } + + if deleteOwnedFiles { + args = append(args, "--remove-all-files") + } + if removeHome { + args = append(args, "--remove-home") + } + + args = append(args, u.Name) + + // Delete the user and try to remove all files associated + cmd := exec.Command("deluser", args...) + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + + // Forcefully remove the users home directory as sometimes + // the --remove-home or --remove-all-files flags don't work + if deleteOwnedFiles || removeHome { + cmd = exec.Command("rm", "-rf", "/home/"+u.Name) + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + } + + return nil +} + +// Function SetPassword sets the users password +// to the given parameter. +func (u *User) SetPassword(password string) (error) { + return nil +} + +// Function ForcePasswordChange forces a user to change +// their password upon next login. +func (u *User) ForcePasswordChange() (error) { + cmd := exec.Command("chage", "-d", "0", u.Name) + + var stderr bytes.Buffer + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + + return nil +} + +// Function Lock lock's a users account, rendering them +// unable to login through any means of authentication. +func (u *User) Lock() (error) { + cmd := exec.Command("chage", "-E", "0", u.Name) + + var stderr bytes.Buffer + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + + return nil +} + +// Function Unlock will unlock a users account, allowing +// them to authenticate through all previous mediums. +func (u *User) Unlock() (error) { + cmd := exec.Command("chage", "-E", "-1", u.Name) + + var stderr bytes.Buffer + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return errors.New(stderr.String()) + } + + return nil +} + +// Function getUserID returns the ID associated with +// a username if it exists or -1 and an error if the +// given username does not exist. +func getUserID(username string) (int, error) { + cmd := exec.Command("id", "-u", username) + + var stderr, stdout bytes.Buffer + cmd.Stderr = &stderr + cmd.Stdout = &stdout + + if err := cmd.Run(); err != nil { + return -1, errors.New(stderr.String()) + } + + stdoutParsed := strings.Replace(stdout.String(), "\n", "", -1) + + id, err := strconv.Atoi(stdoutParsed) + if err != nil { + return -1, err + } + + return id, nil +} + +// Function getUsername returns the username associated +// with an ID if it exists. +func getUsername(id int) (string, error) { + var stderr, stdout bytes.Buffer + reader, writer := io.Pipe() + + cmdOut := exec.Command("getent", "passwd", strconv.Itoa(id)) + cmdOut.Stdout = writer + + cmdIn := exec.Command("cut", "-d:", "-f1") + cmdIn.Stdin = reader + cmdIn.Stdout = &stdout + cmdIn.Stderr = &stderr + + cmdOut.Start() + cmdIn.Start() + + cmdOut.Wait() + writer.Close() + + cmdIn.Wait() + reader.Close() + + if len(stderr.Bytes()) > 0 { + return "", errors.New(stderr.String()) + } + + return stdout.String(), nil +} + +// Function userExists returns true if the given +// username exists within the system or false if +// the given username does not exist. +func UserExists(username string) (bool) { + cmd := exec.Command("id", "-u", username) + + if err := cmd.Run(); err != nil { + return false + } + + return true +} + +// Function createUser will attempt to create a +// new user in the system with the given username. +// Upon success will return the new ID of the +// created user. +func createUser(username string) (int, error) { + var stderr bytes.Buffer + + args := []string{ + "--disabled-password", + "--gecos", + "", + username, + } + + cmd := exec.Command("adduser", args...) + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return 0, errors.New(stderr.String()) + } + + id, err := getUserID(username) + if err != nil { + return 0, err + } + + return id, nil +} \ No newline at end of file