Skip to content

Commit

Permalink
Merge pull request #105 from nsano-rururu/aws_ses
Browse files Browse the repository at this point in the history
Add support for AWS SES
  • Loading branch information
jertel authored May 1, 2021
2 parents 4987e68 + 5bbd7df commit 8c9fb9d
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 1 deletion.
59 changes: 59 additions & 0 deletions docs/source/ruletypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1735,6 +1735,65 @@ Example usage::
Environment: '$VAR' # environment variable
Message: { field: message } # field in the first match

AWS SES
~~~~~~~

The AWS SES alerter is similar to Email alerter but uses AWS SES to send emails. The AWS SES alerter can use AWS credentials
from the rule yaml, standard AWS config files or environment variables.

AWS SES requires one option:

``ses_email``: An address or list of addresses to sent the alert to.

``ses_from_addr``: This sets the From header in the email.

Optional:

``ses_aws_access_key``: An access key to connect to AWS SES with.

``ses_aws_secret_key``: The secret key associated with the access key.

``ses_aws_region``: The AWS region in which the AWS SES resource is located. Default is us-east-1

``ses_aws_profile``: The AWS profile to use. If none specified, the default will be used.

``ses_email_reply_to``: This sets the Reply-To header in the email.

``ses_cc``: This adds the CC emails to the list of recipients. By default, this is left empty.

``ses_bcc``: This adds the BCC emails to the list of recipients but does not show up in the email message. By default, this is left empty.

Example When not using aws_profile usage::

alert:
- "ses"
ses_aws_access_key_id: "XXXXXXXXXXXXXXXXXX'"
ses_aws_secret_access_key: "YYYYYYYYYYYYYYYYYYYY"
ses_aws_region: "us-east-1"
ses_from_addr: "xxxx1@xxx.com"
ses_email: "xxxx1@xxx.com"

Example When to use aws_profile usage::

# Create ~/.aws/credentials

[default]
aws_access_key_id = xxxxxxxxxxxxxxxxxxxx
aws_secret_access_key = yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy

# Create ~/.aws/config

[default]
region = us-east-1

# alert rule setting

alert:
- "ses"
ses_aws_profile: "default"
ses_from_addr: "xxxx1@xxx.com"
ses_email: "xxxx1@xxx.com"

AWS SNS
~~~~~~~

Expand Down
105 changes: 105 additions & 0 deletions elastalert/alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -2274,3 +2274,108 @@ def alert(self, matches):

def get_info(self):
return {'type': 'datadog'}


class SesAlerter(Alerter):
""" Sends an email alert using AWS SES """
required_options = frozenset(['ses_email', 'ses_from_addr'])

def __init__(self, *args):
super(SesAlerter, self).__init__(*args)

self.aws_access_key_id = self.rule.get('ses_aws_access_key_id')
self.aws_secret_access_key = self.rule.get('ses_aws_secret_access_key')
self.aws_region = self.rule.get('ses_aws_region', 'us-east-1')
self.aws_profile = self.rule.get('ses_aws_profile', '')

self.from_addr = self.rule.get('ses_from_addr')

# Convert email to a list if it isn't already
if isinstance(self.rule['ses_email'], str):
self.rule['ses_email'] = [self.rule['ses_email']]

# If there is a cc then also convert it a list if it isn't
cc = self.rule.get('ses_cc')
if cc and isinstance(cc, str):
self.rule['ses_cc'] = [self.rule['ses_cc']]

# If there is a bcc then also convert it to a list if it isn't
bcc = self.rule.get('ses_bcc')
if bcc and isinstance(bcc, str):
self.rule['ses_bcc'] = [self.rule['ses_bcc']]

# If there is a email_reply_to then also convert it to a list if it isn't
reply_to = self.rule.get('ses_email_reply_to')
if reply_to and isinstance(reply_to, str):
self.rule['ses_email_reply_to'] = [self.rule['ses_email_reply_to']]

add_suffix = self.rule.get('ses_email_add_domain')
if add_suffix and not add_suffix.startswith('@'):
self.rule['ses_email_add_domain'] = '@' + add_suffix

def alert(self, matches):
body = self.create_alert_body(matches)

to_addr = self.rule['ses_email']
if 'ses_email_from_field' in self.rule:
recipient = lookup_es_key(matches[0], self.rule['ses_email_from_field'])
if isinstance(recipient, str):
if '@' in recipient:
to_addr = [recipient]
elif 'ses_email_add_domain' in self.rule:
to_addr = [recipient + self.rule['ses_email_add_domain']]
elif isinstance(recipient, list):
to_addr = recipient
if 'ses_email_add_domain' in self.rule:
to_addr = [name + self.rule['ses_email_add_domain'] for name in to_addr]

if self.aws_profile != '':
session = boto3.Session(profile_name=self.aws_profile)
else:
session = boto3.Session(
aws_access_key_id=self.aws_access_key_id,
aws_secret_access_key=self.aws_secret_access_key,
region_name=self.aws_region
)

client = session.client('ses')
try:
client.send_email(
Source=self.from_addr,
Destination={
'ToAddresses': to_addr,
'CcAddresses': self.rule.get('ses_cc', []),
'BccAddresses': self.rule.get('ses_bcc', [])
},
Message={
'Subject': {
'Charset': 'UTF-8',
'Data': self.create_title(matches),
},
'Body': {
'Text': {
'Charset': 'UTF-8',
'Data': body,
}
}
},
ReplyToAddresses=self.rule.get('ses_email_reply_to', []))
except Exception as e:
raise EAException("Error sending ses: %s" % (e,))

elastalert_logger.info("Sent ses to %s" % (to_addr,))

def create_default_title(self, matches):
subject = 'Elastalert2: %s' % (self.rule['name'])

# If the rule has a query_key, add that value plus timestamp to subject
if 'query_key' in self.rule:
qk = matches[0].get(self.rule['query_key'])
if qk:
subject += ' - %s' % (qk)

return subject

def get_info(self):
return {'type': 'ses',
'recipients': self.rule['ses_email']}
3 changes: 2 additions & 1 deletion elastalert/loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ class RulesLoader(object):
'discord': alerts.DiscordAlerter,
'dingtalk': alerts.DingTalkAlerter,
'chatwork': alerts.ChatworkAlerter,
'datadog': alerts.DatadogAlerter
'datadog': alerts.DatadogAlerter,
'ses': alerts.SesAlerter
}

# A partial ordering of alert types. Relative order will be preserved in the resulting alerts list
Expand Down

0 comments on commit 8c9fb9d

Please sign in to comment.