Add allow and blocked to yaml definitions
Allow to define a Allow and Block list of IP addresses per web hook.
This commit is contained in:
parent
a9cda6f310
commit
dfb7b3603a
|
@ -2,21 +2,25 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
// Tries to lock a spot. Returns false, if the max. number of concurrent runs has been reached
|
||||
|
@ -86,3 +90,69 @@ func (hook *Hook) Run() error {
|
|||
}
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func isAddressInList(addr string, addrList []string) (bool, error) {
|
||||
ip, _, err := net.ParseCIDR(addr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, item := range addrList {
|
||||
iAddr, iNet, err := net.ParseCIDR(item)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if ip.Equal(iAddr) {
|
||||
return true, nil
|
||||
}
|
||||
if iNet.Contains(ip) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Extract only the CIDR address from the given address identifier. This removes the port, if present
|
||||
func cidr(addr string) string {
|
||||
// Check for IPv6 address
|
||||
s, e := strings.Index(addr, "["), strings.Index(addr, "]")
|
||||
if s >= 0 && e > 0 {
|
||||
return addr[s+1:e] + "/128"
|
||||
}
|
||||
// Simply remove the port
|
||||
i := strings.Index(addr, ":")
|
||||
if i > 0 {
|
||||
return addr[:i-1] + "/32"
|
||||
}
|
||||
return addr + "/32"
|
||||
}
|
||||
|
||||
// IsAddressAllowed checks if the hook allows the given address. An address is allowed, if it is present in the AllowAddresses list (if non-empty) and if it is not present in the BlockedAddresses list (if non-empty)
|
||||
func (hook *Hook) IsAddressAllowed(addr string) (bool, error) {
|
||||
addr = cidr(addr)
|
||||
if hook.AllowAddresses != nil && len(hook.AllowAddresses) > 0 {
|
||||
// If AllowAddresses is defined and not empty, the given addr must be in the AllowAddresses list
|
||||
found, err := isAddressInList(addr, hook.AllowAddresses)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// If not present in the list, block the request. Otherwise we still need to pass the BlockedAddresses check
|
||||
if !found {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if hook.BlockedAddresses != nil && len(hook.BlockedAddresses) > 0 {
|
||||
// If BlockedAddresses is defined and not empty, the given addr must not be in the BlockedAddresses list
|
||||
found, err := isAddressInList(addr, hook.BlockedAddresses)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if found {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
@ -112,6 +112,22 @@ func createHandler(hook Hook) Handler {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("GET %s %s", r.RemoteAddr, hook.Name)
|
||||
|
||||
// Check if adresses are allowed or blocked
|
||||
allowed, err := hook.IsAddressAllowed(r.RemoteAddr)
|
||||
if err != nil {
|
||||
log.Printf("ERR: Error checking for address permissions for hook \"%s\": %s", hook.Name, err)
|
||||
w.WriteHeader(500)
|
||||
fmt.Fprintf(w, "{\"status\":\"fail\",\"reason\":\"server error\"}")
|
||||
return
|
||||
}
|
||||
if !allowed {
|
||||
log.Printf("Blocked: '%s' for not allowed remote end %s", hook.Name, r.RemoteAddr)
|
||||
// Return a 403 - Forbidden
|
||||
w.WriteHeader(403)
|
||||
fmt.Fprintf(w, "{\"status\":\"blocked\",\"reason\":\"address not allowed\"}")
|
||||
return
|
||||
}
|
||||
|
||||
// Check for available slots
|
||||
if !hook.TryLock() {
|
||||
log.Printf("ERR: \"%s\" max concurrency reached", hook.Name)
|
||||
|
|
14
weblug.yaml
14
weblug.yaml
|
@ -2,8 +2,8 @@
|
|||
## Weblug example config
|
||||
|
||||
settings:
|
||||
bind: "127.0.0.1:2088" # bind address for webserver
|
||||
#bind: ":2088" # bind to all addresses
|
||||
#bind: "127.0.0.1:2088" # bind address for webserver
|
||||
bind: ":2088" # bind to all addresses
|
||||
|
||||
# hook definition. A hook needs to define the HTTP endpoint ("route") and the
|
||||
# command that will be executed, once this route is executed
|
||||
|
@ -24,3 +24,13 @@ 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"]
|
||||
|
|
Loading…
Reference in a new issue