allow multiple webhook secrets per provider
This commit is contained in:
parent
02f6df4fae
commit
afce71d8cf
|
@ -10,8 +10,10 @@ echo 'GITHUB_SECRET=xxxxxxx' >> .env
|
|||
./gitdeploy run --listen :3000 --serve-path ./public_overrides --exec ./path/to/scripts/dir/
|
||||
```
|
||||
|
||||
To manage `git credentials`
|
||||
see [The Vanilla DevOps Git Credentials Cheatsheet][1]
|
||||
Note: If you have mulitple webhook secrets - such as different repos with the same provider -
|
||||
you should put them in a comma-separated list, such as `GITHUB_SECRET=xxxxxxx,yyyyyyy`.
|
||||
|
||||
To manage `git credentials` see [The Vanilla DevOps Git Credentials Cheatsheet][1]
|
||||
|
||||
[1]: https://coolaj86.com/articles/vanilla-devops-git-credentials-cheatsheet/
|
||||
|
||||
|
|
|
@ -31,28 +31,31 @@ func init() {
|
|||
|
||||
// InitWebhook prepares the webhook router.
|
||||
// It should be called after arguments are parsed and ENVs are set.InitWebhook
|
||||
func InitWebhook(providername string, secret *string, envname string) func() {
|
||||
func InitWebhook(providername string, secretList *string, envname string) func() {
|
||||
return func() {
|
||||
if "" == *secret {
|
||||
*secret = os.Getenv(envname)
|
||||
}
|
||||
if "" == *secret {
|
||||
fmt.Fprintf(os.Stderr, "skipped route for missing %s\n", envname)
|
||||
secrets := webhooks.ParseSecrets(providername, *secretList, envname)
|
||||
if 0 == len(secrets) {
|
||||
fmt.Fprintf(os.Stderr, "skipped route for missing %q\n", envname)
|
||||
return
|
||||
}
|
||||
secretB := []byte(*secret)
|
||||
|
||||
webhooks.AddRouteHandler(providername, func(router chi.Router) {
|
||||
router.Post("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, options.DefaultMaxBodySize)
|
||||
|
||||
accessToken := r.URL.Query().Get("access_token")
|
||||
if "" != accessToken {
|
||||
if 0 == subtle.ConstantTimeCompare(
|
||||
[]byte(r.URL.Query().Get("access_token")),
|
||||
secretB,
|
||||
) {
|
||||
log.Printf("invalid bitbucket access_token\n")
|
||||
http.Error(w, "invalid access_token", http.StatusBadRequest)
|
||||
if len(accessToken) > 0 {
|
||||
var valid bool
|
||||
accessTokenB := []byte(accessToken)
|
||||
for _, secret := range secrets {
|
||||
if 1 == subtle.ConstantTimeCompare(accessTokenB, secret) {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
log.Printf("invalid %q access_token\n", providername)
|
||||
http.Error(w, fmt.Sprintf("invalid %q access_token", providername), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -64,12 +67,19 @@ func InitWebhook(providername string, secret *string, envname string) func() {
|
|||
return
|
||||
}
|
||||
|
||||
if "" == accessToken {
|
||||
if 0 == len(accessToken) {
|
||||
sig := r.Header.Get("X-Hub-Signature")
|
||||
// TODO replace with generic X-Hub-Signature validation
|
||||
if err := github.ValidateSignature(sig, payload, secretB); nil != err {
|
||||
log.Printf("invalid bitbucket signature: error: %s\n", err)
|
||||
http.Error(w, "invalid bitbucket signature", http.StatusBadRequest)
|
||||
for _, secret := range secrets {
|
||||
// TODO replace with generic X-Hub-Signature validation
|
||||
if err = github.ValidateSignature(sig, payload, secret); nil != err {
|
||||
continue
|
||||
}
|
||||
// err = nil
|
||||
break
|
||||
}
|
||||
if nil != err {
|
||||
log.Printf("invalid %q signature: error: %s\n", providername, err)
|
||||
http.Error(w, fmt.Sprintf("invalid %q signature", providername), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,16 +32,14 @@ func init() {
|
|||
|
||||
// InitWebhook prepares the webhook router.
|
||||
// It should be called after arguments are parsed and ENVs are set.InitWebhook
|
||||
func InitWebhook(providername string, secret *string, envname string) func() {
|
||||
func InitWebhook(providername string, secretList *string, envname string) func() {
|
||||
return func() {
|
||||
if "" == *secret {
|
||||
*secret = os.Getenv(envname)
|
||||
}
|
||||
if "" == *secret {
|
||||
fmt.Fprintf(os.Stderr, "skipped route for missing %s\n", envname)
|
||||
secrets := webhooks.ParseSecrets(providername, *secretList, envname)
|
||||
if 0 == len(secrets) {
|
||||
fmt.Fprintf(os.Stderr, "skipped route for missing %q\n", envname)
|
||||
return
|
||||
}
|
||||
secretB := []byte(*secret)
|
||||
|
||||
webhooks.AddRouteHandler(providername, func(router chi.Router) {
|
||||
router.Post("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, options.DefaultMaxBodySize)
|
||||
|
@ -52,11 +50,18 @@ func InitWebhook(providername string, secret *string, envname string) func() {
|
|||
return
|
||||
}
|
||||
|
||||
var valid bool
|
||||
sig := r.Header.Get("X-Gitea-Signature")
|
||||
sigB, err := hex.DecodeString(sig)
|
||||
if !ValidMAC(payload, sigB, secretB) {
|
||||
log.Printf("invalid gitea signature: %q\n", sig)
|
||||
http.Error(w, "invalid gitea signature", http.StatusBadRequest)
|
||||
for _, secret := range secrets {
|
||||
if ValidMAC(payload, sigB, secret) {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
log.Printf("invalid %q signature: %q\n", providername, sig)
|
||||
http.Error(w, fmt.Sprintf("invalid %q signature", providername), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -18,24 +18,22 @@ import (
|
|||
)
|
||||
|
||||
func init() {
|
||||
var githubSecret string
|
||||
var githubSecrets string
|
||||
options.ServerFlags.StringVar(
|
||||
&githubSecret, "github-secret", "",
|
||||
&githubSecrets, "github-secret", "",
|
||||
"secret for github webhooks (same as GITHUB_SECRET=)",
|
||||
)
|
||||
webhooks.AddProvider("github", InitWebhook("github", &githubSecret, "GITHUB_SECRET"))
|
||||
webhooks.AddProvider("github", InitWebhook("github", &githubSecrets, "GITHUB_SECRET"))
|
||||
}
|
||||
|
||||
func InitWebhook(providername string, secret *string, envname string) func() {
|
||||
func InitWebhook(providername string, secretList *string, envname string) func() {
|
||||
return func() {
|
||||
if "" == *secret {
|
||||
*secret = os.Getenv(envname)
|
||||
}
|
||||
if "" == *secret {
|
||||
fmt.Fprintf(os.Stderr, "skipped route for missing %s\n", envname)
|
||||
secrets := webhooks.ParseSecrets(providername, *secretList, envname)
|
||||
if 0 == len(secrets) {
|
||||
fmt.Fprintf(os.Stderr, "skipped route for missing %q\n", envname)
|
||||
return
|
||||
}
|
||||
githubSecretB := []byte(*secret)
|
||||
|
||||
webhooks.AddRouteHandler(providername, func(router chi.Router) {
|
||||
router.Post("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, options.DefaultMaxBodySize)
|
||||
|
@ -47,9 +45,16 @@ func InitWebhook(providername string, secret *string, envname string) func() {
|
|||
}
|
||||
|
||||
sig := r.Header.Get("X-Hub-Signature")
|
||||
if err := github.ValidateSignature(sig, payload, githubSecretB); nil != err {
|
||||
log.Printf("invalid github signature: error: %s\n", err)
|
||||
http.Error(w, "invalid github signature", http.StatusBadRequest)
|
||||
for _, secret := range secrets {
|
||||
if err = github.ValidateSignature(sig, payload, secret); nil != err {
|
||||
continue
|
||||
}
|
||||
// err = nil
|
||||
break
|
||||
}
|
||||
if nil != err {
|
||||
log.Printf("invalid %q signature: error: %s\n", providername, err)
|
||||
http.Error(w, fmt.Sprintf("invalid %q signature", providername), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package webhooks
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
|
@ -61,3 +64,21 @@ func RouteHandlers(r chi.Router) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func ParseSecrets(providername, secretList, envname string) [][]byte {
|
||||
if 0 == len(secretList) {
|
||||
secretList = os.Getenv(envname)
|
||||
}
|
||||
if 0 == len(secretList) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var secrets [][]byte
|
||||
for _, secret := range strings.Fields(strings.ReplaceAll(secretList, ",", " ")) {
|
||||
if len(secret) > 0 {
|
||||
secrets = append(secrets, []byte(secret))
|
||||
}
|
||||
}
|
||||
|
||||
return secrets
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue