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:
parent
a4e21288db
commit
46418bcf3a
2
Makefile
2
Makefile
|
@ -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 $^
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
|
|
12
doc/weblug.8
12
doc/weblug.8
|
@ -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
|
||||||
|
|
|
@ -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
30
test/checkenv
Executable 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"
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue