github.com/crowdsecurity/crowdsec@v1.6.1/cmd/notification-sentinel/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "crypto/hmac" 6 "crypto/sha256" 7 "encoding/base64" 8 "fmt" 9 "net/http" 10 "os" 11 "strings" 12 "time" 13 14 "github.com/crowdsecurity/crowdsec/pkg/protobufs" 15 "github.com/hashicorp/go-hclog" 16 "github.com/hashicorp/go-plugin" 17 "gopkg.in/yaml.v3" 18 ) 19 20 type PluginConfig struct { 21 Name string `yaml:"name"` 22 CustomerID string `yaml:"customer_id"` 23 SharedKey string `yaml:"shared_key"` 24 LogType string `yaml:"log_type"` 25 LogLevel *string `yaml:"log_level"` 26 } 27 28 type SentinelPlugin struct { 29 PluginConfigByName map[string]PluginConfig 30 } 31 32 var logger hclog.Logger = hclog.New(&hclog.LoggerOptions{ 33 Name: "sentinel-plugin", 34 Level: hclog.LevelFromString("INFO"), 35 Output: os.Stderr, 36 JSONFormat: true, 37 }) 38 39 func (s *SentinelPlugin) getAuthorizationHeader(now string, length int, pluginName string) (string, error) { 40 xHeaders := "x-ms-date:" + now 41 42 stringToHash := fmt.Sprintf("POST\n%d\napplication/json\n%s\n/api/logs", length, xHeaders) 43 decodedKey, _ := base64.StdEncoding.DecodeString(s.PluginConfigByName[pluginName].SharedKey) 44 45 h := hmac.New(sha256.New, decodedKey) 46 h.Write([]byte(stringToHash)) 47 48 encodedHash := base64.StdEncoding.EncodeToString(h.Sum(nil)) 49 authorization := "SharedKey " + s.PluginConfigByName[pluginName].CustomerID + ":" + encodedHash 50 51 logger.Trace("authorization header", "header", authorization) 52 53 return authorization, nil 54 } 55 56 func (s *SentinelPlugin) Notify(ctx context.Context, notification *protobufs.Notification) (*protobufs.Empty, error) { 57 58 if _, ok := s.PluginConfigByName[notification.Name]; !ok { 59 return nil, fmt.Errorf("invalid plugin config name %s", notification.Name) 60 } 61 cfg := s.PluginConfigByName[notification.Name] 62 63 if cfg.LogLevel != nil && *cfg.LogLevel != "" { 64 logger.SetLevel(hclog.LevelFromString(*cfg.LogLevel)) 65 } 66 67 logger.Info("received notification for sentinel config", "name", notification.Name) 68 69 url := fmt.Sprintf("https://%s.ods.opinsights.azure.com/api/logs?api-version=2016-04-01", s.PluginConfigByName[notification.Name].CustomerID) 70 body := strings.NewReader(notification.Text) 71 72 //Cannot use time.RFC1123 as azure wants GMT, not UTC 73 now := time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT") 74 75 authorization, err := s.getAuthorizationHeader(now, len(notification.Text), notification.Name) 76 77 if err != nil { 78 return &protobufs.Empty{}, err 79 } 80 81 req, err := http.NewRequest(http.MethodPost, url, body) 82 if err != nil { 83 logger.Error("failed to create request", "error", err) 84 return &protobufs.Empty{}, err 85 } 86 87 req.Header.Set("Content-Type", "application/json") 88 req.Header.Set("Log-Type", s.PluginConfigByName[notification.Name].LogType) 89 req.Header.Set("Authorization", authorization) 90 req.Header.Set("x-ms-date", now) 91 92 client := &http.Client{} 93 resp, err := client.Do(req.WithContext(ctx)) 94 if err != nil { 95 logger.Error("failed to send request", "error", err) 96 return &protobufs.Empty{}, err 97 } 98 defer resp.Body.Close() 99 logger.Debug("sent notification to sentinel", "status", resp.Status) 100 101 if resp.StatusCode != http.StatusOK { 102 return &protobufs.Empty{}, fmt.Errorf("failed to send notification to sentinel: %s", resp.Status) 103 } 104 105 return &protobufs.Empty{}, nil 106 } 107 108 func (s *SentinelPlugin) Configure(ctx context.Context, config *protobufs.Config) (*protobufs.Empty, error) { 109 d := PluginConfig{} 110 err := yaml.Unmarshal(config.Config, &d) 111 s.PluginConfigByName[d.Name] = d 112 return &protobufs.Empty{}, err 113 } 114 115 func main() { 116 var handshake = plugin.HandshakeConfig{ 117 ProtocolVersion: 1, 118 MagicCookieKey: "CROWDSEC_PLUGIN_KEY", 119 MagicCookieValue: os.Getenv("CROWDSEC_PLUGIN_KEY"), 120 } 121 122 sp := &SentinelPlugin{PluginConfigByName: make(map[string]PluginConfig)} 123 plugin.Serve(&plugin.ServeConfig{ 124 HandshakeConfig: handshake, 125 Plugins: map[string]plugin.Plugin{ 126 "sentinel": &protobufs.NotifierPlugin{ 127 Impl: sp, 128 }, 129 }, 130 GRPCServer: plugin.DefaultGRPCServer, 131 Logger: logger, 132 }) 133 }