Add http basic auth

Adds the `basic_auth` section in a webhook that allows to define a
required username/password for being able to access a given webhook.

Signed-off-by: phoenix <felix@feldspaten.org>
This commit is contained in:
Felix Niederwanger 2023-04-22 13:39:10 +02:00
parent 213135f6d7
commit a6e644d238
Signed by: phoenix
GPG key ID: 6E77A590E3F6D71C
3 changed files with 51 additions and 11 deletions

View file

@ -10,17 +10,23 @@ 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
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
}
type BasicAuth struct {
Username string `yaml:"username"` // Optional: Required username for the webhook to be allowed. If empty, any username will be accepted
Password string `yaml:"password"` // If set, the http basic auth is enabled and the request must contain this password for being allowed
}
// Tries to lock a spot. Returns false, if the max. number of concurrent runs has been reached

View file

@ -128,6 +128,25 @@ func createHandler(hook Hook) Handler {
return
}
// Check for basic auth, if enabled
if hook.HttpBasicAuth.Password != "" {
username, password, ok := r.BasicAuth()
if !ok {
// Return a 403 - Forbidden
w.WriteHeader(401)
fmt.Fprintf(w, "{\"status\":\"unauthorized\",\"message\":\"webhook requires authentication\"}")
return
}
if hook.HttpBasicAuth.Password != password || (hook.HttpBasicAuth.Username != "" && hook.HttpBasicAuth.Username != username) {
// Return a 403 - Forbidden
w.WriteHeader(403)
fmt.Fprintf(w, "{\"status\":\"blocked\",\"reason\":\"not authenticated\"}")
return
}
}
// Check for available slots
if !hook.TryLock() {
log.Printf("ERR: \"%s\" max concurrency reached", hook.Name)

View file

@ -13,10 +13,12 @@ hooks:
command: "sleep 5"
background: True # Terminate http request immediately
concurrency: 2 # At most 2 parallel processes are allowed
- name: 'hook two'
route: "/webhooks/2"
command: "bash -c 'sleep 5'"
concurrency: 5 # At most 5 parallel processes are allowed
- name: 'hook 3'
route: "/webhooks/data/3"
command: "bash -c 'echo $UID $GID'"
@ -24,13 +26,26 @@ hooks:
gid: 200 # Run command with system group id (gid) 200
concurrency: 1 # No concurrency. Returns 500 on parallel requests
output: True # Print program output to console
- name: 'hook 4'
route: "/webhooks/restricted/4"
command: "true"
# Allow only requests from localhost
allowed: ["127.0.0.1/8", "::1/128"]
- name: 'hook 5'
route: "/webhooks/restricted/5"
command: "true"
# Allow everything, except those two subnets
blocked: ["192.168.0.0/16", "10.0.0.0/8"]
- name: 'hook auth'
route: "/webhooks/restricted/auth"
command: "true"
# Require basic auth for this weebhook
basic_auth:
# Username is optional. If defined, the following username must match
# If not defined, any user will be accepted
username: 'user'
# Password is obligatory to enable basic_auth. If defined, a request must authenticate with the given password (cleartext)
password: 'password'