Compare commits

..

8 Commits

4 changed files with 79 additions and 22 deletions

View File

@ -3,7 +3,13 @@
"test": {
"Script": "./example.sh",
"Secret": "THISISVERYSECRET",
"SignatureHeader": "X-Gitea-Signature"
"SignatureHeader": "X-Gitea-Signature",
"Tests": [
{
"Command": "git",
"Arguments": [ "pull" ]
}
]
}
}
}

2
go.mod
View File

@ -1,4 +1,4 @@
module git.alra.uk/alvierahman90/ghookr
module git.alra.uk/alvierahman90/gohookr
go 1.16

47
main.go
View File

@ -17,18 +17,9 @@ import (
)
var config_filename = "/etc/ghookr.json"
var noSignatureCheck = false
func main() {
// Used for testing purposes... generates hmac string
if os.Getenv("HMACGEN") == "true" {
input, err := ioutil.ReadAll(os.Stdin)
secret := os.Getenv("SECRET")
if err != nil {
panic(err.Error())
}
fmt.Println(getSha256HMACSignature([]byte(secret), string(input)))
return
}
r := mux.NewRouter()
r.HandleFunc("/webhook/{service}", webhook)
@ -41,14 +32,14 @@ func main() {
config_filename = p
}
if p, ok := os.LookupEnv("NO_SIGNATURE_CHECK"); ok {
noSignatureCheck = p == "true"
}
log.Fatal(http.ListenAndServe(port, r))
}
func webhook(w http.ResponseWriter, r *http.Request) {
// TODO run any specified tests before running script
service_name := mux.Vars(r)["service"]
payload := ""
if p, err := ioutil.ReadAll(r.Body); err != nil {
writeResponse(w, 500, "Internal Server Error: Could not read payload")
@ -65,20 +56,31 @@ func webhook(w http.ResponseWriter, r *http.Request) {
config := Config{}
json.Unmarshal(raw_config, &config)
var service = Service{}
if val, ok := config.Services[string(service_name)]; !ok {
// check what service is specified in URL (/webhook/{service}) and if it exists
service, ok := config.Services[string(mux.Vars(r)["service"])]
if !ok {
writeResponse(w, 404, "Service Not Found")
return
} else {
service = val
}
// Verify that signature provided matches signature calculated using secretsss
signature := r.Header.Get(service.SignatureHeader)
if signature == getSha256HMACSignature([]byte(service.Secret), payload) {
calculatedSignature := getSha256HMACSignature([]byte(service.Secret), payload)
if noSignatureCheck || signature == calculatedSignature {
writeResponse(w, 400, "Bad Request: Signatures do not match")
return
}
// Run tests, immediately stop if one fails
for _, test := range service.Tests {
if _, err := exec.Command(test.Command, test.Arguments...).Output(); err != nil {
writeResponse(w, 409,
fmt.Sprintf("409 Conflict: Test failed: %v", err.Error()),
)
return
}
}
if stdout, err := exec.Command(service.Script, payload).Output(); err != nil {
writeResponse(w, 500, err.Error())
return
@ -99,12 +101,17 @@ func getSha256HMACSignature(secret []byte, data string) string {
return hex.EncodeToString(h.Sum(nil))
}
type Test struct {
Command string
Arguments []string
}
type Service struct {
Gitea bool
Script string
Secret string
SignatureHeader string
Tests []string
Tests []Test
}
type Config struct {

View File

@ -5,3 +5,47 @@ A _really_ simple webhook receiver.
Check config.json for an example configuration.
Default config path is `/etc/ghookr.conf`, can be overriden with `CONFIG` environment variable.
## Signature Verification
Signature verificaiton is done using SHA256 HMACs.
You **must** set which HTTP header gohookr will receive a signature from using the `SignatureHeader`
key for each service.
You should also specify a shared secret in the `Secret` key.
### Disable Signature Verification
You can disable signature verification altogether by setting environment variable `NO_SIGNATURE_VERIFICATION`
to `true`.
## Tests
gohookr can run test before running your script.
Tests must be in the form of bash scripts.
A non-zero return code is considered a fail and gohookr will run no further tests and will not
deploy.
Tests are run in the order they're listed so any actions that need to be done before
tests are run can simply be put before the tests.
## Example Config
An example config file can be found [here](./config.json) but also below:
```json
{
"Services": {
"test": {
"Script": "./example.sh",
"Secret": "THISISVERYSECRET",
"SignatureHeader": "X-Gitea-Signature",
"Tests": [
{
"Command": "git",
"Arguments": [ "pull" ]
}
]
}
}
}
```