github.com/crowdsecurity/crowdsec@v1.6.1/cmd/notification-email/main.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"time"
     8  
     9  	"github.com/crowdsecurity/crowdsec/pkg/protobufs"
    10  	"github.com/hashicorp/go-hclog"
    11  	plugin "github.com/hashicorp/go-plugin"
    12  	mail "github.com/xhit/go-simple-mail/v2"
    13  	"gopkg.in/yaml.v2"
    14  )
    15  
    16  var baseLogger hclog.Logger = hclog.New(&hclog.LoggerOptions{
    17  	Name:       "email-plugin",
    18  	Level:      hclog.LevelFromString("INFO"),
    19  	Output:     os.Stderr,
    20  	JSONFormat: true,
    21  })
    22  
    23  var AuthStringToType map[string]mail.AuthType = map[string]mail.AuthType{
    24  	"none":    mail.AuthNone,
    25  	"crammd5": mail.AuthCRAMMD5,
    26  	"login":   mail.AuthLogin,
    27  	"plain":   mail.AuthPlain,
    28  }
    29  
    30  var EncryptionStringToType map[string]mail.Encryption = map[string]mail.Encryption{
    31  	"ssltls":   mail.EncryptionSSLTLS,
    32  	"starttls": mail.EncryptionSTARTTLS,
    33  	"none":     mail.EncryptionNone,
    34  }
    35  
    36  type PluginConfig struct {
    37  	Name     string  `yaml:"name"`
    38  	LogLevel *string `yaml:"log_level"`
    39  
    40  	SMTPHost       string   `yaml:"smtp_host"`
    41  	SMTPPort       int      `yaml:"smtp_port"`
    42  	SMTPUsername   string   `yaml:"smtp_username"`
    43  	SMTPPassword   string   `yaml:"smtp_password"`
    44  	SenderEmail    string   `yaml:"sender_email"`
    45  	SenderName     string   `yaml:"sender_name"`
    46  	ReceiverEmails []string `yaml:"receiver_emails"`
    47  	EmailSubject   string   `yaml:"email_subject"`
    48  	EncryptionType string   `yaml:"encryption_type"`
    49  	AuthType       string   `yaml:"auth_type"`
    50  	HeloHost       string   `yaml:"helo_host"`
    51  	ConnectTimeout string   `yaml:"connect_timeout"`
    52  	SendTimeout    string   `yaml:"send_timeout"`
    53  }
    54  
    55  type EmailPlugin struct {
    56  	ConfigByName map[string]PluginConfig
    57  }
    58  
    59  func (n *EmailPlugin) Configure(ctx context.Context, config *protobufs.Config) (*protobufs.Empty, error) {
    60  	d := PluginConfig{
    61  		SMTPPort:       25,
    62  		SenderName:     "Crowdsec",
    63  		EmailSubject:   "Crowdsec notification",
    64  		EncryptionType: "ssltls",
    65  		AuthType:       "login",
    66  		SenderEmail:    "crowdsec@crowdsec.local",
    67  		HeloHost:	"localhost",
    68  	}
    69  
    70  	if err := yaml.Unmarshal(config.Config, &d); err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	if d.Name == "" {
    75  		return nil, fmt.Errorf("name is required")
    76  	}
    77  
    78  	if d.SMTPHost == "" {
    79  		return nil, fmt.Errorf("SMTP host is not set")
    80  	}
    81  
    82  	if d.ReceiverEmails == nil || len(d.ReceiverEmails) == 0 {
    83  		return nil, fmt.Errorf("receiver emails are not set")
    84  	}
    85  
    86  	n.ConfigByName[d.Name] = d
    87  	baseLogger.Debug(fmt.Sprintf("Email plugin '%s' use SMTP host '%s:%d'", d.Name, d.SMTPHost, d.SMTPPort))
    88  	return &protobufs.Empty{}, nil
    89  }
    90  
    91  func (n *EmailPlugin) Notify(ctx context.Context, notification *protobufs.Notification) (*protobufs.Empty, error) {
    92  	if _, ok := n.ConfigByName[notification.Name]; !ok {
    93  		return nil, fmt.Errorf("invalid plugin config name %s", notification.Name)
    94  	}
    95  	cfg := n.ConfigByName[notification.Name]
    96  
    97  	logger := baseLogger.Named(cfg.Name)
    98  
    99  	if cfg.LogLevel != nil && *cfg.LogLevel != "" {
   100  		logger.SetLevel(hclog.LevelFromString(*cfg.LogLevel))
   101  	}
   102  
   103  	logger.Debug("got notification")
   104  
   105  	server := mail.NewSMTPClient()
   106  	server.Host = cfg.SMTPHost
   107  	server.Port = cfg.SMTPPort
   108  	server.Username = cfg.SMTPUsername
   109  	server.Password = cfg.SMTPPassword
   110  	server.Encryption = EncryptionStringToType[cfg.EncryptionType]
   111  	server.Authentication = AuthStringToType[cfg.AuthType]
   112  	server.Helo = cfg.HeloHost
   113  
   114  	var err error
   115  
   116  	if cfg.ConnectTimeout != "" {
   117  		server.ConnectTimeout, err = time.ParseDuration(cfg.ConnectTimeout)
   118  		if err != nil {
   119  			logger.Warn(fmt.Sprintf("invalid connect timeout '%s', using default '10s'", cfg.ConnectTimeout))
   120  			server.ConnectTimeout = 10 * time.Second
   121  		}
   122  	}
   123  
   124  	if cfg.SendTimeout != "" {
   125  		server.SendTimeout, err = time.ParseDuration(cfg.SendTimeout)
   126  		if err != nil {
   127  			logger.Warn(fmt.Sprintf("invalid send timeout '%s', using default '10s'", cfg.SendTimeout))
   128  			server.SendTimeout = 10 * time.Second
   129  		}
   130  	}
   131  
   132  	logger.Debug("making smtp connection")
   133  	smtpClient, err := server.Connect()
   134  	if err != nil {
   135  		return &protobufs.Empty{}, err
   136  	}
   137  	logger.Debug("smtp connection done")
   138  
   139  	email := mail.NewMSG()
   140  	email.SetFrom(fmt.Sprintf("%s <%s>", cfg.SenderName, cfg.SenderEmail)).
   141  		AddTo(cfg.ReceiverEmails...).
   142  		SetSubject(cfg.EmailSubject)
   143  	email.SetBody(mail.TextHTML, notification.Text)
   144  
   145  	err = email.Send(smtpClient)
   146  	if err != nil {
   147  		return &protobufs.Empty{}, err
   148  	}
   149  	logger.Info(fmt.Sprintf("sent email to %v", cfg.ReceiverEmails))
   150  	return &protobufs.Empty{}, nil
   151  }
   152  
   153  func main() {
   154  	var handshake = plugin.HandshakeConfig{
   155  		ProtocolVersion:  1,
   156  		MagicCookieKey:   "CROWDSEC_PLUGIN_KEY",
   157  		MagicCookieValue: os.Getenv("CROWDSEC_PLUGIN_KEY"),
   158  	}
   159  
   160  	plugin.Serve(&plugin.ServeConfig{
   161  		HandshakeConfig: handshake,
   162  		Plugins: map[string]plugin.Plugin{
   163  			"email": &protobufs.NotifierPlugin{
   164  				Impl: &EmailPlugin{ConfigByName: make(map[string]PluginConfig)},
   165  			},
   166  		},
   167  		GRPCServer: plugin.DefaultGRPCServer,
   168  		Logger:     baseLogger,
   169  	})
   170  }