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
This commit is contained in:
mmetc 2023-09-29 16:59:06 +02:00 committed by GitHub
parent b8e6bd8c9a
commit 3cb9dbdb21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 102 additions and 6 deletions

View file

@ -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 following template receives a list of models.Alert objects
# The output goes in the email message body # The output goes in the email message body
format: | format: |
<html><body>
{{range . -}} {{range . -}}
{{$alert := . -}} {{$alert := . -}}
{{range .Decisions -}} {{range .Decisions -}}
<html><body><p><a href=https://www.whois.com/whois/{{.Value}}>{{.Value}}</a> will get <b>{{.Type}}</b> for next <b>{{.Duration}}</b> for triggering <b>{{.Scenario}}</b> on machine <b>{{$alert.MachineID}}</b>.</p> <p><a href=https://app.crowdsec.net/cti/{{.Value}}>CrowdSec CTI</a></p></body></html> <p><a href="https://www.whois.com/whois/{{.Value}}">{{.Value}}</a> will get <b>{{.Type}}</b> for next <b>{{.Duration}}</b> for triggering <b>{{.Scenario}}</b> on machine <b>{{$alert.MachineID}}</b>.</p> <p><a href="https://app.crowdsec.net/cti/{{.Value}}">CrowdSec CTI</a></p>
{{end -}} {{end -}}
{{end -}} {{end -}}
</body></html>
smtp_host: # example: smtp.gmail.com smtp_host: # example: smtp.gmail.com
smtp_username: # Replace with your actual username smtp_username: # Replace with your actual username
@ -35,7 +37,15 @@ receiver_emails:
# - email2@gmail.com # - email2@gmail.com
# One of "ssltls", "starttls", "none" # 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
--- ---

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"os" "os"
"time"
"github.com/crowdsecurity/crowdsec/pkg/protobufs" "github.com/crowdsecurity/crowdsec/pkg/protobufs"
"github.com/hashicorp/go-hclog" "github.com/hashicorp/go-hclog"
@ -47,6 +48,8 @@ type PluginConfig struct {
EncryptionType string `yaml:"encryption_type"` EncryptionType string `yaml:"encryption_type"`
AuthType string `yaml:"auth_type"` AuthType string `yaml:"auth_type"`
HeloHost string `yaml:"helo_host"` HeloHost string `yaml:"helo_host"`
ConnectTimeout string `yaml:"connect_timeout"`
SendTimeout string `yaml:"send_timeout"`
} }
type EmailPlugin struct { 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 { 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 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.Authentication = AuthStringToType[cfg.AuthType]
server.Helo = cfg.HeloHost 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") logger.Debug("making smtp connection")
smtpClient, err := server.Connect() smtpClient, err := server.Connect()
if err != nil { if err != nil {

2
go.mod
View file

@ -73,7 +73,7 @@ require (
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26 github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26
github.com/wasilibs/go-re2 v1.3.0 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/crypto v0.9.0
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
golang.org/x/mod v0.11.0 golang.org/x/mod v0.11.0

4
go.sum
View file

@ -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 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 h1:cmL5Enob4W83ti/ZHuZLuKD/xqJfus4fVPwE+/BDm+4=
github.com/xdg/stringprep v1.0.3/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= 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.16.0 h1:ouGy/Ww4kuaqu2E2UrDw7SvLaziWTB60ICLkIkNVccA=
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/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= 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.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=

View file

@ -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="<html><body> {{range . -}} {{$alert := . -}} {{range .Decisions -}} <p><a href=\"https://www.whois.com/whois/{{.Value}}\">{{.Value}}</a> will get <b>{{.Type}}</b> for next <b>{{.Duration}}</b> for triggering <b>{{.Scenario}}</b> on machine <b>{{$alert.MachineID}}</b>.</p> <p><a href=\"https://app.crowdsec.net/cti/{{.Value}}\">CrowdSec CTI</a></p> {{end -}} {{end -}} </body></html>"
'
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
<html><body> <p><a href="https://www.whois.com/whois/1.2.3.4">1.2.3.4</a> will get <b>ban</b> for next <b>30s</b> for triggering <b>manual 'ban' from 'githubciXXXXXXXXXXXXXXXXXXXXXXXX'</b> on machine <b>githubciXXXXXXXXXXXXXXXXXXXXXXXX</b>.</p> <p><a href="https://app.crowdsec.net/cti/1.2.3.4">CrowdSec CTI</a></p> <p><a href="https://www.whois.com/whois/1.2.3.5">1.2.3.5</a> will get <b>ban</b> for next <b>30s</b> for triggering <b>manual 'ban' from 'githubciXXXXXXXXXXXXXXXXXXXXXXXX'</b> on machine <b>githubciXXXXXXXXXXXXXXXXXXXXXXXX</b>.</p> <p><a href="https://app.crowdsec.net/cti/1.2.3.5">CrowdSec CTI</a></p> </body></html>
EOT
}