added webhook validation
This commit is contained in:
parent
a021fab543
commit
dd88498e0d
6 changed files with 158 additions and 21 deletions
100
git.go
100
git.go
|
|
@ -4,9 +4,14 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
|
@ -19,7 +24,10 @@ const (
|
|||
contentType = "Content-Type"
|
||||
jsonType = "application/json"
|
||||
|
||||
secretName = "git-auth"
|
||||
authSecretName = "git-auth"
|
||||
validateSecretName = "webhook-secret"
|
||||
|
||||
secretDelim = "_"
|
||||
)
|
||||
|
||||
type Git struct {
|
||||
|
|
@ -27,17 +35,82 @@ type Git struct {
|
|||
cl *http.Client
|
||||
}
|
||||
|
||||
func (g *Git) getSecret(host string) (username, password string, err error) {
|
||||
sec, err := g.sec.Get(secretName, metav1.GetOptions{})
|
||||
func makeKeys(host, owner, repo string) []string {
|
||||
return []string{
|
||||
host + secretDelim + owner + secretDelim + repo,
|
||||
host + secretDelim + owner,
|
||||
host,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Git) getValidateSecret(host, owner, repo string) (key []byte, ok bool) {
|
||||
sec, err := g.sec.Get(validateSecretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s, ok := sec.Data[host]
|
||||
for _, v := range makeKeys(host, owner, repo) {
|
||||
key, ok = sec.Data[v]
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Git) validate(r *http.Request, payload []byte, repo Repository) (valid bool) {
|
||||
var (
|
||||
h hash.Hash
|
||||
sig []byte
|
||||
err error
|
||||
)
|
||||
u, err := url.Parse(repo.CloneURL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
key, ok := g.getValidateSecret(u.Host, repo.Owner.Login, repo.Name)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
api := detectAPI(r)
|
||||
switch api {
|
||||
case giteaAPI:
|
||||
sig, err = hex.DecodeString(r.Header.Get("X-Gitea-Signature"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
h = hmac.New(sha256.New, key)
|
||||
case githubAPI:
|
||||
sig, err = hex.DecodeString(strings.TrimPrefix(r.Header.Get("X-Hub-Signature"), "sha1="))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
h = hmac.New(sha1.New, key)
|
||||
}
|
||||
h.Write(payload)
|
||||
return hmac.Equal(h.Sum(nil), sig)
|
||||
|
||||
}
|
||||
|
||||
func (g *Git) getAuthSecret(host, owner, repo string) (username, password string, err error) {
|
||||
sec, err := g.sec.Get(authSecretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var (
|
||||
secret []byte
|
||||
ok bool
|
||||
)
|
||||
for _, v := range makeKeys(host, owner, repo) {
|
||||
secret, ok = sec.Data[v]
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
err = fmt.Errorf("no key found for host %s in secret %s/%s", host, sec.Namespace, sec.Name)
|
||||
return
|
||||
}
|
||||
a := strings.SplitN(string(s), ":", 2)
|
||||
a := strings.SplitN(string(secret), ":", 2)
|
||||
if len(a) < 2 {
|
||||
err = fmt.Errorf("missing credentials for host %s in secret %s/%s", host, sec.Namespace, sec.Name)
|
||||
return
|
||||
|
|
@ -47,8 +120,8 @@ func (g *Git) getSecret(host string) (username, password string, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (g *Git) send(req *http.Request) (*http.Response, error) {
|
||||
user, pass, err := g.getSecret(req.URL.Host)
|
||||
func (g *Git) send(req *http.Request, owner, repo string) (*http.Response, error) {
|
||||
user, pass, err := g.getAuthSecret(req.URL.Host, owner, repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -69,7 +142,7 @@ func (g *Git) getPullRequest(repo Repository, api, id string) (pr PullRequest, e
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
resp, err := g.send(req)
|
||||
resp, err := g.send(req, repo.Owner.Login, repo.Name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -97,7 +170,7 @@ func (g *Git) addLabel(repo Repository, api, label, color string) (labelID int64
|
|||
return
|
||||
}
|
||||
|
||||
resp, err := g.send(req)
|
||||
resp, err := g.send(req, repo.Owner.Login, repo.Name)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error getting labels of %s: %s", u, err)
|
||||
return
|
||||
|
|
@ -121,6 +194,9 @@ func (g *Git) addLabel(repo Repository, api, label, color string) (labelID int64
|
|||
return
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
if api == githubAPI {
|
||||
color = strings.TrimPrefix(color, "#")
|
||||
}
|
||||
l := &Label{Name: label, Color: color}
|
||||
err = json.NewEncoder(buf).Encode(l)
|
||||
if err != nil {
|
||||
|
|
@ -131,7 +207,7 @@ func (g *Git) addLabel(repo Repository, api, label, color string) (labelID int64
|
|||
return
|
||||
}
|
||||
|
||||
resp, err = g.send(req)
|
||||
resp, err = g.send(req, repo.Owner.Login, repo.Name)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error creating label %s for %s: %s", label, u, err)
|
||||
return
|
||||
|
|
@ -162,7 +238,7 @@ func (g *Git) setLabel(repo Repository, api, id, label, color string, remove []s
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
resp, err := g.send(req)
|
||||
resp, err := g.send(req, repo.Owner.Login, repo.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting issue labels of %s: %s", u, err)
|
||||
}
|
||||
|
|
@ -207,7 +283,7 @@ func (g *Git) setLabel(repo Repository, api, id, label, color string, remove []s
|
|||
return
|
||||
}
|
||||
|
||||
resp, err = g.send(req)
|
||||
resp, err = g.send(req, repo.Owner.Login, repo.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting issue label %s for %s: %s", label, u, err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue