Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sign webhooks with optional HMAC #140

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions devconf.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
zmqport = 28332
rpchost = "127.0.0.1"
rpcport = 22555
rpcpass = "suchpay"
rpcuser = "suchpay"
rpcpass = "gigawallet"
rpcuser = "gigawallet"

## Setup loggers, see pkg/config.go LoggersConfig
[loggers.events]
Expand All @@ -41,6 +41,8 @@
#[callbacks.example1]
# path = "https://example.com/invoiceEvents"
# types = ["INV"]
# # Optional: Add HMAC secret for request signing
# # hmacsecret = "your-secret-key-here"


## Setup MQTT event publishing, see pkg/config.go MQTTConfig
Expand Down
5 changes: 3 additions & 2 deletions pkg/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ type LoggersConfig struct {
}

type CallbackConfig struct {
Path string
Types []string
Path string
Types []string
HMACSecret string
}

type MQTTConfig struct {
Expand Down
39 changes: 31 additions & 8 deletions pkg/receivers/callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package receivers
import (
"bytes"
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
Expand All @@ -12,20 +15,22 @@ import (
"github.com/dogecoinfoundation/gigawallet/pkg/conductor"
)

func NewCallbackSender(path string, bus giga.MessageBus) CallbackSender {
func NewCallbackSender(config giga.CallbackConfig, bus giga.MessageBus) CallbackSender {
// create a MessageLogger
return CallbackSender{
make(chan giga.Message, 1000),
path,
config.Path,
config.HMACSecret,
bus,
}
}

type CallbackSender struct {
// incomming msgs
Rec chan giga.Message
Path string
Bus giga.MessageBus
Rec chan giga.Message
Path string
HMACSecret string
Bus giga.MessageBus
}

// Implements giga.MessageSubscriber
Expand All @@ -45,7 +50,7 @@ func (s CallbackSender) Run(started, stopped chan bool, stop chan context.Contex
close(stopped)
return
case msg := <-s.Rec:
err := postWithRetry(s.Path, msg, s.Bus)
err := postWithRetry(s, msg)
if err != nil {
s.Bus.Send(giga.SYS_ERR, fmt.Sprintf("CallbackSender: %v", msg))
}
Expand All @@ -58,7 +63,7 @@ func (s CallbackSender) Run(started, stopped chan bool, stop chan context.Contex
// Reads config and sets up any configured callbacks
func SetupCallbacks(cond *conductor.Conductor, bus giga.MessageBus, conf giga.Config) {
for name, c := range conf.Callbacks {
s := NewCallbackSender(c.Path, bus)
s := NewCallbackSender(c, bus)
cond.Service(fmt.Sprintf("Callback sender for: %s", c.Path), s)

types := []giga.EventType{}
Expand All @@ -78,7 +83,19 @@ func SetupCallbacks(cond *conductor.Conductor, bus giga.MessageBus, conf giga.Co
}
}

func postWithRetry(path string, msg giga.Message, bus giga.MessageBus) error {
func generateSha256HMAC(payload []byte, secret string) string {
if secret == "" {
return ""
}
h := hmac.New(sha256.New, []byte(secret))
h.Write(payload)
return hex.EncodeToString(h.Sum(nil))
}

func postWithRetry(sender CallbackSender, msg giga.Message) error {
path := sender.Path
bus := sender.Bus

maxRetries := 6
initialDelay := 1 * time.Second
maxDelay := 32 * time.Second
Expand All @@ -98,6 +115,12 @@ func postWithRetry(path string, msg giga.Message, bus giga.MessageBus) error {
client := &http.Client{Timeout: 30 * time.Second}

go func() {
if sender.HMACSecret != "" {
signature := generateSha256HMAC(objJSON, sender.HMACSecret)
req.Header.Set("X-Giga-Signature", fmt.Sprintf("sha256=%s", signature))
req.Header.Set("X-Giga-Timestamp", fmt.Sprintf("%d", time.Now().Unix()))
}

retryCount := 0
delay := initialDelay

Expand Down