From af3ceffdacf9e51c24d0d10e6f7921e267b69f7c Mon Sep 17 00:00:00 2001 From: Stefan Schubert <22217677+malaupa@users.noreply.github.com> Date: Wed, 5 Jul 2023 19:50:07 +0200 Subject: [PATCH] add run-as to run hook in other user context --- docs/Hook-Definition.md | 1 + internal/hook/hook.go | 1 + setuser.go | 32 ++++++++++++++++++++++++++++++++ setuser_windows.go | 10 ++++++++++ webhook.go | 3 +++ 5 files changed, 47 insertions(+) create mode 100644 setuser.go create mode 100644 setuser_windows.go diff --git a/docs/Hook-Definition.md b/docs/Hook-Definition.md index 8a7e7443..d2754c12 100644 --- a/docs/Hook-Definition.md +++ b/docs/Hook-Definition.md @@ -6,6 +6,7 @@ Hooks are defined as objects in the JSON or YAML hooks configuration file. Pleas * `id` - specifies the ID of your hook. This value is used to create the HTTP endpoint (http://yourserver:port/hooks/your-hook-id) * `execute-command` - specifies the command that should be executed when the hook is triggered + * `run-as` - specifies a different user to run the command with * `command-working-directory` - specifies the working directory that will be used for the script when it's executed * `response-message` - specifies the string that will be returned to the hook initiator * `response-headers` - specifies the list of headers in format `{"name": "X-Example-Header", "value": "it works"}` that will be returned in HTTP response for the hook diff --git a/internal/hook/hook.go b/internal/hook/hook.go index 05100957..4e343a76 100644 --- a/internal/hook/hook.go +++ b/internal/hook/hook.go @@ -566,6 +566,7 @@ func (h *HooksFiles) Set(value string) error { type Hook struct { ID string `json:"id,omitempty"` ExecuteCommand string `json:"execute-command,omitempty"` + RunAs string `json:"run-as,omitempty"` CommandWorkingDirectory string `json:"command-working-directory,omitempty"` ResponseMessage string `json:"response-message,omitempty"` ResponseHeaders ResponseHeaders `json:"response-headers,omitempty"` diff --git a/setuser.go b/setuser.go new file mode 100644 index 00000000..f6479f07 --- /dev/null +++ b/setuser.go @@ -0,0 +1,32 @@ +//go:build !windows +// +build !windows + +package main + +import ( + "log" + "os/exec" + "os/user" + "strconv" + "syscall" +) + +// sets user for the command to execute +func setUser(cmd *exec.Cmd, username string) { + user, err := user.Lookup(username) + if err != nil { + log.Printf("[%s] error lookup user: %s\n", username, err) + return + } + uid, err := strconv.ParseUint(user.Uid, 10, 32) + if err != nil { + log.Printf("Uid [%s] is not an decimal value: %s\n", user.Uid, err) + return + } + gid, err := strconv.ParseUint(user.Gid, 10, 32) + if err != nil { + log.Printf("Uid [%s] is not an decimal value: %s\n", user.Uid, err) + return + } + cmd.SysProcAttr = &syscall.SysProcAttr{Credential: &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}} +} diff --git a/setuser_windows.go b/setuser_windows.go new file mode 100644 index 00000000..1f50775f --- /dev/null +++ b/setuser_windows.go @@ -0,0 +1,10 @@ +//go:build windows +// +build windows + +package main + +import "os/exec" + +func setUser(cmd *exec.Cmd, username string) { + // NOOP: Windows doesn't have setuid setgid equivalent to the Unix world. +} diff --git a/webhook.go b/webhook.go index d23cd028..8af90b5d 100644 --- a/webhook.go +++ b/webhook.go @@ -575,6 +575,9 @@ func handleHook(h *hook.Hook, r *hook.Request) (string, error) { } cmd := exec.Command(cmdPath) + if h.RunAs != "" { + setUser(cmd, h.RunAs) + } cmd.Dir = h.CommandWorkingDirectory cmd.Args, errors = h.ExtractCommandArguments(r)