@@ -306,9 +326,10 @@
Account Users
-
View, edit, update, and remove users that can access this gPanel Account
+
View, edit, update, and remove users that can access the gPanel Account and utilize SSH.
+
@@ -370,6 +391,8 @@
+
+
diff --git a/pkg/api/ssh/addkey.go b/pkg/api/ssh/addkey.go
new file mode 100644
index 0000000..792cd9b
--- /dev/null
+++ b/pkg/api/ssh/addkey.go
@@ -0,0 +1,39 @@
+package ssh
+
+import (
+ "encoding/json"
+ "log"
+ "net/http"
+ "strconv"
+
+ "github.com/Ennovar/gPanel/pkg/system"
+)
+
+func AddKey(res http.ResponseWriter, req *http.Request, logger *log.Logger) bool {
+ if req.Method != "UPDATE" {
+ logger.Println(req.URL.Path + "::" + req.Method + "::" + strconv.Itoa(http.StatusMethodNotAllowed) + "::" + http.StatusText(http.StatusMethodNotAllowed))
+ http.Error(res, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
+ return false
+ }
+
+ var requestData struct {
+ Username string `json:"username"`
+ PublicKey string `json:"publickey"`
+ }
+
+ err := json.NewDecoder(req.Body).Decode(&requestData)
+ if err != nil {
+ logger.Println(req.URL.Path + "::" + err.Error())
+ http.Error(res, err.Error(), http.StatusBadRequest)
+ return false
+ }
+
+ if err = system.AddAuthorizedKey(requestData.Username, requestData.PublicKey); err != nil {
+ logger.Println(req.URL.Path + "::" + err.Error())
+ http.Error(res, err.Error(), http.StatusInternalServerError)
+ return false
+ }
+
+ res.WriteHeader(http.StatusNoContent)
+ return true
+}
diff --git a/pkg/api/ssh/deletekey.go b/pkg/api/ssh/deletekey.go
new file mode 100644
index 0000000..865278f
--- /dev/null
+++ b/pkg/api/ssh/deletekey.go
@@ -0,0 +1,39 @@
+package ssh
+
+import (
+ "encoding/json"
+ "log"
+ "net/http"
+ "strconv"
+
+ "github.com/Ennovar/gPanel/pkg/system"
+)
+
+func DeleteKey(res http.ResponseWriter, req *http.Request, logger *log.Logger) bool {
+ if req.Method != "UPDATE" {
+ logger.Println(req.URL.Path + "::" + req.Method + "::" + strconv.Itoa(http.StatusMethodNotAllowed) + "::" + http.StatusText(http.StatusMethodNotAllowed))
+ http.Error(res, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
+ return false
+ }
+
+ var requestData struct {
+ Username string `json:"username"`
+ PublicKey string `json:"publickey"`
+ }
+
+ err := json.NewDecoder(req.Body).Decode(&requestData)
+ if err != nil {
+ logger.Println(req.URL.Path + "::" + err.Error())
+ http.Error(res, err.Error(), http.StatusBadRequest)
+ return false
+ }
+
+ if err = system.DeleteAuthorizedKey(requestData.Username, requestData.PublicKey); err != nil {
+ logger.Println(req.URL.Path + "::" + err.Error())
+ http.Error(res, err.Error(), http.StatusInternalServerError)
+ return false
+ }
+
+ res.WriteHeader(http.StatusNoContent)
+ return true
+}
diff --git a/pkg/gpaccount/apihandler.go b/pkg/gpaccount/apihandler.go
index e59fe46..e16218c 100644
--- a/pkg/gpaccount/apihandler.go
+++ b/pkg/gpaccount/apihandler.go
@@ -5,12 +5,13 @@ import (
"net/http"
"strings"
+ "github.com/Ennovar/gPanel/pkg/api/domain"
"github.com/Ennovar/gPanel/pkg/api/ip"
logapi "github.com/Ennovar/gPanel/pkg/api/log"
"github.com/Ennovar/gPanel/pkg/api/server"
- "github.com/Ennovar/gPanel/pkg/api/user"
- "github.com/Ennovar/gPanel/pkg/api/domain"
"github.com/Ennovar/gPanel/pkg/api/settings"
+ "github.com/Ennovar/gPanel/pkg/api/ssh"
+ "github.com/Ennovar/gPanel/pkg/api/user"
)
func (con *Controller) apiHandler(res http.ResponseWriter, req *http.Request) (bool, bool) {
@@ -67,6 +68,10 @@ func (con *Controller) apiHandler(res http.ResponseWriter, req *http.Request) (b
return true, domain.Unlink(res, req, con.APILogger)
case "/settings/get_nameservers":
return true, settings.GetNameservers(res, req, con.APILogger)
+ case "/ssh/addkey":
+ return true, ssh.AddKey(res, req, con.APILogger)
+ case "/ssh/deletekey":
+ return true, ssh.DeleteKey(res, req, con.APILogger)
default:
return false, false
}
diff --git a/pkg/system/keys.go b/pkg/system/keys.go
new file mode 100644
index 0000000..15b1294
--- /dev/null
+++ b/pkg/system/keys.go
@@ -0,0 +1,49 @@
+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); 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
+}
diff --git a/pkg/system/user.go b/pkg/system/user.go
index d2b5b46..9d90518 100644
--- a/pkg/system/user.go
+++ b/pkg/system/user.go
@@ -58,14 +58,6 @@ func CreateBundleUser(username string) (error, error) {
return err, errors.New(cerr.String())
}
- // Correct permissions on authorized_keys file
- cmd = exec.Command("chmod", "600", "/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
@@ -82,16 +74,85 @@ func CreateBundleUser(username string) (error, error) {
return err, errors.New(cerr.String())
}
+ /* 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())
+ }
+ /* OWNERSHIP AND FILE PERMISSIONS END */
+
return nil, nil
}
func DeleteBundleUser(username string) (error, error) {
var cerr bytes.Buffer
+ var err error
- cmd := exec.Command("deluser", "--remove-all-files", username)
+ // 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 {
+ 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())
}