mirror of https://github.com/ncarlier/webhookd
119 lines
2.5 KiB
Go
119 lines
2.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httputil"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/go-fed/httpsig"
|
|
configflag "github.com/ncarlier/webhookd/pkg/config/flag"
|
|
)
|
|
|
|
type config struct {
|
|
KeyID string `flag:"key-id" desc:"Signature key ID"`
|
|
KeyFile string `flag:"key-file" desc:"Private key file (PEM format)" default:"./key.pem"`
|
|
JSON string `flag:"json" desc:"JSON payload"`
|
|
}
|
|
|
|
func main() {
|
|
conf := &config{}
|
|
configflag.Bind(conf, "HTTP_SIG")
|
|
|
|
flag.Parse()
|
|
|
|
if conf.KeyID == "" {
|
|
log.Fatal("missing key ID")
|
|
}
|
|
|
|
args := flag.Args()
|
|
if len(args) <= 0 {
|
|
log.Fatal("missing target URL")
|
|
}
|
|
targetURL := args[0]
|
|
if _, err := url.Parse(targetURL); err != nil {
|
|
log.Fatal("invalid target URL")
|
|
}
|
|
|
|
keyBytes, err := os.ReadFile(conf.KeyFile)
|
|
if err != nil {
|
|
log.Fatal(err.Error())
|
|
}
|
|
|
|
pemBlock, _ := pem.Decode(keyBytes)
|
|
if pemBlock == nil {
|
|
log.Fatal("invalid PEM format")
|
|
}
|
|
|
|
privateKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
|
|
if err != nil {
|
|
log.Fatal(err.Error())
|
|
}
|
|
|
|
var payload io.Reader
|
|
var jsonBytes []byte
|
|
if conf.JSON != "" {
|
|
var err error
|
|
jsonBytes, err = os.ReadFile(conf.JSON)
|
|
if err != nil {
|
|
log.Fatal(err.Error())
|
|
}
|
|
payload = bytes.NewReader(jsonBytes)
|
|
}
|
|
|
|
prefs := []httpsig.Algorithm{httpsig.RSA_SHA256}
|
|
digestAlgorithm := httpsig.DigestSha256
|
|
headers := []string{httpsig.RequestTarget, "date"}
|
|
signer, _, err := httpsig.NewSigner(prefs, digestAlgorithm, headers, httpsig.Signature, 0)
|
|
if err != nil {
|
|
log.Fatal(err.Error())
|
|
}
|
|
|
|
req, err := http.NewRequest("POST", targetURL, payload)
|
|
if err != nil {
|
|
log.Fatal(err.Error())
|
|
}
|
|
if payload != nil {
|
|
req.Header.Add("content-type", "application/json")
|
|
}
|
|
req.Header.Add("date", time.Now().UTC().Format(http.TimeFormat))
|
|
|
|
if err = signer.SignRequest(privateKey, conf.KeyID, req, jsonBytes); err != nil {
|
|
log.Fatal(err.Error())
|
|
}
|
|
|
|
dump, err := httputil.DumpRequest(req, true)
|
|
if err != nil {
|
|
log.Fatal(err.Error())
|
|
}
|
|
scanner := bufio.NewScanner(strings.NewReader(string(dump)))
|
|
for scanner.Scan() {
|
|
fmt.Println(">", scanner.Text())
|
|
}
|
|
|
|
client := &http.Client{Timeout: 10 * time.Second}
|
|
res, err := client.Do(req)
|
|
if err != nil {
|
|
log.Fatal(err.Error())
|
|
}
|
|
|
|
dump, err = httputil.DumpResponse(res, true)
|
|
if err != nil {
|
|
log.Fatal(err.Error())
|
|
}
|
|
scanner = bufio.NewScanner(strings.NewReader(string(dump)))
|
|
for scanner.Scan() {
|
|
fmt.Println("<", scanner.Text())
|
|
}
|
|
}
|