Email alerting
Email alerts are very common: most connected people have an email, and I bet that you 🫵 have more than one ^^
Email is at least as old as the Internet. So you have plenty options to send email on the command-line: curl, sendmail, mailsend, mailsend bis, etc, etc.
Here I'll focus on the curl command, because it's ubiquitous and flexible enough to avoid having a dedicated reaction-curl.sh script.
You're welcome to document options if you know some!
💌 curl 💌
curl sends email with the SMTP protocol. We'll need the following informations:
- User: most commonly your email address, eg. 
server@example.fr. - Password: the account password, eg. 
myPassword(do better please 😋). - From: the address that will be in the 
From:header, most commonly the same value as the mail account. - To: the address that will be in the 
To:header and in the mail transaction, eg.notif@example.fr. It can be the same address as "Mail account" / "From address". - Server: protocol, domain name and port, eg. 
smtps://mail.example.fr:465. - Subject, the message we want to send.
 - And Message, the message we want to send.
 
Sample curl command (insecure; see below):
curl \
  # require SSL
  --ssl-reqd \
  # exit with non-zero code when the transaction fails
  --fail \
  # do not show progress but show errors
  --silent \
  --show-error \
  # from in the transaction
  --mail-from "$FROM" \
  # from in the header
  --header "from: reaction <${FROM}>" \
  # to in the transaction
  --mail-rcpt "$TO" \
  # to in the header
  --header "to: $TO" \
  # auth options
  --user "${USER}:${PASSWORD}" \
  # server url
  --url "$SERVER" \
  # subject
  --header "subject: $MESSAGE" \
  # body
  --form "=(;type=multipart/mixed" \
  --form "=${MESSAGE};type=text/plain" \
  --form "=)"
Short form:
curl --ssl-reqd -fsS \
  --mail-from "$FROM" -H "from: reaction <${FROM}>" \
  --mail-rcpt "$TO" -H "to: $TO" \
  -u "${USER}:${PASSWORD}" --url "$SERVER" \
  -H "subject: $MESSAGE" \
  -F "=(;type=multipart/mixed" -F "=${MESSAGE};type=text/plain" -F "=)"
Protecting the password
Specifying a password on the command line is not secure, because all programs of the system can see it. curl tries to hide it as soon as possible, but this stays inherently insecure.
Moreover, you may want to commit and publish your configuration file, or at least to let it visible by all users (mode 644 for example).
So we're going to load the password from a secret file.
Let's say we have a secret file at path
/var/secrets/mail_passwordwith mode600.
Since version 8.3.0 (September 2023, hopefully not to recent for your distribution!), curl has a variable system that can load files, perform simple operations on them, and use them in most other options.
We're gonna replace this option:
  --user "${USER}:${PASSWORD}"
We'll first create a variable that loads the secret file.
The we can use it in the expanded --user option.
We'll trim the variable, to ensure any whitespace before and after the password is discarded:
  --variable "PASS@/var/secrets/mail_password"
  --expand-user "${USER}:{{PASS:trim}}"
Creating the Action
Before creating the action, I invite you to test your parameters. Checking that your command runs on the command line before creating an action is good practice!
Let's create a JSONnet function/file that can be reused for multiple types of alerts:
/etc/reaction/_mail.jsonnet
local user = "server@example.fr";
local from = user;
local server = "smtps://mail.example.fr:465";
local secret_file = "/var/secrets/mail_password";
local mail(to, subject, body = "") = {
  mail: {
    cmd: [
      "curl",
      "--ssl-reqd",
      "-fsS",
      "--mail-from", from,
      "-H", "from: reaction <" + from + ">",
      "--mail-rcpt", to,
      "-H", "to: " + to,
      "--variable", "PASS@" + secret_file,
      "--expand-user", user + ":{{PASS:trim}}",
      "--url", server,
      "-H", "subject: " + subject,
      "-F", "=(;type=multipart/mixed",
      "-F", '=' + (if body != '' then body else subject) + ';type=text/plain',
      "-F", "=)",
    ],
    oneshot: true,
  },
};
mail
- The file begins with a 
_so that it is not loaded directly by reaction. - If the body message is not given, it defaults to the subject.
 - See oneshot in the reference for more information on this option.
 
Calling the action
We can now load it in another file:
/etc/reaction/mystream.jsonnet
local mail = import '_mail.jsonnet';
{
  patterns: {
    // ...
  },
  streams: {
    mystream: {
      // ...
      filters: {
        myfilter: {
          // ...
          actions: banFor("4h") + mail("notif@example.fr", "banned <ip>")
        }
      }
    }
  }
}
- See the FAQ for more information on how to separate the configuration in multiple files.
 
Caveats
The emails sent contain no Date: headers, so your mail client may print a false date,
even if your mail servers know when the emails were received.