feat: add middleware to verify gitea webhooks

This commit is contained in:
Derrick Hammer 2024-02-11 00:23:46 -05:00
parent 5ac86cc75c
commit e8af456476
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
2 changed files with 58 additions and 7 deletions

View File

@ -1,11 +1,16 @@
package api package api
import ( import (
"bytes"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"context" "context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"git.lumeweb.com/LumeWeb/gitea-github-proxy/config" "git.lumeweb.com/LumeWeb/gitea-github-proxy/config"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"go.uber.org/zap" "go.uber.org/zap"
"io"
"net/http" "net/http"
"strings" "strings"
) )
@ -86,6 +91,51 @@ func loggingMiddleware(logger *zap.Logger) mux.MiddlewareFunc {
} }
} }
func giteaVerifyWebhookMiddleware(cfg *config.Config, logger *zap.Logger) mux.MiddlewareFunc {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
signature := r.Header.Get("X-Gitea-Signature")
if signature == "" {
http.Error(w, "No signature provided", http.StatusBadRequest)
return
}
decodedSignature, err := hex.DecodeString(signature)
if err != nil {
http.Error(w, "Error decoding signature", http.StatusBadRequest)
logger.Error("Error decoding signature", zap.Error(err))
return
}
payload, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error reading body", http.StatusBadRequest)
logger.Error("Error reading body", zap.Error(err))
return
}
defer func(body io.ReadCloser) {
err := body.Close()
if err != nil {
logger.Error("Error closing body", zap.Error(err))
}
}(r.Body)
r.Body = io.NopCloser(bytes.NewBuffer(payload))
mac := hmac.New(sha256.New, []byte(cfg.GiteaWebHookSecret))
mac.Write(payload)
expectedMAC := mac.Sum(nil)
if !hmac.Equal(decodedSignature, expectedMAC) {
http.Error(w, "Invalid signature", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
}
func addAuthStatusToRequestServ(status bool, r *http.Request, w http.ResponseWriter, next http.Handler) { func addAuthStatusToRequestServ(status bool, r *http.Request, w http.ResponseWriter, next http.Handler) {
ctx := context.WithValue(r.Context(), AUTHED_CONTEXT_KEY, status) ctx := context.WithValue(r.Context(), AUTHED_CONTEXT_KEY, status)
r = r.WithContext(ctx) r = r.WithContext(ctx)

View File

@ -31,13 +31,14 @@ func (p *PrivateKey) UnmarshalText(text []byte) error {
} }
type Config struct { type Config struct {
Host string `mapstructure:"host"` Host string `mapstructure:"host"`
Port int `mapstructure:"port"` Port int `mapstructure:"port"`
GiteaUrl string `mapstructure:"gitea_url"` GiteaUrl string `mapstructure:"gitea_url"`
DbPath string `mapstructure:"db_path"` DbPath string `mapstructure:"db_path"`
Oauth OauthConfig Oauth OauthConfig
JwtPrivateKey PrivateKey JwtPrivateKey PrivateKey
Domain string `mapstructure:"domain"` Domain string `mapstructure:"domain"`
GiteaWebHookSecret string `mapstructure:"gitea_webhook_secret"`
} }
type OauthConfig struct { type OauthConfig struct {