From 121ab40b022f258f8ef5f23d7055099a62a47636 Mon Sep 17 00:00:00 2001 From: phoenix Date: Mon, 5 Jun 2023 17:00:30 +0200 Subject: [PATCH] Add possibility for priviledge drop Adds the `uid` and `gid` settings in the main configuration file, which allows weblug to run under a unprivileged user account. This comes with the limitation, that unless the program runs as root, custom webhook `uid/gid` settings are not possible. --- cmd/weblug/config.go | 2 ++ cmd/weblug/weblug.go | 20 +++++++++++++++++++- doc/weblug.8 | 4 ++++ test/blackbox.sh | 10 +++++++++- test/test_uid.yaml | 11 +++++++++++ weblug.yml | 4 ++++ 6 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 test/test_uid.yaml diff --git a/cmd/weblug/config.go b/cmd/weblug/config.go index 84d2bb9..190eab2 100644 --- a/cmd/weblug/config.go +++ b/cmd/weblug/config.go @@ -14,6 +14,8 @@ type Config struct { type ConfigSettings struct { BindAddress string `yaml:"bind"` // Bind address for the webserver + UID int `yaml:"uid"` // Custom user ID or 0, if not being used + GID int `yaml:"gid"` // Custom group ID or 0, if not being used } func (cf *Config) SetDefaults() { diff --git a/cmd/weblug/weblug.go b/cmd/weblug/weblug.go index 6dd64d7..3dba720 100644 --- a/cmd/weblug/weblug.go +++ b/cmd/weblug/weblug.go @@ -38,7 +38,11 @@ func awaitTerminationSignal() { // Perform sanity check on hooks func sanityCheckHooks(hooks []Hook) error { - uid := os.Getuid() + // Check UID and GID settings. When hooks have their own UID and GID settings, we need the main program to run as root (required for setgid/setuid) + uid := cf.Settings.UID + if uid == 0 { + uid = os.Getuid() + } for _, hook := range hooks { // If a hook sets a custom uid or gid, ensure we're running as root, otherwise print a warning if hook.UID != 0 && uid != 0 { @@ -81,6 +85,20 @@ func main() { os.Exit(3) } + // Drop privileges? + if cf.Settings.GID != 0 { + if err := syscall.Setgid(cf.Settings.GID); err != nil { + fmt.Fprintf(os.Stderr, "setgid failed: %s\n", err) + os.Exit(1) + } + } + if cf.Settings.UID != 0 { + if err := syscall.Setuid(cf.Settings.UID); err != nil { + fmt.Fprintf(os.Stderr, "setuid failed: %s\n", err) + os.Exit(1) + } + } + // Create default handlers http.HandleFunc("/health", createHealthHandler()) http.HandleFunc("/health.json", createHealthHandler()) diff --git a/doc/weblug.8 b/doc/weblug.8 index 20c899a..36d950e 100644 --- a/doc/weblug.8 +++ b/doc/weblug.8 @@ -46,6 +46,10 @@ See the following example configuration file: .br .B " bind: ":2088" # bind to all addresses .br +.B " uid: 0 # run under specified user id +.br +.B " gid: 0 # run under specified group id +.br .B "# hook definitions. A hook needs to define the HTTP endpoint ("route") and the command .br .B "# See the following examples for more possible options. diff --git a/test/blackbox.sh b/test/blackbox.sh index 40711e1..1a7beb1 100755 --- a/test/blackbox.sh +++ b/test/blackbox.sh @@ -19,6 +19,7 @@ export SECRET1="top5ecret" rm -f testfile ../weblug test.yaml & +../weblug test_uid.yaml & sleep 1 ## Check touch webhook, which creates "testfile" @@ -60,7 +61,14 @@ fi ## Check environment variables - timeout 10 curl --fail 'http://127.0.0.1:2088/env' + +## Check UID/GID handling + +# Ensure weblug is running with UID=65534 +pgrep -u 65534 weblug +# Ensure weblug2 (test_uid) is actually running +timeout 10 curl --fail 'http://127.0.0.1:2089/uid' + echo -e "\n\nall good" diff --git a/test/test_uid.yaml b/test/test_uid.yaml new file mode 100644 index 0000000..1b210a2 --- /dev/null +++ b/test/test_uid.yaml @@ -0,0 +1,11 @@ +--- +settings: + bind: "127.0.0.1:2089" # bind address for webserver + uid: 65534 + gid: 65534 + +hooks: + - name: 'uid hook' + route: "/uid" + command: "id -u" + output: True \ No newline at end of file diff --git a/weblug.yml b/weblug.yml index f626518..7d788c5 100644 --- a/weblug.yml +++ b/weblug.yml @@ -4,6 +4,10 @@ settings: #bind: "127.0.0.1:2088" # bind address for webserver bind: ":2088" # bind to all addresses + # Note: Due to current limitations, weblug needs to run as root when you use custom uid,gid settings per webhook + # This is a known issue, see https://codeberg.org/grisu48/weblug/issues/9 + uid: 0 # run under specified user id + gid: 0 # run under specified group id # hook definitions. A hook needs to define the HTTP endpoint ("route") and the command # See the following examples for more possible options.