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.
This commit is contained in:
Felix Niederwanger 2023-06-05 17:00:30 +02:00
parent 1bdcbd7904
commit 121ab40b02
Signed by: phoenix
GPG key ID: 31860289A704FB3C
6 changed files with 49 additions and 2 deletions

View file

@ -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() {

View file

@ -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())

View file

@ -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.

View file

@ -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"

11
test/test_uid.yaml Normal file
View file

@ -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

View file

@ -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.