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
|
||||
|
||||
__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
|
||||
|
||||
require (
|
||||
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e
|
||||
github.com/clbanning/mxj v1.8.4
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
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/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
|
@ -25,8 +25,10 @@ import (
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
"net/http"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/99designs/httpsignatures-go"
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
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) {
|
||||
// Check for the signature and date headers
|
||||
if _, ok := headers["X-Signature"]; !ok {
|
||||
@ -835,6 +872,7 @@ const (
|
||||
MatchHashSHA256 string = "payload-hash-sha256"
|
||||
MatchHashSHA512 string = "payload-hash-sha512"
|
||||
IPWhitelist string = "ip-whitelist"
|
||||
MatchHmacSHA256 string = "payload-hmac-sha256"
|
||||
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)
|
||||
}
|
||||
|
||||
if r.Type == MatchHmacSHA256 {
|
||||
return CheckHmacSHA256(*headers, *body, r.Secret)
|
||||
}
|
||||
|
||||
arg, err := r.Parameter.Get(headers, query, payload)
|
||||
if err == nil {
|
||||
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 {
|
||||
addr string
|
||||
ipRange string
|
||||
|
Loading…
Reference in New Issue
Block a user