main.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. package main
  2. import (
  3. "bufio"
  4. "bytes"
  5. "crypto/x509"
  6. "encoding/pem"
  7. "flag"
  8. "fmt"
  9. "io"
  10. "log"
  11. "net/http"
  12. "net/http/httputil"
  13. "net/url"
  14. "os"
  15. "strings"
  16. "time"
  17. "github.com/go-fed/httpsig"
  18. configflag "github.com/ncarlier/webhookd/pkg/config/flag"
  19. )
  20. type config struct {
  21. KeyID string `flag:"key-id" desc:"Signature key ID"`
  22. KeyFile string `flag:"key-file" desc:"Private key file (PEM format)" default:"./key.pem"`
  23. JSON string `flag:"json" desc:"JSON payload"`
  24. }
  25. func main() {
  26. conf := &config{}
  27. configflag.Bind(conf, "HTTP_SIG")
  28. flag.Parse()
  29. if conf.KeyID == "" {
  30. log.Fatal("missing key ID")
  31. }
  32. args := flag.Args()
  33. if len(args) <= 0 {
  34. log.Fatal("missing target URL")
  35. }
  36. targetURL := args[0]
  37. if _, err := url.Parse(targetURL); err != nil {
  38. log.Fatal("invalid target URL")
  39. }
  40. keyBytes, err := os.ReadFile(conf.KeyFile)
  41. if err != nil {
  42. log.Fatal(err.Error())
  43. }
  44. pemBlock, _ := pem.Decode(keyBytes)
  45. if pemBlock == nil {
  46. log.Fatal("invalid PEM format")
  47. }
  48. privateKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
  49. if err != nil {
  50. log.Fatal(err.Error())
  51. }
  52. var payload io.Reader
  53. var jsonBytes []byte
  54. if conf.JSON != "" {
  55. var err error
  56. jsonBytes, err = os.ReadFile(conf.JSON)
  57. if err != nil {
  58. log.Fatal(err.Error())
  59. }
  60. payload = bytes.NewReader(jsonBytes)
  61. }
  62. prefs := []httpsig.Algorithm{httpsig.RSA_SHA256}
  63. digestAlgorithm := httpsig.DigestSha256
  64. headers := []string{httpsig.RequestTarget, "date"}
  65. signer, _, err := httpsig.NewSigner(prefs, digestAlgorithm, headers, httpsig.Signature, 0)
  66. if err != nil {
  67. log.Fatal(err.Error())
  68. }
  69. req, err := http.NewRequest("POST", targetURL, payload)
  70. if err != nil {
  71. log.Fatal(err.Error())
  72. }
  73. if payload != nil {
  74. req.Header.Add("content-type", "application/json")
  75. }
  76. req.Header.Add("date", time.Now().UTC().Format(http.TimeFormat))
  77. if err = signer.SignRequest(privateKey, conf.KeyID, req, jsonBytes); err != nil {
  78. log.Fatal(err.Error())
  79. }
  80. dump, err := httputil.DumpRequest(req, true)
  81. if err != nil {
  82. log.Fatal(err.Error())
  83. }
  84. scanner := bufio.NewScanner(strings.NewReader(string(dump)))
  85. for scanner.Scan() {
  86. fmt.Println(">", scanner.Text())
  87. }
  88. client := &http.Client{Timeout: 10 * time.Second}
  89. res, err := client.Do(req)
  90. if err != nil {
  91. log.Fatal(err.Error())
  92. }
  93. dump, err = httputil.DumpResponse(res, true)
  94. if err != nil {
  95. log.Fatal(err.Error())
  96. }
  97. scanner = bufio.NewScanner(strings.NewReader(string(dump)))
  98. for scanner.Scan() {
  99. fmt.Println("<", scanner.Text())
  100. }
  101. }