diff --git a/_docs/unit.md b/_docs/unit.md index 15b7e07..0012dcd 100644 --- a/_docs/unit.md +++ b/_docs/unit.md @@ -6,6 +6,7 @@ Schema: * `command` (required, string) - command to execute (will be executed in a shell) +* `user` (optional, string) - custom user as process owner (only `bin` mode and only for linux), usually requires root privileges * `interval` (optional, interval) - interval between attempts * `timeout` (optional, interval) - maximum execution timeout (enabled only for bin mode and only if positive) * `graceful_timeout` (optional, interval) - maximum execution timeout after which SIGINT will be sent (enabled only for bin mode and only if positive). diff --git a/server/mode_bin.go b/server/mode_bin.go index 8e8f100..5e8cef6 100644 --- a/server/mode_bin.go +++ b/server/mode_bin.go @@ -34,6 +34,7 @@ func (m *markerResponse) WriteHeader(statusCode int) { } type binHandler struct { + user string command string workDir string shell string @@ -70,8 +71,13 @@ func (bh *binHandler) ServeHTTP(writer http.ResponseWriter, request *http.Reques cmd.Stdout = marker cmd.Env = env internal.SetBinFlags(cmd) - - err := bh.run(ctx, cmd) + err := setUser(cmd, bh.user) + if err != nil { + writer.Header().Set("X-Return-Code", strconv.Itoa(cmd.ProcessState.ExitCode())) + writer.WriteHeader(http.StatusInternalServerError) + return + } + err = bh.run(ctx, cmd) if codeReset, ok := writer.(interface{ Status(status int) }); ok && err != nil { codeReset.Status(http.StatusBadGateway) diff --git a/server/mode_bin_default.go b/server/mode_bin_default.go new file mode 100644 index 0000000..f309de3 --- /dev/null +++ b/server/mode_bin_default.go @@ -0,0 +1,9 @@ +//+build !linux + +package server + +import "os/exec" + +func setUser(cmd *exec.Cmd, user string) error { + return nil +} diff --git a/server/mode_bin_linux.go b/server/mode_bin_linux.go new file mode 100644 index 0000000..e198d32 --- /dev/null +++ b/server/mode_bin_linux.go @@ -0,0 +1,35 @@ +package server + +import ( + "os/exec" + "os/user" + "strconv" + "syscall" +) + +func setUser(cmd *exec.Cmd, userName string) error { + if userName == "" { + return nil + } + info, err := user.Lookup(userName) + if err != nil { + return err + } + uid, err := strconv.Atoi(info.Uid) + if err != nil { + return err + } + gid, err := strconv.Atoi(info.Gid) + if err != nil { + return err + } + + if cmd.SysProcAttr == nil { + cmd.SysProcAttr = &syscall.SysProcAttr{} + } + cmd.SysProcAttr.Credential = &syscall.Credential{ + Uid: uint32(uid), + Gid: uint32(gid), + } + return nil +} diff --git a/server/unit.go b/server/unit.go index f6eaa09..49c54b5 100644 --- a/server/unit.go +++ b/server/unit.go @@ -34,6 +34,7 @@ type Unit struct { Mode string `yaml:"mode,omitempty"` // execution mode: bin, cgi or proxy WorkDir string `yaml:"workdir,omitempty"` // working directory for the worker. if empty - temporary one will generated automatically Command string `yaml:"command"` // command in a shell to execute + User string `yaml:"user,omitempty"` // user name as process owner (only for bin mode) Timeout time.Duration `yaml:"timeout,omitempty"` // maximum execution timeout (enabled only for bin mode and only if positive) GracefulTimeout time.Duration `yaml:"graceful_timeout,omitempty"` // maximum execution timeout after which SIGINT will be sent (enabled only for bin mode and only if positive) Shell string `yaml:"shell,omitempty"` // shell to execute command in bin mode (default - /bin/sh) @@ -258,6 +259,7 @@ func (cfg Unit) createRunner() (http.Handler, error) { switch cfg.Mode { case "bin": return &binHandler{ + user: cfg.User, command: cfg.Command, workDir: cfg.WorkDir, shell: cfg.Shell,