mirror of
https://github.com/adnanh/webhook.git
synced 2025-08-03 00:10:32 +00:00
Add IP whitelist match rule.
This commit is contained in:
parent
c8a83349d2
commit
173273b466
3 changed files with 115 additions and 52 deletions
73
hook/hook.go
73
hook/hook.go
|
@ -8,6 +8,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/textproto"
|
||||
"reflect"
|
||||
"regexp"
|
||||
|
@ -100,6 +101,48 @@ func CheckPayloadSignature(payload []byte, secret string, signature string) (str
|
|||
return expectedMAC, err
|
||||
}
|
||||
|
||||
// CheckIPWhitelist makes sure the provided remote address (of the form IP:port) falls within the provided IP range
|
||||
// (in CIDR form or a single IP address).
|
||||
func CheckIPWhitelist(remoteAddr string, ipRange string) (bool, error) {
|
||||
// Extract IP address from remote address.
|
||||
|
||||
ip := remoteAddr
|
||||
|
||||
if strings.LastIndex(remoteAddr, ":") != -1 {
|
||||
ip = remoteAddr[0:strings.LastIndex(remoteAddr, ":")]
|
||||
}
|
||||
|
||||
ip = strings.TrimSpace(ip)
|
||||
|
||||
// IPv6 addresses will likely be surrounded by [], so don't forget to remove those.
|
||||
|
||||
if strings.HasPrefix(ip, "[") && strings.HasSuffix(ip, "]") {
|
||||
ip = ip[1 : len(ip)-1]
|
||||
}
|
||||
|
||||
parsedIP := net.ParseIP(strings.TrimSpace(ip))
|
||||
|
||||
if parsedIP == nil {
|
||||
return false, fmt.Errorf("invalid IP address found in remote address '%s'", remoteAddr)
|
||||
}
|
||||
|
||||
// Extract IP range in CIDR form. If a single IP address is provided, turn it into CIDR form.
|
||||
|
||||
ipRange = strings.TrimSpace(ipRange)
|
||||
|
||||
if strings.Index(ipRange, "/") == -1 {
|
||||
ipRange = ipRange + "/32"
|
||||
}
|
||||
|
||||
_, cidr, err := net.ParseCIDR(ipRange)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return cidr.Contains(parsedIP), nil
|
||||
}
|
||||
|
||||
// ReplaceParameter replaces parameter value with the passed value in the passed map
|
||||
// (please note you should pass pointer to the map, because we're modifying it)
|
||||
// based on the passed string
|
||||
|
@ -479,16 +522,16 @@ type Rules struct {
|
|||
|
||||
// Evaluate finds the first rule property that is not nil and returns the value
|
||||
// it evaluates to
|
||||
func (r Rules) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
|
||||
func (r Rules) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
|
||||
switch {
|
||||
case r.And != nil:
|
||||
return r.And.Evaluate(headers, query, payload, body)
|
||||
return r.And.Evaluate(headers, query, payload, body, remoteAddr)
|
||||
case r.Or != nil:
|
||||
return r.Or.Evaluate(headers, query, payload, body)
|
||||
return r.Or.Evaluate(headers, query, payload, body, remoteAddr)
|
||||
case r.Not != nil:
|
||||
return r.Not.Evaluate(headers, query, payload, body)
|
||||
return r.Not.Evaluate(headers, query, payload, body, remoteAddr)
|
||||
case r.Match != nil:
|
||||
return r.Match.Evaluate(headers, query, payload, body)
|
||||
return r.Match.Evaluate(headers, query, payload, body, remoteAddr)
|
||||
}
|
||||
|
||||
return false, nil
|
||||
|
@ -498,11 +541,11 @@ func (r Rules) Evaluate(headers, query, payload *map[string]interface{}, body *[
|
|||
type AndRule []Rules
|
||||
|
||||
// Evaluate AndRule will return true if and only if all of ChildRules evaluate to true
|
||||
func (r AndRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
|
||||
func (r AndRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
|
||||
res := true
|
||||
|
||||
for _, v := range r {
|
||||
rv, err := v.Evaluate(headers, query, payload, body)
|
||||
rv, err := v.Evaluate(headers, query, payload, body, remoteAddr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -520,11 +563,11 @@ func (r AndRule) Evaluate(headers, query, payload *map[string]interface{}, body
|
|||
type OrRule []Rules
|
||||
|
||||
// Evaluate OrRule will return true if any of ChildRules evaluate to true
|
||||
func (r OrRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
|
||||
func (r OrRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
|
||||
res := false
|
||||
|
||||
for _, v := range r {
|
||||
rv, err := v.Evaluate(headers, query, payload, body)
|
||||
rv, err := v.Evaluate(headers, query, payload, body, remoteAddr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -542,8 +585,8 @@ func (r OrRule) Evaluate(headers, query, payload *map[string]interface{}, body *
|
|||
type NotRule Rules
|
||||
|
||||
// Evaluate NotRule will return true if and only if ChildRule evaluates to false
|
||||
func (r NotRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
|
||||
rv, err := Rules(r).Evaluate(headers, query, payload, body)
|
||||
func (r NotRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
|
||||
rv, err := Rules(r).Evaluate(headers, query, payload, body, remoteAddr)
|
||||
return !rv, err
|
||||
}
|
||||
|
||||
|
@ -554,6 +597,7 @@ type MatchRule struct {
|
|||
Secret string `json:"secret,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
Parameter Argument `json:"parameter,omitempty"`
|
||||
IPRange string `json:"ip-range,omitempty"`
|
||||
}
|
||||
|
||||
// Constants for the MatchRule type
|
||||
|
@ -561,10 +605,15 @@ const (
|
|||
MatchValue string = "value"
|
||||
MatchRegex string = "regex"
|
||||
MatchHashSHA1 string = "payload-hash-sha1"
|
||||
IPWhitelist string = "ip-whitelist"
|
||||
)
|
||||
|
||||
// Evaluate MatchRule will return based on the type
|
||||
func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
|
||||
func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
|
||||
if r.Type == IPWhitelist {
|
||||
return CheckIPWhitelist(remoteAddr, r.IPRange)
|
||||
}
|
||||
|
||||
if arg, ok := r.Parameter.Get(headers, query, payload); ok {
|
||||
switch r.Type {
|
||||
case MatchValue:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue