E-mail notifications: Include matching log line? Filter?

Hi,
is there a way to add a filter on which decisions lead to an e-mail notification? Ideally, I would only like to get a notification if an IP from a specific range is blocked.
And is there a way to include the log line(s) that triggered the decision into the notification mail?
Thanks for any pointers!

is there a way to add a filter on which decisions lead to an e-mail notification? Ideally, I would only like to get a notification if an IP from a specific range is blocked.

Yes you would need to modify the profiles.yaml to filter, the only downside is you would need to duplicate some items for example:

name: default_ip_remediation_notify
#debug: true
filters:
 - Alert.Remediation == true && Alert.GetScope() == "Ip" && IpInRange(Alert.GetValue(), "1.2.3.0/24")
decisions:
 - type: ban
   duration: 4h
#duration_expr: "Sprintf('%dh', (GetDecisionsCount(Alert.GetValue()) + 1) * 4)"
notifications:
  - slack_default  # Set the webhook in /etc/crowdsec/notifications/slack.yaml before enabling this.
on_success: break
---
name: default_ip_remediation
#debug: true
filters:
 - Alert.Remediation == true && Alert.GetScope() == "Ip"
decisions:
 - type: ban
   duration: 4h
#duration_expr: "Sprintf('%dh', (GetDecisionsCount(Alert.GetValue()) + 1) * 4)"
#notifications:
#  - slack_default  # Set the webhook in /etc/crowdsec/notifications/slack.yaml before enabling this.
on_success: break

For example above I want to be notified in slack if IP is in the range of 1.2.3.0/24 and Alert.GetValue() is a function that returns the current alert IP address if the Alert.GetScope() == ' Ip' and then since we didnt remove the default_ip_remediation any alerts that dont fit in the range will still get decisions but wont notify me via slack.

And is there a way to include the log line(s) that triggered the decision into the notification mail?

Yes there is a way but it not recommend as it can drastically increase your database size (as meta information is stored within it). However, if you would like too you need to create an enricher that add the line in as a meta attribute:

#/etc/crowdsec/parsers/s02-enrich/my-enrich.yaml
#filter: "evt.Parsed.program == 'kernel'"
## Filter can be ommited if you want to add logs to ALL events, but if you want to target a specific program I suggest adding it, go to hub and find the parser for the logs that you want and copy paste it here.
name: my/enricher
description: "Enrich meta with log lines"
statics:
  - meta: log_line
    expression: evt.Line.Raw

then you would need to modify the email template to include this meta information:

  <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 -}}
    {{ range GetMeta . "log_line" -}}
      <p>{{ . | HTMLEscape }}</p>
    {{ end -}}
  {{end -}}
  </body></html>

The key area is:

    {{ range GetMeta . "log_line" -}}
      <p>{{ . | HTMLEscape -}}</p>
    {{ end -}}

This will use the GetMeta function which takes alert as the first arg passed by . character as we are outside the .Decisions scope and log_line is passed as the enricher we made previously should attach the log lines to this meta attribute.

Please note the HTMLEscape function is not mandatory but I like adding it here incase somehow they add some html attribute into the log line which we should escape and not render in the email body. The HTMLEscape function is not a silver bullet and is bypassable in some edge cases but it should at least stop < or > from rendering.

Another thing to note in some scenario we use cache_size attribute to limit the amount of events held in memory to stop scenario from OOMing the system, note if the sceanrio has this key only the last X as denoted by the cache_size key is kept so meaning if a scenario is triggered and you wonder why you only saw X log lines it because of this key.

Thank you. I’ll trust your advice and will not enrich the database with log snippets.

I will test duplicating profiles for various scenarios. Am I right to assume that expr can handle also more complex evaluations, such as, e.g.:

filters:
 - Alert.Remediation == true && Alert.GetScope() == "Ip" && ( IpInRange(Alert.GetValue(), "1.2.3.0/24") || IpInRange(Alert.GetValue(), "2.3.4.0/24"))

BTW, while reading through the docs, I noticed that the link on Introduction | CrowdSec to github should be expr/docs/language-definition.md at master · expr-lang/expr · GitHub (is: on GitHub antonmedv/expr/blob/master/docs/Language-Definition.md - apologies, had to invalidate the link, as new users can only post two links in a post…)

Yes expr can handle && (||) syntax even allow you to use words instead || or

yes expr changed to a named organization so we should update it :+1:

1 Like