Compare commits

..

8 Commits

Author SHA1 Message Date
b4d9935e39 Add example config to readme 2021-07-29 07:44:14 +01:00
4691ea5cc3 Rewrite how tests are defined 2021-07-29 07:42:02 +01:00
a82726ca52 Update readme.md 2021-07-29 07:37:25 +01:00
c99a5943aa Only execute if all tests are successful 2021-07-29 07:37:25 +01:00
c0f0cc6c60 Clean up code 2021-07-29 07:37:25 +01:00
242ae06cad Remove testing related code 2021-07-29 07:37:25 +01:00
cd2c0ff0fe Add ability to disable signature verification 2021-07-29 07:37:25 +01:00
9fca2cefa3 Fix module name 2021-07-29 07:37:18 +01:00
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" ]
}
]
}
}
}
```