gohookr/main.go

142 lines
3.4 KiB
Go
Raw Normal View History

2021-07-28 15:20:06 +00:00
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
2022-01-21 21:08:05 +00:00
"git.alv.cx/alvierahman90/gohookr/config"
2021-07-28 15:20:06 +00:00
"github.com/gorilla/mux"
)
var config_filename = "/etc/gohookr.json"
2021-07-29 07:29:37 +00:00
var checkSignature = true
2021-07-28 15:20:06 +00:00
func main() {
2022-02-12 14:07:11 +00:00
var c config.Config
2021-07-28 15:20:06 +00:00
r := mux.NewRouter()
r.HandleFunc("/webhooks/{service}", webhookHandler)
2021-07-28 15:20:06 +00:00
if p, ok := os.LookupEnv("CONFIG"); ok {
config_filename = p
}
if p, ok := os.LookupEnv("NO_SIGNATURE_CHECK"); ok {
checkSignature = p != "true"
}
2022-02-12 14:07:11 +00:00
c, err := loadConfig(config_filename)
if err != nil {
panic(err.Error())
}
2022-02-12 14:07:11 +00:00
fmt.Printf("CONFIG OK: %s\n", config_filename)
fmt.Printf("LISTENING AT: %s\n", c.ListenAddress)
for _, v := range os.Args {
if v == "checkConfig" {
return
}
}
log.Fatal(http.ListenAndServe(c.ListenAddress, r))
}
func loadConfig(config_filename string) (config.Config, error) {
var c config.Config
raw_config, err := ioutil.ReadFile(config_filename)
if err != nil {
return config.Config{}, err
}
2021-08-04 11:04:01 +00:00
2021-09-01 17:57:21 +00:00
if err := json.Unmarshal(raw_config, &c); err != nil {
2022-02-12 14:07:11 +00:00
return config.Config{}, err
2021-09-01 17:57:21 +00:00
}
2021-08-04 11:04:01 +00:00
if err := c.Validate(); err != nil {
2022-02-12 14:07:11 +00:00
return config.Config{}, err
2021-07-28 15:20:06 +00:00
}
2022-02-12 14:07:11 +00:00
return c, nil
2021-07-28 15:20:06 +00:00
}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
2022-02-12 14:07:11 +00:00
c, err := loadConfig(config_filename)
if err != nil {
writeResponse(w, 500, "Unable to read config file")
}
2021-08-06 13:53:22 +00:00
// Check what service is specified in URL (/webhooks/{service}) and if it exists
serviceName := string(mux.Vars(r)["service"])
service, ok := c.Services[serviceName]
if !ok {
writeResponse(w, 404, "Service Not Found")
fmt.Printf("Service not found: %v\n", serviceName)
return
}
fmt.Printf("Got webhook for: %v\n", serviceName)
// Read payload or return 500 if that doesn't work out
2021-07-28 15:20:06 +00:00
payload := ""
if p, err := ioutil.ReadAll(r.Body); err != nil {
writeResponse(w, 500, "Internal Server Error: Could not read payload")
2021-08-06 13:53:22 +00:00
fmt.Println("Error: Could not read payload")
2021-07-28 15:20:06 +00:00
return
} else {
payload = string(p)
}
// Verify that signature provided matches signature calculated using secretsss
2021-07-28 15:20:06 +00:00
signature := r.Header.Get(service.SignatureHeader)
calculatedSignature := fmt.Sprintf(
"%v%v",
service.SignaturePrefix,
getSha256HMACSignature([]byte(service.Secret), payload),
)
fmt.Printf("signature = %v\n", signature)
fmt.Printf("calcuatedSignature = %v\n", calculatedSignature)
2021-08-14 00:06:29 +00:00
if checkSignature && !service.DisableSignatureVerification && signature != calculatedSignature {
2021-07-28 15:20:06 +00:00
writeResponse(w, 400, "Bad Request: Signatures do not match")
2021-08-06 13:53:22 +00:00
fmt.Println("Signatures do not match!")
2021-07-28 15:20:06 +00:00
return
}
2021-08-06 13:53:22 +00:00
// Run tests and script as goroutine to prevent timing out
2022-02-12 14:07:11 +00:00
go func() {
// Run tests, immediately stop if one fails
for _, test := range service.Tests {
if _, err := test.Execute(payload); err != nil {
2021-08-06 13:53:22 +00:00
fmt.Printf("Test failed(%v) for service %v\n", test, serviceName)
return
}
}
stdout, err := service.Script.Execute(payload)
fmt.Println(string(stdout))
if err != nil {
fmt.Println(err.Error())
}
}()
writeResponse(w, 200, "OK")
return
2021-07-28 15:20:06 +00:00
}
func writeResponse(w http.ResponseWriter, responseCode int, responseString string) {
w.WriteHeader(responseCode)
w.Write([]byte(fmt.Sprintf("%v %v", responseCode, responseString)))
}
func getSha256HMACSignature(secret []byte, data string) string {
h := hmac.New(sha256.New, secret)
io.WriteString(h, data)
return hex.EncodeToString(h.Sum(nil))
}