From 80532f6eb8c0d08fae9048129432bfa2373f7a56 Mon Sep 17 00:00:00 2001 From: George Shaw Date: Fri, 6 Apr 2018 15:37:35 -0500 Subject: [PATCH] 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 -}