Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(alerts) add dingtalk alerts #3264

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 53 additions & 2 deletions elastalert/alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from twilio.base.exceptions import TwilioRestException
from twilio.rest import Client as TwilioClient

from .util import EAException
from .util import EAException, get_timestamp_sign
from .util import elastalert_logger
from .util import lookup_es_key
from .util import pretty_ts
Expand Down Expand Up @@ -1073,7 +1073,6 @@ def format_body(self, body):

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

body = self.format_body(body)
# post to Teams
headers = {'content-type': 'application/json'}
Expand Down Expand Up @@ -2184,3 +2183,55 @@ def get_info(self):
'type': 'hivealerter',
'hive_host': self.rule.get('hive_connection', {}).get('hive_host', '')
}


class DingTalkAlerter(Alerter):
required_options = frozenset(['dingtalk_webhook', 'dingtalk_message', "dingtalk_isAtAll", "dingtalk_token", "dingtalk_title"])

def __init__(self, rule):
super(DingTalkAlerter, self).__init__(rule)
self.dingtalk_webhook_url = self.rule['dingtalk_webhook']
self.dingtalk_message = self.rule.get('dingtalk_msgtype', 'text')
self.dingtalk_isAtAll = self.rule.get('dingtalk_isAtAll', False)
self.dingtalk_token = self.rule.get('dingtalk_token', '')
self.dingtalk_title = self.rule.get('dingtalk_title', '')

def format_body(self, body):
return body.encode('utf8')

def get_signed_url(self):
timestamp, sign = get_timestamp_sign(self.dingtalk_token)
webhook = self.dingtalk_webhook_url + "&timestamp=" + timestamp + "&sign=" + sign
return webhook

def alert(self, matches):
headers = {
"Content-Type": "application/json",
"Accept": "application/json;charset=utf-8"
}
body = self.create_alert_body(matches)
payload = {
"msgtype": self.dingtalk_message,
"text": {
"content": body
},
"at": {
"isAtAll": self.dingtalk_isAtAll
}
}
try:
response = requests.post(self.get_signed_url(),
data=json.dumps(payload),
headers=headers)
response.raise_for_status()
print(response.text)
except RequestException as e:
print(e)
pass
raise EAException("Error request to Dingtalk: {0}".format(str(e)))

def get_info(self):
return {
"type": "dingtalk",
"dingtalk_webhook": self.dingtalk_webhook_url
}
3 changes: 2 additions & 1 deletion elastalert/loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ class RulesLoader(object):
'servicenow': alerts.ServiceNowAlerter,
'alerta': alerts.AlertaAlerter,
'post': alerts.HTTPPostAlerter,
'hivealerter': alerts.HiveAlerter
'hivealerter': alerts.HiveAlerter,
'dingtalk': alerts.DingTalkAlerter
}

# A partial ordering of alert types. Relative order will be preserved in the resulting alerts list
Expand Down
18 changes: 18 additions & 0 deletions elastalert/util.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
# -*- coding: utf-8 -*-
import base64
import collections
import datetime
import hashlib
import hmac
import logging
import os
import re
import sys
import time
import urllib

import dateutil.parser
import pytz
Expand Down Expand Up @@ -460,3 +465,16 @@ def should_scrolling_continue(rule_conf):
stop_the_scroll = 0 < max_scrolling <= rule_conf.get('scrolling_cycle')

return not stop_the_scroll


def get_timestamp_sign(token):
timestamp = str(round(time.time() * 1000))
secret_enc = token.encode('utf-8')
string_to_sign = '{}\n{}'.format(timestamp, token)
string_to_sign_enc = string_to_sign.encode('utf-8')
hmac_code = hmac.new(secret_enc, string_to_sign_enc,
digestmod=hashlib.sha256).digest()
sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
print("timestamp: ", timestamp)
print("sign:", sign)
return timestamp, sign
10 changes: 5 additions & 5 deletions example_rules/example_change.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,20 @@

# (Required)
# Rule name, must be unique
name: New country login
name: filebeat-7.3.0-2022.08.16-000001

# (Required)
# Type of alert.
# the change rule will alert when a certain field changes in two documents within a timeframe
type: change
type: blacklist

# (Required)
# Index to search, wildcard supported
index: logstash-*
index: filebeat-7.3.0-2022.08.16-000001

# (Required, change specific)
# The field to look for changes in
compare_key: country_name
compare_key: "INFO"

# (Required, change specific)
# Ignore documents without the compare_key (country_name) field
Expand All @@ -56,7 +56,7 @@ timeframe:
filter:
- query:
query_string:
query: "document_type: login"
query: "kubernetes.pod.name: game-server and INFO"

# (Required)
# The alert is use when a match is found
Expand Down
72 changes: 72 additions & 0 deletions example_rules/example_dingtalk.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# This is the folder that contains the rule yaml files
# Any .yaml file will be loaded as a rule
rules_folder: rules

# How often ElastAlert will query Elasticsearch
# The unit can be anything from weeks to seconds
run_every:
minutes: 1

# ElastAlert will buffer results from the most recent
# period of time, in case some log sources are not in real time
buffer_time:
minutes: 15

# The Elasticsearch hostname for metadata writeback
# Note that every rule can have its own Elasticsearch host
es_host: localhost

# The Elasticsearch port
es_port: 9200

# The AWS region to use. Set this when using AWS-managed elasticsearch
#aws_region: us-east-1

# The AWS profile to use. Use this if you are using an aws-cli profile.
# See http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html
# for details
#profile: test

# Optional URL prefix for Elasticsearch
#es_url_prefix: elasticsearch

# Connect with TLS to Elasticsearch
#use_ssl: True

# Verify TLS certificates
#verify_certs: True

# GET request with body is the default option for Elasticsearch.
# If it fails for some reason, you can pass 'GET', 'POST' or 'source'.
# See http://elasticsearch-py.readthedocs.io/en/master/connection.html?highlight=send_get_body_as#transport
# for details
#es_send_get_body_as: GET

# Option basic-auth username and password for Elasticsearch
#es_username: elasticusername
#es_password: elasticpassword

# The index on es_host which is used for metadata storage
# This can be a unmapped index, but it is recommended that you run
# elastalert-create-index to set a mapping
writeback_index: elastalert_status

# If an alert fails for some reason, ElastAlert will retry
# sending the alert until this time period has elapsed
alert_time_limit:
days: 2


# Use the token method when creating the pinning robot
alert:
- dingtalk

dingtalk_webhook: your-dingtalk-robot-webhook-url
# dingtalk msgType such as text
dingtalk_msgtype: text
# dingtalk at allUser
dingtalk_isAtAll: True
# dingtalk robot token such as: SEC8423d34b07446fbcc4cf3abe6b71f3d36b5152cdf54cf5dd29482180ce2b2513
dingtalk_token: ""
# dingtalk title such as: <project-name>
dingtalk_title: "castle-pre-usw"