From 6f299d5229b6b15909a8d814f72c72c174c50809 Mon Sep 17 00:00:00 2001 From: Somtochi Onyekwere Date: Fri, 15 Jan 2021 15:13:44 +0100 Subject: [PATCH 1/4] Add webhook for nexus Signed-off-by: Somtochi Onyekwere --- api/v1beta1/receiver_types.go | 2 + ...ification.toolkit.fluxcd.io_receivers.yaml | 1 + docs/spec/v1beta1/receiver.md | 18 +++++++++ internal/server/receiver_handlers.go | 39 +++++++++++++++++++ 4 files changed, 60 insertions(+) diff --git a/api/v1beta1/receiver_types.go b/api/v1beta1/receiver_types.go index 75566ce47..a19c5b214 100644 --- a/api/v1beta1/receiver_types.go +++ b/api/v1beta1/receiver_types.go @@ -28,6 +28,7 @@ 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 // +required Type string `json:"type"` @@ -71,6 +72,7 @@ const ( DockerHubReceiver string = "dockerhub" QuayReceiver string = "quay" GCRReceiver string = "gcr" + NexusReceiver string = "nexus" ) func ReceiverReady(receiver Receiver, reason, message, url string) Receiver { diff --git a/config/crd/bases/notification.toolkit.fluxcd.io_receivers.yaml b/config/crd/bases/notification.toolkit.fluxcd.io_receivers.yaml index 97eb09e7a..9d9b17900 100644 --- a/config/crd/bases/notification.toolkit.fluxcd.io_receivers.yaml +++ b/config/crd/bases/notification.toolkit.fluxcd.io_receivers.yaml @@ -111,6 +111,7 @@ spec: - dockerhub - quay - gcr + - nexus type: string required: - resources diff --git a/docs/spec/v1beta1/receiver.md b/docs/spec/v1beta1/receiver.md index 25858213c..eef5edd97 100644 --- a/docs/spec/v1beta1/receiver.md +++ b/docs/spec/v1beta1/receiver.md @@ -46,6 +46,7 @@ const ( DockerHubReceiver string = "dockerhub" QuayReceiver string = "quay" GCRReceiver string = "gcr" + NexusReceiver string = "nexus" ) ``` @@ -229,6 +230,23 @@ spec: name: webapp ``` +### Nexus receiver + +```yaml +apiVersion: notification.toolkit.fluxcd.io/v1beta1 +kind: Receiver +metadata: + name: nexus-receiver + namespace: default +spec: + type: nexus + secretRef: + name: webhook-token + resources: + - kind: ImageRepository + name: webapp +``` + ### GCR receiver ```yaml diff --git a/internal/server/receiver_handlers.go b/internal/server/receiver_handlers.go index 2f5c90f63..15614e829 100644 --- a/internal/server/receiver_handlers.go +++ b/internal/server/receiver_handlers.go @@ -19,8 +19,12 @@ package server import ( "context" "encoding/base64" + "crypto/hmac" + "crypto/sha1" + "encoding/hex" "encoding/json" "fmt" + "io/ioutil" "net/http" "net/url" "strings" @@ -265,6 +269,34 @@ func (s *ReceiverServer) validate(ctx context.Context, receiver v1beta1.Receiver fmt.Sprintf("handling event from %s for tag %s", d.Digest, d.Tag), "receiver", receiver.Name) return nil + case v1beta1.NexusReceiver: + signature := r.Header.Get("X-Nexus-Webhook-Signature") + if len(signature) == 0 { + return fmt.Errorf("Signature is missing from header") + } + + b, err := ioutil.ReadAll(r.Body) + if err != nil { + return fmt.Errorf("cannot read payload. error: %s", err) + } + + if !verifyHmacSignature([]byte(token), signature, b) { + return fmt.Errorf("invalid nexus signature") + } + type payload struct { + Action string `json:"action"` + RepositoryName string `json:"repositoryName"` + } + var p payload + + if err := json.Unmarshal(b, &p); err != nil { + return fmt.Errorf("cannot decode Nexus webhook payload: %s", err) + } + + s.logger.Info( + fmt.Sprintf("handling event from %s", p.RepositoryName), + "receiver", receiver.Name) + return nil } return fmt.Errorf("recevier type '%s' not supported", receiver.Spec.Type) @@ -382,3 +414,10 @@ func authenticateGCRRequest(c *http.Client, bearer string, tokenIndex int) (err return nil } + +func verifyHmacSignature(key []byte, signature string, payload []byte) bool { + mac := hmac.New(sha1.New, key) + _, _ = mac.Write(payload) + expectedMAC := hex.EncodeToString(mac.Sum(nil)) + return hmac.Equal([]byte(signature), []byte(expectedMAC)) +} From 9e37e307efb5b18f92403ccb7b6dc7d85a080370 Mon Sep 17 00:00:00 2001 From: Somtochi Onyekwere Date: Wed, 20 Jan 2021 12:43:08 +0100 Subject: [PATCH 2/4] run make test Signed-off-by: Somtochi Onyekwere --- internal/server/receiver_handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/server/receiver_handlers.go b/internal/server/receiver_handlers.go index 15614e829..39a429c76 100644 --- a/internal/server/receiver_handlers.go +++ b/internal/server/receiver_handlers.go @@ -18,9 +18,9 @@ package server import ( "context" - "encoding/base64" "crypto/hmac" "crypto/sha1" + "encoding/base64" "encoding/hex" "encoding/json" "fmt" From d9a9cf7bb638c51ef71c64ec24eecbc1e845464b Mon Sep 17 00:00:00 2001 From: Somtochi Onyekwere Date: Wed, 20 Jan 2021 14:18:17 +0100 Subject: [PATCH 3/4] add nexus signature header to docs Signed-off-by: Somtochi Onyekwere --- docs/spec/v1beta1/receiver.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/spec/v1beta1/receiver.md b/docs/spec/v1beta1/receiver.md index eef5edd97..0013d23c9 100644 --- a/docs/spec/v1beta1/receiver.md +++ b/docs/spec/v1beta1/receiver.md @@ -247,6 +247,10 @@ spec: name: webapp ``` +Note that you have to fill in the generated token as the secret key when creating the Nexus Webhook Capability. +See [nexus documentation](https://help.sonatype.com/repomanager3/webhooks/enabling-a-repository-webhook-capability) +The controller uses the `X-Nexus-Webhook-Signature` HTTP header to verify that the request is legitimate. + ### GCR receiver ```yaml From f293489aa81f2f9841d84415f792162fa20940ef Mon Sep 17 00:00:00 2001 From: Somtochi Onyekwere Date: Wed, 20 Jan 2021 15:39:17 +0100 Subject: [PATCH 4/4] Update the link Signed-off-by: Somtochi Onyekwere --- docs/spec/v1beta1/receiver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/v1beta1/receiver.md b/docs/spec/v1beta1/receiver.md index 0013d23c9..8eaaabfa2 100644 --- a/docs/spec/v1beta1/receiver.md +++ b/docs/spec/v1beta1/receiver.md @@ -248,7 +248,7 @@ spec: ``` Note that you have to fill in the generated token as the secret key when creating the Nexus Webhook Capability. -See [nexus documentation](https://help.sonatype.com/repomanager3/webhooks/enabling-a-repository-webhook-capability) +See [Nexus Webhook Capability](https://help.sonatype.com/repomanager3/webhooks/enabling-a-repository-webhook-capability) The controller uses the `X-Nexus-Webhook-Signature` HTTP header to verify that the request is legitimate. ### GCR receiver