diff --git a/config.json b/config.json index 2a3f74b..7cb906e 100644 --- a/config.json +++ b/config.json @@ -1,4 +1,5 @@ { + "ListenAddress": "127.0.0.1:8654", "Services": { "test": { "Script": "./example.sh", diff --git a/main.go b/main.go index 51aa47e..5b0e3cb 100644 --- a/main.go +++ b/main.go @@ -18,14 +18,20 @@ import ( var config_filename = "/etc/gohookr.json" var checkSignature = true +var config Config func main() { r := mux.NewRouter() r.HandleFunc("/webhooks/{service}", webhookHandler) - port := ":80" - if p, ok := os.LookupEnv("PORT"); ok { - port = fmt.Sprintf(":%v", p) + raw_config, err := ioutil.ReadFile(config_filename) + if err != nil { + panic(err.Error()) + } + config = Config{} + json.Unmarshal(raw_config, &config) + if err := config.Validate(); err != nil { + panic(err.Error()) } if p, ok := os.LookupEnv("CONFIG"); ok { @@ -36,7 +42,7 @@ func main() { checkSignature = p != "true" } - log.Fatal(http.ListenAndServe(port, r)) + log.Fatal(http.ListenAndServe(config.ListenAddress, r)) } func webhookHandler(w http.ResponseWriter, r *http.Request) { @@ -48,14 +54,6 @@ func webhookHandler(w http.ResponseWriter, r *http.Request) { payload = string(p) } - raw_config, err := ioutil.ReadFile(config_filename) - if err != nil { - writeResponse(w, 500, "Internal Server Error: Could not open config file") - return - } - config := Config{} - json.Unmarshal(raw_config, &config) - // check what service is specified in URL (/webhooks/{service}) and if it exists service, ok := config.Services[string(mux.Vars(r)["service"])] if !ok { @@ -68,7 +66,7 @@ func webhookHandler(w http.ResponseWriter, r *http.Request) { calculatedSignature := getSha256HMACSignature([]byte(service.Secret), payload) fmt.Printf("signature = %v\n", signature) fmt.Printf("calcuatedSignature = %v\n", signature) - if signature != calculatedSignature && checkSignature{ + if signature != calculatedSignature && checkSignature { writeResponse(w, 400, "Bad Request: Signatures do not match") return } @@ -103,13 +101,32 @@ func getSha256HMACSignature(secret []byte, data string) string { return hex.EncodeToString(h.Sum(nil)) } +func (c Config) Validate() error { + if c.ListenAddress == "" { + return requiredFieldError{"ListenAddress", ""} + } + + for serviceName, service := range c.Services { + if service.Script == "" { + return requiredFieldError{"Script", serviceName} + } + if service.SignatureHeader == "" { + return requiredFieldError{"SignatureHeader", serviceName} + } + if service.Secret == "" { + return requiredFieldError{"Secret", serviceName} + } + } + + return nil +} + type Test struct { - Command string + Command string Arguments []string } type Service struct { - Gitea bool Script string Secret string SignatureHeader string @@ -117,5 +134,15 @@ type Service struct { } type Config struct { - Services map[string]Service + ListenAddress string + Services map[string]Service +} + +type requiredFieldError struct { + fieldName string + serviceName string +} + +func (e requiredFieldError) Error() string { + return fmt.Sprintf("%v cannot be empty (%v)", e.fieldName, e.serviceName) } diff --git a/readme.md b/readme.md index 412c515..fbce155 100644 --- a/readme.md +++ b/readme.md @@ -1,8 +1,6 @@ # gohookr -A _really_ simple webhook receiver, which listens at `0.0.0.0:/webhooks/`. - -Default port is 80 and can be overriden by setting the environment variable `PORT`. +A _really_ simple webhook receiver, which listens at `/webhooks/`. Default config path is `/etc/gohookr.conf` and can be overriden by setting environment variable `CONFIG`. @@ -46,10 +44,15 @@ real tests are run can simply be put before the tests. ## Example Config +Required config keys are `ListenAddress` and `Services`. + +Requried keys per service are `Script`, `Secret`, `SignatureHeader`. + An example config file can be found [here](./config.json) but also below: ```json { + "ListenAddress": "127.0.0.1:8654", "Services": { "test": { "Script": "./example.sh",