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
all: weblug
.PHONY: test
weblug: cmd/weblug/*.go
go build -o weblug $^

View file

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

View file

@ -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 <felix@feldspaten.org> 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

View file

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

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
gid: 10
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"
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"