Merge eaa5796aad
into 9c7f8c1ac4
This commit is contained in:
commit
cf0fe44c6b
@ -257,6 +257,56 @@ Values in the request body can be accessed in the command or to the match rule b
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Icoming Drone.io hook
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "redeploy-webhook",
|
||||||
|
"execute-command": "/home/adnan/deploy-go-webhook.sh",
|
||||||
|
"command-working-directory": "/home/adnan/go",
|
||||||
|
"response-message": "Executing deploy script",
|
||||||
|
"trigger-rule":
|
||||||
|
{
|
||||||
|
"and": [
|
||||||
|
{
|
||||||
|
"match":
|
||||||
|
{
|
||||||
|
"type": "value",
|
||||||
|
"value": "build",
|
||||||
|
"parameter": {
|
||||||
|
"source": "header",
|
||||||
|
"name": "X-Drone-Event"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"match":
|
||||||
|
{
|
||||||
|
"type": "value",
|
||||||
|
"value": "success",
|
||||||
|
"parameter": {
|
||||||
|
"source": "payload",
|
||||||
|
"name": "build.status"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"match":
|
||||||
|
{
|
||||||
|
"type": "payload-hmac-sha256",
|
||||||
|
"secret": "600a2774d248847509ba27482330d513",
|
||||||
|
"parameter": {
|
||||||
|
"source": "header",
|
||||||
|
"name": "Signature"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
## A simple webhook with a secret key in GET query
|
## A simple webhook with a secret key in GET query
|
||||||
|
|
||||||
__Not recommended in production due to low security__
|
__Not recommended in production due to low security__
|
||||||
|
1
go.mod
1
go.mod
@ -3,6 +3,7 @@ module github.com/adnanh/webhook
|
|||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e
|
||||||
github.com/clbanning/mxj v1.8.4
|
github.com/clbanning/mxj v1.8.4
|
||||||
github.com/dustin/go-humanize v1.0.0
|
github.com/dustin/go-humanize v1.0.0
|
||||||
github.com/fsnotify/fsnotify v1.4.7 // indirect
|
github.com/fsnotify/fsnotify v1.4.7 // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -1,3 +1,5 @@
|
|||||||
|
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e h1:rl2Aq4ZODqTDkeSqQBy+fzpZPamacO1Srp8zq7jf2Sc=
|
||||||
|
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e/go.mod h1:Xa6lInWHNQnuWoF0YPSsx+INFA9qk7/7pTjwb3PInkY=
|
||||||
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
|
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
|
||||||
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
||||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||||
|
@ -25,8 +25,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
|
"github.com/99designs/httpsignatures-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Constants used to specify the parameter source
|
// Constants used to specify the parameter source
|
||||||
@ -212,6 +214,41 @@ func CheckPayloadSignature512(payload []byte, secret string, signature string) (
|
|||||||
return ValidateMAC(payload, hmac.New(sha512.New, []byte(secret)), signatures)
|
return ValidateMAC(payload, hmac.New(sha512.New, []byte(secret)), signatures)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CheckHmacSHA256(headers map[string]interface{}, body []byte, signingKey string) (bool, error) {
|
||||||
|
// Check headers for relevant parts
|
||||||
|
if _,ok := headers["Signature"]; !ok {
|
||||||
|
return false, errors.New("Missing Signature header")
|
||||||
|
}
|
||||||
|
if _,ok := headers["Digest"]; !ok {
|
||||||
|
return false, errors.New("Missing Digest header")
|
||||||
|
}
|
||||||
|
if _,ok := headers["Date"]; !ok {
|
||||||
|
return false, errors.New("Missing Date header")
|
||||||
|
}
|
||||||
|
if signingKey == "" {
|
||||||
|
return false, errors.New("Secret key is required and cannot be empty")
|
||||||
|
}
|
||||||
|
headerSignature := headers["Signature"].(string)
|
||||||
|
headerDate := headers["Date"].(string)
|
||||||
|
headerDigest := headers["Digest"].(string)
|
||||||
|
|
||||||
|
tmpHttpRequest := &http.Request{
|
||||||
|
Header: http.Header{
|
||||||
|
"Date": []string{headerDate},
|
||||||
|
"Digest": []string{headerDigest},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
sig,err := httpsignatures.FromString(headerSignature)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.New("httpsignature error")
|
||||||
|
}
|
||||||
|
if !sig.IsValid(signingKey, tmpHttpRequest) {
|
||||||
|
return false, errors.New("Invalid Signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func CheckScalrSignature(headers map[string]interface{}, body []byte, signingKey string, checkDate bool) (bool, error) {
|
func CheckScalrSignature(headers map[string]interface{}, body []byte, signingKey string, checkDate bool) (bool, error) {
|
||||||
// Check for the signature and date headers
|
// Check for the signature and date headers
|
||||||
if _, ok := headers["X-Signature"]; !ok {
|
if _, ok := headers["X-Signature"]; !ok {
|
||||||
@ -835,6 +872,7 @@ const (
|
|||||||
MatchHashSHA256 string = "payload-hash-sha256"
|
MatchHashSHA256 string = "payload-hash-sha256"
|
||||||
MatchHashSHA512 string = "payload-hash-sha512"
|
MatchHashSHA512 string = "payload-hash-sha512"
|
||||||
IPWhitelist string = "ip-whitelist"
|
IPWhitelist string = "ip-whitelist"
|
||||||
|
MatchHmacSHA256 string = "payload-hmac-sha256"
|
||||||
ScalrSignature string = "scalr-signature"
|
ScalrSignature string = "scalr-signature"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -847,6 +885,10 @@ func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, bod
|
|||||||
return CheckScalrSignature(*headers, *body, r.Secret, true)
|
return CheckScalrSignature(*headers, *body, r.Secret, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.Type == MatchHmacSHA256 {
|
||||||
|
return CheckHmacSHA256(*headers, *body, r.Secret)
|
||||||
|
}
|
||||||
|
|
||||||
arg, err := r.Parameter.Get(headers, query, payload)
|
arg, err := r.Parameter.Get(headers, query, payload)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
switch r.Type {
|
switch r.Type {
|
||||||
|
@ -181,6 +181,104 @@ func TestCheckScalrSignature(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var checkHmacSHA256SignatureTests = []struct {
|
||||||
|
description string
|
||||||
|
headers map[string]interface{}
|
||||||
|
payload []byte
|
||||||
|
secret string
|
||||||
|
expectedSignature string
|
||||||
|
ok bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Valid Signature",
|
||||||
|
map[string]interface{}{
|
||||||
|
"Date": "Thu, 10 Sep 2020 19:09:14 GMT",
|
||||||
|
"Digest": "SHA-256=HQ0wDM4daEmV1R+8SD2bTXu5TPUn/EhMdNyfQL3G3sU=",
|
||||||
|
"Signature": `keyId="hmac-key",algorithm="hmac-sha256",signature="JD2+OsbOqw8DBil5n0a8XVIzvMYXLODcnzJ+R7aieT4=",headers="date digest"`,
|
||||||
|
},
|
||||||
|
[]byte(`"x", "y"`),
|
||||||
|
"600a2774d248847509ba27482330d513", "", true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Wrong Signature",
|
||||||
|
map[string]interface{}{
|
||||||
|
"Date": "Thu, 10 Sep 2020 19:09:14 GMT",
|
||||||
|
"Digest": "SHA-256=HQ0wDM4daEmV1R+8SD2bTXu5TPUn/EhMdNyfQL3G3sU=",
|
||||||
|
"Signature": `keyId="hmac-key",algorithm="hmac-sha256",signature="JD2+OsbOqw8DBil5n0a8XVIzvMYXLODcnzJ+R7aieT4",headers="date digest"`,
|
||||||
|
},
|
||||||
|
[]byte(`"x", "y"`),
|
||||||
|
"600a2774d248847509ba27482330d513", "Invalid Signature", false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Wrong Signature format upstream error",
|
||||||
|
map[string]interface{}{
|
||||||
|
"Date": "Thu, 10 Sep 2020 19:09:14 GMT",
|
||||||
|
"Digest": "SHA-256=HQ0wDM4daEmV1R+8SD2bTXu5TPUn/EhMdNyfQL3G3sU=",
|
||||||
|
"Signature": `algorithm="hmac-sha256",signature="JD2+OsbOqw8DBil5n0a8XVIzvMYXLODcnzJ+R7aieT4",headers="date digest"`,
|
||||||
|
},
|
||||||
|
[]byte(`"x", "y"`),
|
||||||
|
"600a2774d248847509ba27482330d513", "httpsignature error", false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Missing Date header",
|
||||||
|
map[string]interface{}{
|
||||||
|
"Digest": "SHA-256=HQ0wDM4daEmV1R+8SD2bTXu5TPUn/EhMdNyfQL3G3sU=",
|
||||||
|
"Signature": `keyId="hmac-key",algorithm="hmac-sha256",signature="JD2+OsbOqw8DBil5n0a8XVIzvMYXLODcnzJ+R7aieT4=",headers="date digest"`,
|
||||||
|
},
|
||||||
|
[]byte(`"x", "y"`), "600a2774d248847509ba27482330d513", "Missing Date header", false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Missing Digest header",
|
||||||
|
map[string]interface{}{
|
||||||
|
"Date": "Thu, 10 Sep 2020 19:09:14 GMT",
|
||||||
|
"Signature": `keyId="hmac-key",algorithm="hmac-sha256",signature="JD2+OsbOqw8DBil5n0a8XVIzvMYXLODcnzJ+R7aieT4=",headers="date digest"`,
|
||||||
|
},
|
||||||
|
[]byte(`"x", "y"`), "600a2774d248847509ba27482330d513", "Missing Digest header", false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Missing Secret",
|
||||||
|
map[string]interface{}{
|
||||||
|
"Date": "Thu, 10 Sep 2020 19:09:14 GMT",
|
||||||
|
"Digest": "SHA-256=HQ0wDM4daEmV1R+8SD2bTXu5TPUn/EhMdNyfQL3G3sU=",
|
||||||
|
"Signature": `keyId="hmac-key",algorithm="hmac-sha256",signature="JD2+OsbOqw8DBil5n0a8XVIzvMYXLODcnzJ+R7aieT4=",headers="date digest"`,
|
||||||
|
},
|
||||||
|
[]byte(`"x", "y"`), "", "Secret key is required and cannot be empty", false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Incorrect Secret",
|
||||||
|
map[string]interface{}{
|
||||||
|
"Date": "Thu, 10 Sep 2020 19:09:14 GMT",
|
||||||
|
"Digest": "SHA-256=HQ0wDM4daEmV1R+8SD2bTXu5TPUn/EhMdNyfQL3G3sU=",
|
||||||
|
"Signature": `keyId="hmac-key",algorithm="hmac-sha256",signature="JD2+OsbOqw8DBil5n0a8XVIzvMYXLODcnzJ+R7aieT4=",headers="date digest"`,
|
||||||
|
},
|
||||||
|
[]byte(`"x", "y"`), "600a2774d248847509ba27482330d51", "Invalid Signature", false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Incorrect Digest",
|
||||||
|
map[string]interface{}{
|
||||||
|
"Date": "Thu, 10 Sep 2020 19:09:14 GMT",
|
||||||
|
"Digest": "SHA-256=HQ0wDM4daEmV1R+8SD2bTXu5TPUn/EhMdNyfQL3G3sU",
|
||||||
|
"Signature": `keyId="hmac-key",algorithm="hmac-sha256",signature="JD2+OsbOqw8DBil5n0a8XVIzvMYXLODcnzJ+R7aieT4=",headers="date digest"`,
|
||||||
|
},
|
||||||
|
[]byte(`"x", "y"`), "600a2774d248847509ba27482330d513", "Invalid Signature", false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckHmacSHA256Signature(t *testing.T) {
|
||||||
|
for _, testCase := range checkHmacSHA256SignatureTests{
|
||||||
|
valid, err := CheckHmacSHA256(testCase.headers, testCase.payload, testCase.secret)
|
||||||
|
if valid != testCase.ok {
|
||||||
|
t.Errorf("failed to check hmac256 signature for test case: %s\nexpected ok:%#v, got ok:%#v}",
|
||||||
|
testCase.description, testCase.ok, valid)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil && err.Error() != testCase.expectedSignature {
|
||||||
|
t.Errorf("unexpected error message: %s on test case %s", err, testCase.description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var checkIPWhitelistTests = []struct {
|
var checkIPWhitelistTests = []struct {
|
||||||
addr string
|
addr string
|
||||||
ipRange string
|
ipRange string
|
||||||
|
Loading…
Reference in New Issue
Block a user