Add env sanitation

Sanitize the environment variables when running webhooks and add ability
to add custom environment variables per webhook.
This commit is contained in:
Felix Niederwanger 2023-05-28 11:31:02 +02:00
parent a4e21288db
commit 46418bcf3a
Signed by: phoenix
GPG key ID: 31860289A704FB3C
7 changed files with 87 additions and 22 deletions

View file

@ -1,6 +1,8 @@
default: all default: all
all: weblug all: weblug
.PHONY: test
weblug: cmd/weblug/*.go weblug: cmd/weblug/*.go
go build -o weblug $^ go build -o weblug $^

View file

@ -10,18 +10,19 @@ import (
) )
type Hook struct { type Hook struct {
Name string `yaml:"name"` // name of the hook Name string `yaml:"name"` // name of the hook
Route string `yaml:"route"` // http route Route string `yaml:"route"` // http route
Command string `yaml:"command"` // Actual command to execute Command string `yaml:"command"` // Actual command to execute
Background bool `yaml:"background"` // Run in background Background bool `yaml:"background"` // Run in background
Concurrency int `yaml:"concurrency"` // Number of allowed concurrent runs Concurrency int `yaml:"concurrency"` // Number of allowed concurrent runs
concurrentRuns int32 // Number of current concurrent runs concurrentRuns int32 // Number of current concurrent runs
UID int `yaml:"uid"` // UID to use when running the command UID int `yaml:"uid"` // UID to use when running the command
GID int `yaml:"gid"` // GID to use when running the command GID int `yaml:"gid"` // GID to use when running the command
Output bool `yaml:"output"` // Print program output Output bool `yaml:"output"` // Print program output
AllowAddresses []string `yaml:"allowed"` // Addresses that are explicitly allowed AllowAddresses []string `yaml:"allowed"` // Addresses that are explicitly allowed
BlockedAddresses []string `yaml:"blocked"` // Addresses that are explicitly blocked BlockedAddresses []string `yaml:"blocked"` // Addresses that are explicitly blocked
HttpBasicAuth BasicAuth `yaml:"basic_auth"` // Optional requires http basic auth HttpBasicAuth BasicAuth `yaml:"basic_auth"` // Optional requires http basic auth
Env map[string]string `yaml:"env"` // Optional environment variables
} }
type BasicAuth struct { type BasicAuth struct {
@ -89,6 +90,13 @@ func (hook *Hook) Run() error {
cmd.SysProcAttr = &syscall.SysProcAttr{} cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(hook.UID), Gid: uint32(hook.GID)} 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 { if hook.Output {
buf, ret := cmd.Output() buf, ret := cmd.Output()
fmt.Println(string(buf)) fmt.Println(string(buf))

View file

@ -1,6 +1,6 @@
." Manpage for weblug ." Manpage for weblug
." Contact felix@feldspaten.org to correct errors and/or typos. ." Write me an email <felix@feldspaten.org> if you find errors and/or typos. Thank you! :-)
.TH weblug 8 "27 Apr 2023" "1.0" "weblug man page" .TH weblug 8 "28 May 2023" "1.0" "weblug man page"
.SH NAME .SH NAME
weblug - Simple webhook receiver program weblug - Simple webhook receiver program
@ -52,6 +52,14 @@ See the following example configuration file:
.br .br
.B " concurrency: 2 # At most 2 parallel processes are allowed .B " concurrency: 2 # At most 2 parallel processes are allowed
.br .br
.B " env: # Define environment variables
.br
.B " KEY1: "VALUE1"
.br
.B " KEY2: "VALUE2"
.br
.br
.br
.br .br
.B " - name: 'hook two' .B " - name: 'hook two'
.br .br

View file

@ -1,4 +1,4 @@
#!/bin/bash #!/bin/bash -ex
# Blackbox tests for weblug # Blackbox tests for weblug
cleanup() { cleanup() {
@ -14,6 +14,8 @@ if [[ $EUID != 0 && $UID != 0 ]]; then
sleep 3 sleep 3
fi fi
# Secret environment variable, which must be removed by env sanitation.
export SECRET1="top5ecret"
rm -f testfile rm -f testfile
../weblug test.yaml & ../weblug test.yaml &
@ -23,7 +25,7 @@ sleep 1
echo -e "\n\nChecking 'testfile' webhook ... " 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 if [[ ! -f testfile ]]; then
echo "Testfile doesn't exist after running webhook touch" echo "Testfile doesn't exist after running webhook touch"
exit 1 exit 1
@ -34,15 +36,15 @@ rm -f testfile
echo -e "\n\nChecking 'background' webhook ... " 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) ## Check concurrency webhook, that allows only 2 requests at the same time (but sleeps for 5 seconds)
echo -e "\n\nChecking 'concurrency' webhook ... " echo -e "\n\nChecking 'concurrency' webhook ... "
timeout 10 curl http://127.0.0.1:2088/3 & timeout 10 curl --fail http://127.0.0.1:2088/3 &
timeout 10 curl http://127.0.0.1:2088/3 & timeout 10 curl --fail http://127.0.0.1:2088/3 &
! timeout 2 curl 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 ## 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 # Skip this test, if we're not root
if [[ $EUID == 0 || $UID == 0 ]]; then if [[ $EUID == 0 || $UID == 0 ]]; then
curl http://127.0.0.1:2088/webhooks/uid curl --fail 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/gid
else else
echo "Cannot UID and GID webhook tests, because we're not running as root" echo "Cannot UID and GID webhook tests, because we're not running as root"
fi fi
## Check environment variables
timeout 10 curl --fail 'http://127.0.0.1:2088/env'
echo -e "\n\nall good" echo -e "\n\nall good"

30
test/checkenv Executable file
View file

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

View file

@ -25,3 +25,10 @@ hooks:
uid: 10 uid: 10
gid: 10 gid: 10
output: True output: True
- name: 'environment variables'
route: '/env'
command: "bash ./checkenv"
output: True
env:
PUBLIC1: "one"
PUBLIC2: "two"

View file

@ -13,6 +13,9 @@ hooks:
command: "sleep 5" command: "sleep 5"
background: True # Terminate http request immediately background: True # Terminate http request immediately
concurrency: 2 # At most 2 parallel processes are allowed concurrency: 2 # At most 2 parallel processes are allowed
env: # Define environment variables
KEY1: "VALUE1"
KEY2: "VALUE2"
- name: 'hook two' - name: 'hook two'
route: "/webhooks/2" route: "/webhooks/2"