Dear Gitlab users, due to maintenance reasons, Gitlab will not be available on Thursday 30.09.2021 from 5:00 pm to approximately 5:30 pm.

Commit 90802049 authored by Andreas Wagner's avatar Andreas Wagner
Browse files

Add securing webhooks via hmac signature.

parent e7bf7a6b
......@@ -85,7 +85,7 @@ Here is a complete zenodo config section:
In the `Git` config section, besides the obvious `host` and `token` keys, there are several keys that allow you to control what is being handled and how:
Since the software can receive hooks from no matter where, and relies on information contained in the hook in order to "follow its nose" and retrieve files that it then uploads to zenodo, you can specify a repository the hooks of which will be processed exclusively. If hooks from other repositories come in, they will be ignored. If you leave the `repo` key empty, on the other hand, they will be processed as well.
Since the software can receive hooks from no matter where, and relies on information contained in the hook in order to "follow its nose" and retrieve files that it then uploads to zenodo, you can filter in many ways what is being processed: if you add a [secret](https://developer.github.com/webhooks/securing/) to your github repository, you can add this string to a configuration setting calles `secret`, which is used to check incoming messages against their signatures. You can also specify a repository in the `repo` key, the hooks of which will be processed exclusively. If hooks from other repositories come in, they will be ignored. If you leave the `repo` key empty, on the other hand, they will be processed as well.
The same holds for the `user` key: If you specify a value here, only hooks initiated by this github user will be processed. (In the case of "push" webhooks, the payload's `pusher.name` field is compared to this config value.)
......@@ -99,6 +99,8 @@ Here is a complete github config section:
"Git": {
"host": "https://api.github.com",
"token": "aBcDeFgHiJkLmNoPqRsTuVwXyZ",
"secret": "ThIs_iS_NoT_ReAlLy_a_sMaRt_sEcReT!!",
"secret": "",
"user": "foobar",
"repo": "octocat/hello-world",
"commit_phrase": "",
......
......@@ -16,6 +16,7 @@
"Git": {
"host": "https://api.github.com",
"token": "aBcDeFgHiJkLmNoPqRsTuVwXyZ",
"secret": "ThIs_iS_NoT_ReAlLy_a_sMaRt_sEcReT!!",
"user": "digicademy",
"repo": "digicademy/svsal",
"commit_phrase": "",
......
......@@ -2,6 +2,9 @@ package routing
import (
"bytes"
"crypto/hmac"
"crypto/sha1"
"encoding/hex"
"fmt"
"io"
"net/http"
......@@ -124,11 +127,33 @@ func SetupRoutes(conf tei2zenodo.Config) *gin.Engine {
file := buf.String()
r = strings.NewReader(file)
// If Secret is configured, check signature
if conf.Git.Secret != "" {
signature := c.Request.Header.Get("X-Hub-Signature")
result := ValidMAC([]byte(file), []byte(signature), []byte(conf.Git.Secret))
if !result {
if conf.Verbose {
mac := hmac.New(sha1.New, []byte(conf.Git.Secret))
mac.Write([]byte(file))
expectedMAC := []byte("sha1=" + hex.EncodeToString(mac.Sum(nil)))
log.Printf("Webhook with invalid HMAC signature received, ignoring. Sign.: %s, Message hash: %s", signature, expectedMAC)
} else {
log.Printf("Webhook with invalid HMAC signature received, ignoring")
}
AbortMsg(500, tei2zenodo.NewError("errWebhook", fmt.Sprintf("Webhook with invalid HMAC signature received, ignoring"), 500, nil), c)
return
}
if conf.Verbose {
log.Printf(" Webhook HMAC signature verified")
}
}
// Process Hook to extract files
doPublish, files, PHErr := github.ProcessHook(hookType, r, &conf)
if PHErr != nil {
log.Printf("Error parsing webhook event: %v", PHErr)
AbortMsg(500, tei2zenodo.NewError("errWebhook", fmt.Sprintf("problem parsing webhook for %s event", hookType), 500, PHErr), c)
return
}
// log.Printf(" %d files returned", len(files))
log.Printf(" doPublish: %s", strconv.FormatBool(doPublish))
......@@ -191,3 +216,11 @@ func AbortMsg(code int, err *tei2zenodo.Error, c *gin.Context) {
}
c.Abort()
}
// ValidMAC reports whether messageMAC is a valid HMAC tag for message.
func ValidMAC(message, messageMAC, key []byte) bool {
mac := hmac.New(sha1.New, key)
mac.Write(message)
expectedMAC := []byte("sha1=" + hex.EncodeToString(mac.Sum(nil)))
return hmac.Equal(messageMAC, expectedMAC)
}
......@@ -32,6 +32,7 @@ type ZenodoConfig struct {
type GitConfig struct {
Host string
Token string
Secret string
User string
Repo string
Phrase string `json:"commit_phrase"`
......@@ -233,7 +234,7 @@ type ZDate struct {
// Error messages
type Error struct {
Typ string // errNoTEIXML, errParse, errWebHook, errZProcessing, errGHProcessing, errSerializing, errInternal, errBadConfig, errNetComm
Typ string // errNoTEIXML, errParse, errWebhook, errZProcessing, errGHProcessing, errSerializing, errInternal, errBadConfig, errNetComm
Message string
HTTPCode int
ErrorVal error // what may have been raised by previous code
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment