add drone.io webhook signature

This commit is contained in:
sksadri 2020-09-13 19:14:40 +03:00
parent 345bf3d409
commit eaa5796aad
5 changed files with 193 additions and 0 deletions

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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 {

View File

@ -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