From 46418bcf3a49dd3b6c309e9db947629fbf4ee922 Mon Sep 17 00:00:00 2001 From: phoenix Date: Sun, 28 May 2023 11:31:02 +0200 Subject: [PATCH] Add env sanitation Sanitize the environment variables when running webhooks and add ability to add custom environment variables per webhook. --- Makefile | 2 ++ cmd/weblug/hook.go | 32 ++++++++++++++++++++------------ doc/weblug.8 | 12 ++++++++++-- test/blackbox.sh | 23 +++++++++++++++-------- test/checkenv | 30 ++++++++++++++++++++++++++++++ test/test.yaml | 7 +++++++ weblug.yml | 3 +++ 7 files changed, 87 insertions(+), 22 deletions(-) create mode 100755 test/checkenv diff --git a/Makefile b/Makefile index 8d51179..90ba232 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ default: all all: weblug +.PHONY: test + weblug: cmd/weblug/*.go go build -o weblug $^ diff --git a/cmd/weblug/hook.go b/cmd/weblug/hook.go index d6de420..3639d4b 100644 --- a/cmd/weblug/hook.go +++ b/cmd/weblug/hook.go @@ -10,18 +10,19 @@ import ( ) type Hook struct { - Name string `yaml:"name"` // name of the hook - Route string `yaml:"route"` // http route - Command string `yaml:"command"` // Actual command to execute - Background bool `yaml:"background"` // Run in background - Concurrency int `yaml:"concurrency"` // Number of allowed concurrent runs - concurrentRuns int32 // Number of current concurrent runs - UID int `yaml:"uid"` // UID to use when running the command - GID int `yaml:"gid"` // GID to use when running the command - Output bool `yaml:"output"` // Print program output - AllowAddresses []string `yaml:"allowed"` // Addresses that are explicitly allowed - BlockedAddresses []string `yaml:"blocked"` // Addresses that are explicitly blocked - HttpBasicAuth BasicAuth `yaml:"basic_auth"` // Optional requires http basic auth + Name string `yaml:"name"` // name of the hook + Route string `yaml:"route"` // http route + Command string `yaml:"command"` // Actual command to execute + Background bool `yaml:"background"` // Run in background + Concurrency int `yaml:"concurrency"` // Number of allowed concurrent runs + concurrentRuns int32 // Number of current concurrent runs + UID int `yaml:"uid"` // UID to use when running the command + GID int `yaml:"gid"` // GID to use when running the command + Output bool `yaml:"output"` // Print program output + AllowAddresses []string `yaml:"allowed"` // Addresses that are explicitly allowed + BlockedAddresses []string `yaml:"blocked"` // Addresses that are explicitly blocked + HttpBasicAuth BasicAuth `yaml:"basic_auth"` // Optional requires http basic auth + Env map[string]string `yaml:"env"` // Optional environment variables } type BasicAuth struct { @@ -89,6 +90,13 @@ func (hook *Hook) Run() error { cmd.SysProcAttr = &syscall.SysProcAttr{} cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(hook.UID), Gid: uint32(hook.GID)} } + cmd.Env = make([]string, 0) + if hook.Env != nil { + // Build environment variable list as expected by cmd.Env + for k, v := range hook.Env { + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v)) + } + } if hook.Output { buf, ret := cmd.Output() fmt.Println(string(buf)) diff --git a/doc/weblug.8 b/doc/weblug.8 index c6e6d30..9f627c6 100644 --- a/doc/weblug.8 +++ b/doc/weblug.8 @@ -1,6 +1,6 @@ ." Manpage for weblug -." Contact felix@feldspaten.org to correct errors and/or typos. -.TH weblug 8 "27 Apr 2023" "1.0" "weblug man page" +." Write me an email if you find errors and/or typos. Thank you! :-) +.TH weblug 8 "28 May 2023" "1.0" "weblug man page" .SH NAME weblug - Simple webhook receiver program @@ -52,6 +52,14 @@ See the following example configuration file: .br .B " concurrency: 2 # At most 2 parallel processes are allowed .br +.B " env: # Define environment variables +.br +.B " KEY1: "VALUE1" +.br +.B " KEY2: "VALUE2" +.br +.br +.br .br .B " - name: 'hook two' .br diff --git a/test/blackbox.sh b/test/blackbox.sh index 577651a..40711e1 100755 --- a/test/blackbox.sh +++ b/test/blackbox.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -ex # Blackbox tests for weblug cleanup() { @@ -14,6 +14,8 @@ if [[ $EUID != 0 && $UID != 0 ]]; then sleep 3 fi +# Secret environment variable, which must be removed by env sanitation. +export SECRET1="top5ecret" rm -f testfile ../weblug test.yaml & @@ -23,7 +25,7 @@ sleep 1 echo -e "\n\nChecking 'testfile' webhook ... " -curl http://127.0.0.1:2088/webhooks/touch +curl --fail http://127.0.0.1:2088/webhooks/touch if [[ ! -f testfile ]]; then echo "Testfile doesn't exist after running webhook touch" exit 1 @@ -34,15 +36,15 @@ rm -f testfile echo -e "\n\nChecking 'background' webhook ... " -timeout 2 curl http://127.0.0.1:2088/webhooks/background +timeout 2 curl --fail http://127.0.0.1:2088/webhooks/background ## Check concurrency webhook, that allows only 2 requests at the same time (but sleeps for 5 seconds) echo -e "\n\nChecking 'concurrency' webhook ... " -timeout 10 curl http://127.0.0.1:2088/3 & -timeout 10 curl http://127.0.0.1:2088/3 & -! timeout 2 curl http://127.0.0.1:2088/3 +timeout 10 curl --fail http://127.0.0.1:2088/3 & +timeout 10 curl --fail http://127.0.0.1:2088/3 & +! timeout 2 curl --fail http://127.0.0.1:2088/3 ## Check UID and GID webhooks, but only if we're root @@ -50,10 +52,15 @@ echo -e "\n\nChecking 'uid/gid' webhook ... " # Skip this test, if we're not root if [[ $EUID == 0 || $UID == 0 ]]; then - curl http://127.0.0.1:2088/webhooks/uid - curl http://127.0.0.1:2088/webhooks/gid + curl --fail http://127.0.0.1:2088/webhooks/uid + curl --fail http://127.0.0.1:2088/webhooks/gid else echo "Cannot UID and GID webhook tests, because we're not running as root" fi +## Check environment variables + + +timeout 10 curl --fail 'http://127.0.0.1:2088/env' + echo -e "\n\nall good" diff --git a/test/checkenv b/test/checkenv new file mode 100755 index 0000000..6799185 --- /dev/null +++ b/test/checkenv @@ -0,0 +1,30 @@ +#!/bin/bash -e +# Script to test for environment variable sanitation + +set -o pipefail + +if [[ $PUBLIC1 != "one" ]]; then + echo "PUBLIC1 variable not valid" + exit 1 +fi +if [[ $PUBLIC2 != "two" ]]; then + echo "PUBLIC2 variable not valid" + exit 1 +fi + +if env | grep 'SECRET1' >/dev/null; then + echo "SECRET1 variable is set but it should not be" + echo "Environment sanitation failed" + exit 1 +fi + +# There must never be more than 10 variables set +# Some variables will be set by bash at startup (e.g. PWD), so we are never in a +# pristine environment. However, more than 10 variables indicates something's off +# with the env sanitation +if [[ `env | wc -l` -ge 10 ]]; then + echo "More than 10 env variables detected" + exit 1 +fi + +echo "all good" \ No newline at end of file diff --git a/test/test.yaml b/test/test.yaml index 7abad4d..71fe6ff 100644 --- a/test/test.yaml +++ b/test/test.yaml @@ -25,3 +25,10 @@ hooks: uid: 10 gid: 10 output: True + - name: 'environment variables' + route: '/env' + command: "bash ./checkenv" + output: True + env: + PUBLIC1: "one" + PUBLIC2: "two" diff --git a/weblug.yml b/weblug.yml index 68fd2ea..f626518 100644 --- a/weblug.yml +++ b/weblug.yml @@ -13,6 +13,9 @@ hooks: command: "sleep 5" background: True # Terminate http request immediately concurrency: 2 # At most 2 parallel processes are allowed + env: # Define environment variables + KEY1: "VALUE1" + KEY2: "VALUE2" - name: 'hook two' route: "/webhooks/2"