main.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. package main
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "os"
  10. "strings"
  11. "github.com/crowdsecurity/crowdsec/pkg/protobufs"
  12. "github.com/hashicorp/go-hclog"
  13. plugin "github.com/hashicorp/go-plugin"
  14. "gopkg.in/yaml.v2"
  15. )
  16. var logger hclog.Logger = hclog.New(&hclog.LoggerOptions{
  17. Name: "splunk-plugin",
  18. Level: hclog.LevelFromString("INFO"),
  19. Output: os.Stderr,
  20. JSONFormat: true,
  21. })
  22. type PluginConfig struct {
  23. Name string `yaml:"name"`
  24. URL string `yaml:"url"`
  25. Token string `yaml:"token"`
  26. LogLevel *string `yaml:"log_level"`
  27. }
  28. type Splunk struct {
  29. PluginConfigByName map[string]PluginConfig
  30. Client http.Client
  31. }
  32. type Payload struct {
  33. Event string `json:"event"`
  34. }
  35. func (s *Splunk) Notify(ctx context.Context, notification *protobufs.Notification) (*protobufs.Empty, error) {
  36. if _, ok := s.PluginConfigByName[notification.Name]; !ok {
  37. return &protobufs.Empty{}, fmt.Errorf("splunk invalid config name %s", notification.Name)
  38. }
  39. cfg := s.PluginConfigByName[notification.Name]
  40. if cfg.LogLevel != nil && *cfg.LogLevel != "" {
  41. logger.SetLevel(hclog.LevelFromString(*cfg.LogLevel))
  42. }
  43. logger.Info(fmt.Sprintf("received notify signal for %s config", notification.Name))
  44. p := Payload{Event: notification.Text}
  45. data, err := json.Marshal(p)
  46. if err != nil {
  47. return &protobufs.Empty{}, err
  48. }
  49. req, err := http.NewRequest(http.MethodPost, cfg.URL, strings.NewReader(string(data)))
  50. if err != nil {
  51. return &protobufs.Empty{}, err
  52. }
  53. req.Header.Add("Authorization", fmt.Sprintf("Splunk %s", cfg.Token))
  54. logger.Debug(fmt.Sprintf("posting event %s to %s", string(data), req.URL))
  55. resp, err := s.Client.Do(req)
  56. if err != nil {
  57. return &protobufs.Empty{}, err
  58. }
  59. if resp.StatusCode != http.StatusOK {
  60. content, err := io.ReadAll(resp.Body)
  61. if err != nil {
  62. return &protobufs.Empty{}, fmt.Errorf("got non 200 response and failed to read error %s", err)
  63. }
  64. return &protobufs.Empty{}, fmt.Errorf("got non 200 response %s", string(content))
  65. }
  66. respData, err := io.ReadAll(resp.Body)
  67. if err != nil {
  68. return &protobufs.Empty{}, fmt.Errorf("failed to read response body got error %s", err)
  69. }
  70. logger.Debug(fmt.Sprintf("got response %s", string(respData)))
  71. return &protobufs.Empty{}, nil
  72. }
  73. func (s *Splunk) Configure(ctx context.Context, config *protobufs.Config) (*protobufs.Empty, error) {
  74. d := PluginConfig{}
  75. err := yaml.Unmarshal(config.Config, &d)
  76. s.PluginConfigByName[d.Name] = d
  77. logger.Debug(fmt.Sprintf("Splunk plugin '%s' use URL '%s'", d.Name, d.URL))
  78. return &protobufs.Empty{}, err
  79. }
  80. func main() {
  81. var handshake = plugin.HandshakeConfig{
  82. ProtocolVersion: 1,
  83. MagicCookieKey: "CROWDSEC_PLUGIN_KEY",
  84. MagicCookieValue: os.Getenv("CROWDSEC_PLUGIN_KEY"),
  85. }
  86. tr := &http.Transport{
  87. TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
  88. }
  89. client := &http.Client{Transport: tr}
  90. sp := &Splunk{PluginConfigByName: make(map[string]PluginConfig), Client: *client}
  91. plugin.Serve(&plugin.ServeConfig{
  92. HandshakeConfig: handshake,
  93. Plugins: map[string]plugin.Plugin{
  94. "splunk": &protobufs.NotifierPlugin{
  95. Impl: sp,
  96. },
  97. },
  98. GRPCServer: plugin.DefaultGRPCServer,
  99. Logger: logger,
  100. })
  101. }