From 1685bd9f29ea0e157a729fbad2f12890b9978746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20de=20Metz?= Date: Sun, 5 Mar 2017 16:33:00 +0100 Subject: [PATCH 1/7] Add github webhook signature check. --- .../inputs/webhooks/github/github_webhooks.go | 25 ++++++++++++-- .../webhooks/github/github_webhooks_test.go | 33 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/plugins/inputs/webhooks/github/github_webhooks.go b/plugins/inputs/webhooks/github/github_webhooks.go index a31c6fdf2280c..529efe5be6894 100644 --- a/plugins/inputs/webhooks/github/github_webhooks.go +++ b/plugins/inputs/webhooks/github/github_webhooks.go @@ -1,6 +1,9 @@ package github import ( + "crypto/hmac" + "crypto/sha1" + "encoding/hex" "encoding/json" "io/ioutil" "log" @@ -11,8 +14,9 @@ import ( ) type GithubWebhook struct { - Path string - acc telegraf.Accumulator + Path string + Secret string + acc telegraf.Accumulator } func (gh *GithubWebhook) Register(router *mux.Router, acc telegraf.Accumulator) { @@ -29,6 +33,12 @@ func (gh *GithubWebhook) eventHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) return } + + if gh.Secret != "" && !checkSignature(gh.Secret, data, r.Header["X-Hub-Signature"][0]) { + w.WriteHeader(http.StatusBadRequest) + return + } + e, err := NewEvent(data, eventType) if err != nil { w.WriteHeader(http.StatusBadRequest) @@ -108,3 +118,14 @@ func NewEvent(data []byte, name string) (Event, error) { } return nil, &newEventError{"Not a recognized event type"} } + +func checkSignature(secret string, data []byte, signature string) bool { + return hmac.Equal([]byte(signature), []byte(generateSignature(secret, data))) +} + +func generateSignature(secret string, data []byte) string { + mac := hmac.New(sha1.New, []byte(secret)) + mac.Write(data) + result := mac.Sum(nil) + return "sha1=" + hex.EncodeToString(result) +} diff --git a/plugins/inputs/webhooks/github/github_webhooks_test.go b/plugins/inputs/webhooks/github/github_webhooks_test.go index 0ec9917264374..65041e4a06125 100644 --- a/plugins/inputs/webhooks/github/github_webhooks_test.go +++ b/plugins/inputs/webhooks/github/github_webhooks_test.go @@ -21,6 +21,19 @@ func GithubWebhookRequest(event string, jsonString string, t *testing.T) { } } +func GithubWebhookRequestWithSignature(event string, jsonString string, t *testing.T, signature string, expectedStatus int) { + var acc testutil.Accumulator + gh := &GithubWebhook{Path: "/github", Secret: "signature", acc: &acc} + req, _ := http.NewRequest("POST", "/github", strings.NewReader(jsonString)) + req.Header.Add("X-Github-Event", event) + req.Header.Add("X-Hub-Signature", signature) + w := httptest.NewRecorder() + gh.eventHandler(w, req) + if w.Code != expectedStatus { + t.Errorf("POST "+event+" returned HTTP status code %v.\nExpected %v", w.Code, expectedStatus) + } +} + func TestCommitCommentEvent(t *testing.T) { GithubWebhookRequest("commit_comment", CommitCommentEventJSON(), t) } @@ -100,3 +113,23 @@ func TestTeamAddEvent(t *testing.T) { func TestWatchEvent(t *testing.T) { GithubWebhookRequest("watch", WatchEventJSON(), t) } + +func TestEventWithSignatureFail(t *testing.T) { + GithubWebhookRequestWithSignature("watch", WatchEventJSON(), t, "signature", http.StatusBadRequest) +} + +func TestEventWithSignatureSuccess(t *testing.T) { + GithubWebhookRequestWithSignature("watch", WatchEventJSON(), t, generateSignature("signature", []byte(WatchEventJSON())), http.StatusOK) +} + +func TestCheckSignatureSuccess(t *testing.T) { + if !checkSignature("my_little_secret", []byte("random-signature-body"), "sha1=3dca279e731c97c38e3019a075dee9ebbd0a99f0") { + t.Errorf("check signature failed") + } +} + +func TestCheckSignatureFailed(t *testing.T) { + if checkSignature("m_little_secret", []byte("random-signature-body"), "sha1=3dca279e731c97c38e3019a075dee9ebbd0a99f0") { + t.Errorf("check signature failed") + } +} From ef40a816154abcd7cf2e8f5beb4c196b0d49f696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20de=20Metz?= Date: Sun, 5 Mar 2017 17:07:46 +0100 Subject: [PATCH 2/7] Add example config. --- etc/telegraf.conf | 1 + plugins/inputs/webhooks/webhooks.go | 1 + 2 files changed, 2 insertions(+) diff --git a/etc/telegraf.conf b/etc/telegraf.conf index 63e41d7bbcd45..07ae5ac8fd036 100644 --- a/etc/telegraf.conf +++ b/etc/telegraf.conf @@ -2382,6 +2382,7 @@ # # [inputs.webhooks.github] # path = "/github" +# # secret = "" # # [inputs.webhooks.mandrill] # path = "/mandrill" diff --git a/plugins/inputs/webhooks/webhooks.go b/plugins/inputs/webhooks/webhooks.go index fcddbebd7f6e3..bc8519d7a4ede 100644 --- a/plugins/inputs/webhooks/webhooks.go +++ b/plugins/inputs/webhooks/webhooks.go @@ -47,6 +47,7 @@ func (wb *Webhooks) SampleConfig() string { [inputs.webhooks.github] path = "/github" + # secret = "" [inputs.webhooks.mandrill] path = "/mandrill" From 76c0910cfbfe61380bea1174d534803f3ec96ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20de=20Metz?= Date: Sun, 5 Mar 2017 17:12:43 +0100 Subject: [PATCH 3/7] Add a log message when the signature don't match. --- plugins/inputs/webhooks/github/github_webhooks.go | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/inputs/webhooks/github/github_webhooks.go b/plugins/inputs/webhooks/github/github_webhooks.go index 529efe5be6894..8d2538ac7ec51 100644 --- a/plugins/inputs/webhooks/github/github_webhooks.go +++ b/plugins/inputs/webhooks/github/github_webhooks.go @@ -35,6 +35,7 @@ func (gh *GithubWebhook) eventHandler(w http.ResponseWriter, r *http.Request) { } if gh.Secret != "" && !checkSignature(gh.Secret, data, r.Header["X-Hub-Signature"][0]) { + log.Printf("I! Fail to check the github webhook signature\n") w.WriteHeader(http.StatusBadRequest) return } From 263ace4d3b8bfcda46fc70bcd1eb8d9057b140c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20de=20Metz?= Date: Sun, 5 Mar 2017 19:06:16 +0100 Subject: [PATCH 4/7] Use Header.Get. --- plugins/inputs/webhooks/github/github_webhooks.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/inputs/webhooks/github/github_webhooks.go b/plugins/inputs/webhooks/github/github_webhooks.go index 8d2538ac7ec51..94a5c8d26c360 100644 --- a/plugins/inputs/webhooks/github/github_webhooks.go +++ b/plugins/inputs/webhooks/github/github_webhooks.go @@ -27,14 +27,14 @@ func (gh *GithubWebhook) Register(router *mux.Router, acc telegraf.Accumulator) func (gh *GithubWebhook) eventHandler(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() - eventType := r.Header["X-Github-Event"][0] + eventType := r.Header.Get("X-Github-Event") data, err := ioutil.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) return } - if gh.Secret != "" && !checkSignature(gh.Secret, data, r.Header["X-Hub-Signature"][0]) { + if gh.Secret != "" && !checkSignature(gh.Secret, data, r.Header.Get("X-Hub-Signature")) { log.Printf("I! Fail to check the github webhook signature\n") w.WriteHeader(http.StatusBadRequest) return From 33168988d6b4a982715908052bd33fbbce5b6f73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20de=20Metz?= Date: Mon, 6 Mar 2017 17:51:52 +0100 Subject: [PATCH 5/7] Update default log level. --- plugins/inputs/webhooks/github/github_webhooks.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/inputs/webhooks/github/github_webhooks.go b/plugins/inputs/webhooks/github/github_webhooks.go index 94a5c8d26c360..0bb792bf5df08 100644 --- a/plugins/inputs/webhooks/github/github_webhooks.go +++ b/plugins/inputs/webhooks/github/github_webhooks.go @@ -35,7 +35,7 @@ func (gh *GithubWebhook) eventHandler(w http.ResponseWriter, r *http.Request) { } if gh.Secret != "" && !checkSignature(gh.Secret, data, r.Header.Get("X-Hub-Signature")) { - log.Printf("I! Fail to check the github webhook signature\n") + log.Printf("E! Fail to check the github webhook signature\n") w.WriteHeader(http.StatusBadRequest) return } From cad84e6c69e987945caf3700ea080b2aefea123c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20de=20Metz?= Date: Sun, 16 Apr 2017 12:14:37 +0200 Subject: [PATCH 6/7] Add entry in the changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 989e3f7a9298c..6715ef3bb8fd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,7 @@ be deprecated eventually. - [#2636](https://github.com/influxdata/telegraf/pull/2636): Add `message_len_max` option to `kafka_consumer` input - [#1100](https://github.com/influxdata/telegraf/issues/1100): Add collectd parser - [#1820](https://github.com/influxdata/telegraf/issues/1820): easier plugin testing without outputs +- [#2493](https://github.com/influxdata/telegraf/pull/2493): Check signature in the GitHub webhook plugin ### Bugfixes From c5f9620769c16ae01688889e0908b7c476fd3dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20de=20Metz?= Date: Sun, 16 Apr 2017 12:18:43 +0200 Subject: [PATCH 7/7] Add doc about secret. --- plugins/inputs/webhooks/github/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/inputs/webhooks/github/README.md b/plugins/inputs/webhooks/github/README.md index 68594cd78f86b..908d92a639d57 100644 --- a/plugins/inputs/webhooks/github/README.md +++ b/plugins/inputs/webhooks/github/README.md @@ -2,6 +2,8 @@ You should configure your Organization's Webhooks to point at the `webhooks` service. To do this go to `github.com/{my_organization}` and click `Settings > Webhooks > Add webhook`. In the resulting menu set `Payload URL` to `http://:1619/github`, `Content type` to `application/json` and under the section `Which events would you like to trigger this webhook?` select 'Send me everything'. By default all of the events will write to the `github_webhooks` measurement, this is configurable by setting the `measurement_name` in the config file. +You can also add a secret that will be used by telegraf to verify the authenticity of the requests. + ## Events The titles of the following sections are links to the full payloads and details for each event. The body contains what information from the event is persisted. The format is as follows: