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