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