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  }