From 3cb9dbdb21f6c98a1952c57704154cdc381fd257 Mon Sep 17 00:00:00 2001 From: mmetc <92726601+mmetc@users.noreply.github.com> Date: Fri, 29 Sep 2023 16:59:06 +0200 Subject: [PATCH] notification-email: configurable timeouts (#2465) * configurable timeouts * parse email timeouts as duration string * add helo_host to email.yaml * move html and body tags outside of the loops * added quotes to href=.., and formatting test --- cmd/notification-email/email.yaml | 14 +++- cmd/notification-email/main.go | 23 ++++++- go.mod | 2 +- go.sum | 4 +- ...0_http_plugin.bats => 70_plugin_http.bats} | 0 ...dummy_plugin.bats => 71_plugin_dummy.bats} | 0 test/bats/73_plugin_formatting.bats | 65 +++++++++++++++++++ 7 files changed, 102 insertions(+), 6 deletions(-) rename test/bats/{70_http_plugin.bats => 70_plugin_http.bats} (100%) rename test/bats/{71_dummy_plugin.bats => 71_plugin_dummy.bats} (100%) create mode 100644 test/bats/73_plugin_formatting.bats diff --git a/cmd/notification-email/email.yaml b/cmd/notification-email/email.yaml index 37ce2a982..512633c63 100644 --- a/cmd/notification-email/email.yaml +++ b/cmd/notification-email/email.yaml @@ -15,12 +15,14 @@ timeout: 20s # Time to wait for response from the plugin before conside # The following template receives a list of models.Alert objects # The output goes in the email message body format: | + {{range . -}} {{$alert := . -}} {{range .Decisions -}} -

{{.Value}} will get {{.Type}} for next {{.Duration}} for triggering {{.Scenario}} on machine {{$alert.MachineID}}.

CrowdSec CTI

+

{{.Value}} will get {{.Type}} for next {{.Duration}} for triggering {{.Scenario}} on machine {{$alert.MachineID}}.

CrowdSec CTI

{{end -}} {{end -}} + smtp_host: # example: smtp.gmail.com smtp_username: # Replace with your actual username @@ -35,7 +37,15 @@ receiver_emails: # - email2@gmail.com # One of "ssltls", "starttls", "none" -encryption_type: ssltls +encryption_type: "ssltls" + +# If you need to set the HELO hostname: +# helo_host: "localhost" + +# If the email server is hitting the default timeouts (10 seconds), you can increase them here +# +# connect_timeout: 10s +# send_timeout: 10s --- diff --git a/cmd/notification-email/main.go b/cmd/notification-email/main.go index ac09c1eef..789740156 100644 --- a/cmd/notification-email/main.go +++ b/cmd/notification-email/main.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "time" "github.com/crowdsecurity/crowdsec/pkg/protobufs" "github.com/hashicorp/go-hclog" @@ -47,6 +48,8 @@ type PluginConfig struct { EncryptionType string `yaml:"encryption_type"` AuthType string `yaml:"auth_type"` HeloHost string `yaml:"helo_host"` + ConnectTimeout string `yaml:"connect_timeout"` + SendTimeout string `yaml:"send_timeout"` } type EmailPlugin struct { @@ -77,7 +80,7 @@ func (n *EmailPlugin) Configure(ctx context.Context, config *protobufs.Config) ( } if d.ReceiverEmails == nil || len(d.ReceiverEmails) == 0 { - return nil, fmt.Errorf("Receiver emails are not set") + return nil, fmt.Errorf("receiver emails are not set") } n.ConfigByName[d.Name] = d @@ -108,6 +111,24 @@ func (n *EmailPlugin) Notify(ctx context.Context, notification *protobufs.Notifi server.Authentication = AuthStringToType[cfg.AuthType] server.Helo = cfg.HeloHost + var err error + + if cfg.ConnectTimeout != "" { + server.ConnectTimeout, err = time.ParseDuration(cfg.ConnectTimeout) + if err != nil { + logger.Warn(fmt.Sprintf("invalid connect timeout '%s', using default '10s'", cfg.ConnectTimeout)) + server.ConnectTimeout = 10 * time.Second + } + } + + if cfg.SendTimeout != "" { + server.SendTimeout, err = time.ParseDuration(cfg.SendTimeout) + if err != nil { + logger.Warn(fmt.Sprintf("invalid send timeout '%s', using default '10s'", cfg.SendTimeout)) + server.SendTimeout = 10 * time.Second + } + } + logger.Debug("making smtp connection") smtpClient, err := server.Connect() if err != nil { diff --git a/go.mod b/go.mod index 4565e816c..c8bbe1f7a 100644 --- a/go.mod +++ b/go.mod @@ -73,7 +73,7 @@ require ( github.com/stretchr/testify v1.8.4 github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26 github.com/wasilibs/go-re2 v1.3.0 - github.com/xhit/go-simple-mail/v2 v2.15.0 + github.com/xhit/go-simple-mail/v2 v2.16.0 golang.org/x/crypto v0.9.0 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 golang.org/x/mod v0.11.0 diff --git a/go.sum b/go.sum index c8cbb50bf..4469cceab 100644 --- a/go.sum +++ b/go.sum @@ -790,8 +790,8 @@ github.com/xdg/scram v1.0.5/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49 github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xdg/stringprep v1.0.3 h1:cmL5Enob4W83ti/ZHuZLuKD/xqJfus4fVPwE+/BDm+4= github.com/xdg/stringprep v1.0.3/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -github.com/xhit/go-simple-mail/v2 v2.15.0 h1:qMXeqcZErUW/Dw6EXxmPuxHzVI8MdxWnEnu2xcisohU= -github.com/xhit/go-simple-mail/v2 v2.15.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98= +github.com/xhit/go-simple-mail/v2 v2.16.0 h1:ouGy/Ww4kuaqu2E2UrDw7SvLaziWTB60ICLkIkNVccA= +github.com/xhit/go-simple-mail/v2 v2.16.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/test/bats/70_http_plugin.bats b/test/bats/70_plugin_http.bats similarity index 100% rename from test/bats/70_http_plugin.bats rename to test/bats/70_plugin_http.bats diff --git a/test/bats/71_dummy_plugin.bats b/test/bats/71_plugin_dummy.bats similarity index 100% rename from test/bats/71_dummy_plugin.bats rename to test/bats/71_plugin_dummy.bats diff --git a/test/bats/73_plugin_formatting.bats b/test/bats/73_plugin_formatting.bats new file mode 100644 index 000000000..153193fb1 --- /dev/null +++ b/test/bats/73_plugin_formatting.bats @@ -0,0 +1,65 @@ +#!/usr/bin/env bats +# vim: ft=bats:list:ts=8:sts=4:sw=4:et:ai:si: + +set -u + +setup_file() { + load "../lib/setup_file.sh" + is_package_testing && return + + ./instance-data load + + tempfile=$(TMPDIR="${BATS_FILE_TMPDIR}" mktemp) + export tempfile + + DUMMY_YAML="$(config_get '.config_paths.notification_dir')/dummy.yaml" + + # we test the template that is suggested in the email notification + # the $alert is not a shell variable + # shellcheck disable=SC2016 + config_set "${DUMMY_YAML}" ' + .group_wait="5s" | + .group_threshold=2 | + .output_file=strenv(tempfile) | + .format=" {{range . -}} {{$alert := . -}} {{range .Decisions -}}

{{.Value}} will get {{.Type}} for next {{.Duration}} for triggering {{.Scenario}} on machine {{$alert.MachineID}}.

CrowdSec CTI

{{end -}} {{end -}} " + ' + + config_set "$(config_get '.api.server.profiles_path')" ' + .notifications=["dummy_default"] | + .filters=["Alert.GetScope() == \"Ip\""] + ' + + config_set ' + .plugin_config.user="" | + .plugin_config.group="" + ' + + ./instance-crowdsec start +} + +teardown_file() { + load "../lib/teardown_file.sh" +} + +setup() { + is_package_testing && skip + load "../lib/setup.sh" +} + +#---------- + +@test "add two bans" { + rune -0 cscli decisions add --ip 1.2.3.4 --duration 30s + assert_stderr --partial 'Decision successfully added' + + rune -0 cscli decisions add --ip 1.2.3.5 --duration 30s + assert_stderr --partial 'Decision successfully added' + sleep 2 +} + +@test "expected 1 notification" { + rune -0 cat "${tempfile}" + assert_output - <<-EOT +

1.2.3.4 will get ban for next 30s for triggering manual 'ban' from 'githubciXXXXXXXXXXXXXXXXXXXXXXXX' on machine githubciXXXXXXXXXXXXXXXXXXXXXXXX.

CrowdSec CTI

1.2.3.5 will get ban for next 30s for triggering manual 'ban' from 'githubciXXXXXXXXXXXXXXXXXXXXXXXX' on machine githubciXXXXXXXXXXXXXXXXXXXXXXXX.

CrowdSec CTI

+ EOT +}