Skip to content

Commit

Permalink
Merge pull request #127 from SomtochiAma/generic-signing
Browse files Browse the repository at this point in the history
Add generic webhook receiver for HMAC signing
  • Loading branch information
stefanprodan committed Jan 21, 2021
2 parents dc6e6e6 + 0876b74 commit 6167e93
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 20 deletions.
22 changes: 11 additions & 11 deletions api/v1beta1/receiver_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ import (
type ReceiverSpec struct {
// Type of webhook sender, used to determine
// the validation procedure and payload deserialization.
// +kubebuilder:validation:Enum=generic;github;gitlab;bitbucket;harbor;dockerhub;quay;gcr
// +kubebuilder:validation:Enum=generic;github;gitlab;bitbucket;harbor;dockerhub;quay;gcr;nexus
// +kubebuilder:validation:Enum=generic;generic-hmac;github;gitlab;bitbucket;harbor;dockerhub;quay;gcr;nexus
// +required
Type string `json:"type"`

Expand Down Expand Up @@ -64,15 +63,16 @@ type ReceiverStatus struct {
}

const (
GenericReceiver string = "generic"
GitHubReceiver string = "github"
GitLabReceiver string = "gitlab"
BitbucketReceiver string = "bitbucket"
HarborReceiver string = "harbor"
DockerHubReceiver string = "dockerhub"
QuayReceiver string = "quay"
GCRReceiver string = "gcr"
NexusReceiver string = "nexus"
GenericReceiver string = "generic"
GenericHMACReceiver string = "generic-hmac"
GitHubReceiver string = "github"
GitLabReceiver string = "gitlab"
BitbucketReceiver string = "bitbucket"
HarborReceiver string = "harbor"
DockerHubReceiver string = "dockerhub"
QuayReceiver string = "quay"
GCRReceiver string = "gcr"
NexusReceiver string = "nexus"
)

func ReceiverReady(receiver Receiver, reason, message, url string) Receiver {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ spec:
procedure and payload deserialization.
enum:
- generic
- generic-hmac
- github
- gitlab
- bitbucket
Expand Down
82 changes: 73 additions & 9 deletions docs/spec/v1beta1/receiver.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,16 @@ Receiver types:

```go
const (
GenericReceiver string = "generic"
GitHubReceiver string = "github"
GitLabReceiver string = "gitlab"
BitbucketReceiver string = "bitbucket"
HarborReceiver string = "harbor"
DockerHubReceiver string = "dockerhub"
QuayReceiver string = "quay"
GCRReceiver string = "gcr"
NexusReceiver string = "nexus"
GenericReceiver string = "generic"
GenericHMACReceiver string = "generic-hmac"
GitHubReceiver string = "github"
GitLabReceiver string = "gitlab"
BitbucketReceiver string = "bitbucket"
HarborReceiver string = "harbor"
DockerHubReceiver string = "dockerhub"
QuayReceiver string = "quay"
GCRReceiver string = "gcr"
NexusReceiver string = "nexus"
)
```

Expand Down Expand Up @@ -102,6 +103,69 @@ spec:
When the receiver type is set to `generic`, the controller will not perform token validation nor event filtering.

### Generic HMAC receiver

```yaml
apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Receiver
metadata:
name: generic-hmac-receiver
namespace: default
spec:
type: generic-hmac
secretRef:
name: webhook-token
resources:
- kind: GitRepository
name: webapp
namespace: default
```

This generic receiver performs token validation. The controller uses the `X-Signature` header to get
the hash signature. The signature should be prefixed with the hash function(`sha1`, `sha256`, or `sha512`) like this:
`<hash-function>=<hash-signation>`.

1. Generate hash using open ssl and sha1
```sh
echo -n '<body-of-request>' | openssl dgst -sha1 -hmac "aHR0cHM6Ly9ob29rcy5zbGFjay5jb20vc2VydmljZXMv"
```
You can use the flag `sha256` or `sha512` if you want a different hash function

This would output the hash.

2. Send a POST request to the webhook url
```
curl <webhook-url> \
-X POST \
-H "X-Signature: sha1=<generated-hash>" \
-d '<body-of-request>'
```
Generate hash signature using Go:
```go
package main
import (
"crypto/hmac"
"fmt"
"crypto/sha1"
)
// input is the body of the request
// key is your secret token
func GetSignature(input, key string) string {
key_for_sign := []byte(key)
h := hmac.New(sha1.New, key_for_sign)
h.Write([]byte(input))
return fmt.Sprintf("%x", h.Sum(nil))
}
// Don't forget to set request Headers
// req.Header.Set("X-Signature", fmt.Sprintf("sha1=%s", <returned string>))
```


### GitHub receiver

```yaml
Expand Down
15 changes: 15 additions & 0 deletions internal/server/receiver_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,21 @@ func (s *ReceiverServer) validate(ctx context.Context, receiver v1beta1.Receiver
switch receiver.Spec.Type {
case v1beta1.GenericReceiver:
return nil
case v1beta1.GenericHMACReceiver:
b, err := ioutil.ReadAll(r.Body)
if err != nil {
return fmt.Errorf("unable to read request body: %s", err)
}

err = github.ValidateSignature(r.Header.Get("X-Signature"), b, []byte(token))
if err != nil {
return fmt.Errorf("unable to validate signature: %s", err)
}

s.logger.Info(
"handling event from generic-hmac wehbook",
"receiver", receiver.Name)
return nil
case v1beta1.GitHubReceiver:
payload, err := github.ValidatePayload(r, []byte(token))
if err != nil {
Expand Down

0 comments on commit 6167e93

Please sign in to comment.