Exemple #1
0
    def load(self, conf, args=None):
        """
        Discover and load all the rules as defined in the conf and args.
        :param dict conf: Configuration dict
        :param dict args: Arguments dict
        :return: List of rules
        :rtype: list
        """
        names = []
        use_rule = None if args is None else args.rule

        # Load each rule configuration file
        rules = []
        rule_files = self.get_names(conf, use_rule)
        for rule_file in rule_files:
            try:
                rule = self.load_configuration(rule_file, conf, args)
                # A rule failed to load, don't try to process it
                if not rule:
                    elastalert_logger.error('Invalid rule file skipped: %s' %
                                            rule_file)
                    continue
                if rule['name'] in names:
                    raise EAException('Duplicate rule named %s' %
                                      (rule['name']))
            except EAException as e:
                raise EAException('Error loading file %s: %s' % (rule_file, e))

            rules.append(rule)
            names.append(rule['name'])

        return rules
Exemple #2
0
    def alert(self, matches):
        # Format the command and arguments
        command = [
            resolve_string(command_arg, matches[0])
            for command_arg in self.rule['command']
        ]
        self.last_command = command

        # Run command and pipe data
        try:
            subp = subprocess.Popen(command,
                                    stdin=subprocess.PIPE,
                                    shell=self.shell)

            if self.rule.get('pipe_match_json'):
                match_json = json.dumps(matches, cls=DateTimeEncoder) + '\n'
                stdout, stderr = subp.communicate(input=match_json.encode())
            elif self.rule.get('pipe_alert_text'):
                alert_text = self.create_alert_body(matches)
                stdout, stderr = subp.communicate(input=alert_text.encode())
            if self.rule.get("fail_on_non_zero_exit", False) and subp.wait():
                raise EAException(
                    "Non-zero exit code while running command %s" %
                    (' '.join(command)))
        except OSError as e:
            raise EAException("Error while running command %s: %s" %
                              (' '.join(command), e))
        elastalert_logger.info("Alert sent to Command")
Exemple #3
0
    def alert(self, matches):
        client = TwilioClient(self.twilio_account_sid, self.twilio_auth_token)

        try:
            if self.twilio_use_copilot:
                if self.twilio_message_service_sid is None:
                    raise EAException(
                        "Twilio Copilot requires the 'twilio_message_service_sid' option"
                    )

                client.messages.create(
                    body=self.rule['name'],
                    to=self.twilio_to_number,
                    messaging_service_sid=self.twilio_message_service_sid)
            else:
                if self.twilio_from_number is None:
                    raise EAException(
                        "Twilio SMS requires the 'twilio_from_number' option")

                client.messages.create(body=self.rule['name'],
                                       to=self.twilio_to_number,
                                       from_=self.twilio_from_number)
        except TwilioRestException as e:
            raise EAException("Error posting to twilio: %s" % e)

        elastalert_logger.info("Trigger sent to Twilio")
    def get_token(self):

        #获取token是有次数限制的,本想本地缓存过期时间和token,但是elastalert每次调用都是一次性的,不能全局缓存
        if self.expires_in >= datetime.datetime.now() and self.access_token:
            return self.access_token

        #构建获取token的url
        get_token_url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s' %(self.corp_id,self.secret)

        try:
            response = requests.get(get_token_url,verify=False)
            response.raise_for_status()
        except RequestException as e:
            raise EAException("get access_token failed , stacktrace:%s" % e)
            #sys.exit("get access_token failed, system exit")

        token_json = response.json()

        if 'access_token' not in token_json :
            raise EAException("get access_token failed , , the response is :%s" % response.text())
            #sys.exit("get access_token failed, system exit")

        #获取access_token和expires_in
        self.access_token = token_json['access_token']
        self.expires_in = datetime.datetime.now() + datetime.timedelta(seconds=token_json['expires_in'])

        return self.access_token
Exemple #5
0
    def __init__(self, *args):
        super(UniqueLongTermRule, self).__init__(*args)
        self.values = []
        self.garbage_time = 0
        self.exec_num = 0
        self.field = self.rules['compare_key']
        self.timeperiods_index = 0
        self.no_of_timeperiods = int(self.rules['no_of_timeperiods'])
        for i in range(0, self.no_of_timeperiods):
            self.values.append(set())

        timeperiod_sec = int(self.rules['timeframe'].total_seconds())
        run_every_sec = int(self.rules['run_every'].total_seconds())
        if run_every_sec > timeperiod_sec:
            raise EAException(
                "Run Every option cannot be greater than Timeperiod option")
        if timeperiod_sec % run_every_sec != 0:
            raise EAException(
                "Run Every must fit integer number of times in Timeperiod")
        self.runs_per_timeperiod = int(timeperiod_sec / run_every_sec)

        elastalert_logger.info(
            "Timeperiod sec: %d, Number of executions per timeperiod: %d, Number of timeperiods: %d"
            %
            (timeperiod_sec, self.runs_per_timeperiod, self.no_of_timeperiods))
Exemple #6
0
    def alert(self, matches):
        # Format the command and arguments
        try:
            command = [
                resolve_string(command_arg, matches[0])
                for command_arg in self.rule['command']
            ]
            self.last_command = command
        except KeyError as e:
            raise EAException("Error formatting command: %s" % (e))

        # Run command and pipe data
        try:
            subp = subprocess.Popen(command,
                                    stdin=subprocess.PIPE,
                                    shell=self.shell)
            match_json = json.dumps(matches, cls=DateTimeEncoder) + '\n'
            input_string = self.rule['name'] + ":||:" + match_json
            stdout, stderr = subp.communicate(input=input_string.encode())
            if self.rule.get("fail_on_non_zero_exit", False) and subp.wait():
                raise EAException(
                    "Non-zero exit code while running command %s" %
                    (' '.join(command)))
        except OSError as e:
            raise EAException("Error while running command %s: %s" %
                              (' '.join(command), e))
Exemple #7
0
    def alert(self, matches):
        message = self.create_title(matches)
        detailed_message = self.create_alert_body(matches)

        command = [
            self.msend_command, '-l', self.cell_home, '-n', self.cell_name,
            '-a', self.event_class, '-r', self.event_severity, '-m',
            '\'%s\'' % message
        ]

        slotsetvalues = ''

        #
        # Transform slotsetvalues dict into a string
        if (isinstance(self.slotsetvalues, dict)):
            for k, v in self.slotsetvalues.items():
                slotsetvalues += "%s=%s;" % (k, v)
        # If slotsetvalues is a string, copy it straight across
        elif (isinstance(self.slotsetvalues, basestring)):
            slotsetvalues += self.slotsetvalues

        if slotsetvalues.endswith(';'):
            slotsetvalues = slotsetvalues[:-1]

        try:
            if self.new_style_string_format:
                slotsetvalues = slotsetvalues.format(match=matches[0])
            else:
                slotsetvalues = slotsetvalues % matches[0]

        except KeyError as e:
            raise EAException("Error formatting command: %s" % (e))

        if (len(slotsetvalues) > 0):
            command.extend(['-b', '\'%s\'' % slotsetvalues])

        self.last_command = command
        logging.warning("MSEND Command: %s" % ' '.join(command))

        # Run command and pipe data
        try:
            subp = subprocess.Popen(' '.join(command),
                                    stdin=subprocess.PIPE,
                                    shell=self.shell)

            if self.rule.get('pipe_match_json'):
                match_json = json.dumps(matches, cls=DateTimeEncoder) + '\n'
                stdout, stderr = subp.communicate(input=match_json)
            if self.rule.get("fail_on_non_zero_exit", False) and subp.wait():
                raise EAException(
                    "Non-zero exit code while running msend command %s" %
                    (' '.join(command)))
        except OSError as e:
            raise EAException("Error while running msend command %s: %s" %
                              (' '.join(command), e))
Exemple #8
0
    def alert(self, matches):
        client = Exotel(self.exotel_account_sid, self.exotel_auth_token)

        try:
            message_body = self.rule['name'] + self.sms_body
            response = client.sms(self.rule['exotel_from_number'], self.rule['exotel_to_number'], message_body)
            if response != 200:
                raise EAException("Error posting to Exotel, response code is %s" % response)
        except RequestException:
            raise EAException("Error posting to Exotel").with_traceback(sys.exc_info()[2])
        elastalert_logger.info("Trigger sent to Exotel")
Exemple #9
0
 def create_alert(alert, alert_config):
     alert_class = self.alerts_mapping.get(alert) or get_module(alert)
     if not issubclass(alert_class, alerts.Alerter):
         raise EAException(
             'Alert module %s is not a subclass of Alerter' % alert)
     missing_options = (rule['type'].required_options
                        | alert_class.required_options) - frozenset(
                            alert_config or [])
     if missing_options:
         raise EAException('Missing required option(s): %s' %
                           (', '.join(missing_options)))
     return alert_class(alert_config)
Exemple #10
0
    def alert(self, matches):
        body = ''
        for match in matches:
            body += str(BasicMatchString(self.rule, match))
            if len(matches) > 1:
                body += '\n----------------------------------------\n'
        if len(body) > 2047:
            body = body[
                0:
                1950] + '\n *message was cropped according to chatwork embed description limits!*'
        headers = {'X-ChatWorkToken': self.chatwork_apikey}
        # set https proxy, if it was provided
        proxies = {
            'https': self.chatwork_proxy
        } if self.chatwork_proxy else None
        auth = HTTPProxyAuth(
            self.chatwork_proxy_login,
            self.chatwork_proxy_pass) if self.chatwork_proxy_login else None
        params = {'body': body}

        try:
            response = requests.post(self.url,
                                     params=params,
                                     headers=headers,
                                     proxies=proxies,
                                     auth=auth)
            response.raise_for_status()
        except RequestException as e:
            raise EAException(
                "Error posting to Chattwork: %s. Details: %s" %
                (e, "" if e.response is None else e.response.text))

        elastalert_logger.info("Alert sent to Chatwork room %s" %
                               self.chatwork_room_id)
Exemple #11
0
    def alert(self, matches):
        body = '⚠ *%s* ⚠ ```\n' % (self.create_title(matches))
        for match in matches:
            body += str(BasicMatchString(self.rule, match))
            # Separate text of aggregated alerts with dashes
            if len(matches) > 1:
                body += '\n----------------------------------------\n'
        if len(body) > 4095:
            body = body[0:4000] + "\n⚠ *message was cropped according to telegram limits!* ⚠"
        body += ' ```'

        headers = {'content-type': 'application/json'}
        # set https proxy, if it was provided
        proxies = {'https': self.telegram_proxy} if self.telegram_proxy else None
        auth = HTTPProxyAuth(self.telegram_proxy_login, self.telegram_proxy_password) if self.telegram_proxy_login else None
        payload = {
            'chat_id': self.telegram_room_id,
            'text': body,
            'parse_mode': 'markdown',
            'disable_web_page_preview': True
        }

        try:
            response = requests.post(self.url, data=json.dumps(payload, cls=DateTimeEncoder), headers=headers, proxies=proxies, auth=auth)
            warnings.resetwarnings()
            response.raise_for_status()
        except RequestException as e:
            raise EAException("Error posting to Telegram: %s. Details: %s" % (e, "" if e.response is None else e.response.text))

        elastalert_logger.info(
            "Alert sent to Telegram room %s" % self.telegram_room_id)
Exemple #12
0
    def __init__(self, *args):
        super(DifferenceRule, self).__init__(*args)
        # self.diff_key = self.rules['compare_key'].split('.')
        self.diff_key = self.rules['compare_key']
        self.threshold_pct = self.rules['threshold_pct']
        self.delta_sec = self.rules['delta_min'] * 60
        self.agg_sec = self.rules['agg_min'] * 60
        self.qkey = self.rules['query_key']
        # keys are query_key values and values are objects of inner class
        # self.qobj = {}
        self.include = self.rules['include']
        # do not include @timestamp
        self.include = [i for i in self.include if i != '@timestamp']
        # self.include_all = []
        # if 'include_all' in self.rules:
        #	self.include_all = self.rules['include_all']
        #	self.include = list(set(self.include) - set(self.include_all))

        # set realert to 0 to get alert for each query_key in one minute
        # since this query_key is not part of core elastalert
        self.rules['realert'] = datetime.timedelta(minutes=0)
        if not self.delta_sec >= self.agg_sec:
            raise EAException("delta_min must be greater or equal to agg_min")

        self.es = elasticsearch_client(self.rules)

        self.filter_query = {"query_string": {"query": "*"}}
        if self.rules['filter']:
            self.filter_query = self.rules['filter'][0]
Exemple #13
0
    def __init__(self, rule):
        super(MSendAlerter, self).__init__(rule)
        self.cell_name = self.rule['msend_cell_name']
        self.event_class = self.rule['msend_event_class']
        self.event_severity = self.rule['msend_event_severity']

        self.cell_home = self.rule.get('msend_cell_home', '/opt/msend')
        self.msend_command = self.rule.get('msend_command',
                                           '/opt/msend/bin/msend')
        self.slotsetvalues = self.rule.get('msend_slotsetvalues')

        self.new_style_string_format = False
        if 'new_style_string_format' in self.rule and self.rule[
                'new_style_string_format']:
            self.new_style_string_format = True

        self.last_command = []

        # Slot Set Values validation
        if (isinstance(self.slotsetvalues, basestring)):
            regex = re.compile("^\w+=[^\n;'\"]+(;(\w+=[^\n;'\"]+))*$")
            if (regex.match(self.slotsetvalues) is None):
                raise EAException('Invalid slotsetvalues format: %s' %
                                  self.slotsetvalues)

        # MSEND Command Validation
        self.shell = False
        if isinstance(self.msend_command, basestring):
            self.shell = True
            if '%' in self.msend_command:
                logging.warning(
                    'Warning! You could be vulnerable to shell injection!')
 def alert(self, matches):
     headers = {
         "Content-Type": "application/json",
         "Accept": "application/json;charset=utf-8"
     }
     #        pdb.set_trace()
     body = self.my_create_alert_body(matches)
     #body="这是用于创建的第二个规则"
     title = self.rule['name'] + '- alert'
     payload = {
         "msgtype": self.dingtalk_msgtype,
         "markdown": {
             "title": title,
             "text": body
         },
         "at": {
             "isAtAll": False
         }
     }
     try:
         response = requests.post(self.dingtalk_webhook_url,
                                  data=json.dumps(payload,
                                                  cls=DateTimeEncoder),
                                  headers=headers)
         response.raise_for_status()
     except RequestException as e:
         raise EAException("Error request to Dingtalk: {0}".format(str(e)))
    def senddata(self, content):
        config_file = os.environ.get('ALERTA_CONF_FILE') or OPTIONS['config_file']
        config = configparser.RawConfigParser(defaults=OPTIONS)
        try:
            config.read(os.path.expanduser(config_file))
        except Exception:
            sys.exit("Problem reading configuration file %s - is this an ini file?" % config_file)

        want_profile = os.environ.get('ALERTA_DEFAULT_PROFILE') or config.defaults().get('profile')
        if want_profile and config.has_section('profile %s' % want_profile):
            for opt in OPTIONS:
                try:
                    OPTIONS[opt] = config.getboolean('profile %s' % want_profile, opt)
                except (ValueError, AttributeError):
                    OPTIONS[opt] = config.get('profile %s' % want_profile, opt)
        else:
            for opt in OPTIONS:
                try:
                    OPTIONS[opt] = config.getboolean('DEFAULT', opt)
                except (ValueError, AttributeError):
                    OPTIONS[opt] = config.get('DEFAULT', opt)
        try:
            LOG.debug("[alerta] sendto=%s ", OPTIONS.get("endpoint"))
            api = Client(endpoint=OPTIONS.get("endpoint"), key=OPTIONS.get("key"), ssl_verify=OPTIONS.get("sslverify"))
            api.send_alert(**content)
        except RequestException as e:
            raise EAException("send message has error: %s" % e)

        elastalert_logger.info("send msg success" )
Exemple #16
0
    def alert(self, matches):
        """ Each match will trigger a POST to the specified endpoint(s). """
        for match in matches:
            payload = match if self.post_all_values else {}
            payload.update(self.post_static_payload)
            for post_key, es_key in list(self.post_payload.items()):
                payload[post_key] = lookup_es_key(match, es_key)
            headers = {
                "Content-Type": "application/json",
                "Accept": "application/json;charset=utf-8"
            }
            if self.post_ca_certs:
                verify = self.post_ca_certs
            else:
                verify = not self.post_ignore_ssl_errors
            if self.post_ignore_ssl_errors:
                requests.packages.urllib3.disable_warnings()

            headers.update(self.post_http_headers)
            proxies = {'https': self.post_proxy} if self.post_proxy else None
            for url in self.post_url:
                try:
                    response = requests.post(url,
                                             data=json.dumps(
                                                 payload, cls=DateTimeEncoder),
                                             headers=headers,
                                             proxies=proxies,
                                             timeout=self.timeout,
                                             verify=verify)
                    response.raise_for_status()
                except RequestException as e:
                    raise EAException("Error posting HTTP Post alert: %s" % e)
            elastalert_logger.info("HTTP Post alert sent.")
Exemple #17
0
    def __init__(self, *args):
        super(SpikeAggregationRule, self).__init__(*args)

        # shared setup
        self.ts_field = self.rules.get('timestamp_field', '@timestamp')

        # aggregation setup
        # if 'max_threshold' not in self.rules and 'min_threshold' not in self.rules:
        #     raise EAException("MetricAggregationRule must have at least one of either max_threshold or min_threshold")

        self.metric_key = self.rules['metric_agg_key'] + '_' + self.rules[
            'metric_agg_type']

        if not self.rules['metric_agg_type'] in self.allowed_aggregations:
            raise EAException("metric_agg_type must be one of %s" %
                              (str(self.allowed_aggregations)))

        self.rules[
            'aggregation_query_element'] = self.generate_aggregation_query()
        self.ref_window_filled_once = False

        # spike setup
        self.timeframe = self.rules['timeframe']

        self.ref_windows = {}
        self.cur_windows = {}

        self.get_ts = new_get_event_ts(self.ts_field)
        self.first_event = {}
        self.skip_checks = {}
Exemple #18
0
    def __init__(self, rule):
        super(JiraAlerter, self).__init__(rule)
        self.server = self.rule['jira_server']
        self.get_account(self.rule['jira_account_file'])
        self.project = self.rule['jira_project']
        self.issue_type = self.rule['jira_issuetype']

        # Deferred settings refer to values that can only be resolved when a match
        # is found and as such loading them will be delayed until we find a match
        self.deferred_settings = []

        # We used to support only a single component. This allows us to maintain backwards compatibility
        # while also giving the user-facing API a more representative name
        self.components = self.rule.get('jira_components',
                                        self.rule.get('jira_component'))

        # We used to support only a single label. This allows us to maintain backwards compatibility
        # while also giving the user-facing API a more representative name
        self.labels = self.rule.get('jira_labels', self.rule.get('jira_label'))

        self.description = self.rule.get('jira_description', '')
        self.assignee = self.rule.get('jira_assignee')
        self.max_age = self.rule.get('jira_max_age', 30)
        self.priority = self.rule.get('jira_priority')
        self.bump_tickets = self.rule.get('jira_bump_tickets', False)
        self.bump_not_in_statuses = self.rule.get('jira_bump_not_in_statuses')
        self.bump_in_statuses = self.rule.get('jira_bump_in_statuses')
        self.bump_after_inactivity = self.rule.get(
            'jira_bump_after_inactivity', 0)
        self.bump_only = self.rule.get('jira_bump_only', False)
        self.transition = self.rule.get('jira_transition_to', False)
        self.watchers = self.rule.get('jira_watchers')
        self.client = None

        if self.bump_in_statuses and self.bump_not_in_statuses:
            msg = 'Both jira_bump_in_statuses (%s) and jira_bump_not_in_statuses (%s) are set.' % \
                  (','.join(self.bump_in_statuses), ','.join(self.bump_not_in_statuses))
            intersection = list(
                set(self.bump_in_statuses) & set(self.bump_in_statuses))
            if intersection:
                msg = '%s Both have common statuses of (%s). As such, no tickets will ever be found.' % (
                    msg, ','.join(intersection))
            msg += ' This should be simplified to use only one or the other.'
            elastalert_logger.warning(msg)

        self.reset_jira_args()

        try:
            self.client = JIRA(self.server,
                               basic_auth=(self.user, self.password))
            self.get_priorities()
            self.jira_fields = self.client.fields()
            self.get_arbitrary_fields()
        except JIRAError as e:
            # JIRAError may contain HTML, pass along only first 1024 chars
            raise EAException("Error connecting to JIRA: %s" %
                              (str(e)[:1024])).with_traceback(
                                  sys.exc_info()[2])

        self.set_priority()
Exemple #19
0
    def alert(self, matches):
        body = self.create_alert_body(matches)

        # post to victorops
        headers = {'content-type': 'application/json'}
        # set https proxy, if it was provided
        proxies = {
            'https': self.victorops_proxy
        } if self.victorops_proxy else None
        payload = {
            "message_type": self.victorops_message_type,
            "entity_display_name": self.victorops_entity_display_name,
            "monitoring_tool": "ElastAlert",
            "state_message": body
        }
        if self.victorops_entity_id:
            payload["entity_id"] = self.victorops_entity_id

        try:
            response = requests.post(self.url,
                                     data=json.dumps(payload,
                                                     cls=DateTimeEncoder),
                                     headers=headers,
                                     proxies=proxies)
            response.raise_for_status()
        except RequestException as e:
            raise EAException("Error posting to VictorOps: %s" % e)
        elastalert_logger.info("Trigger sent to VictorOps")
Exemple #20
0
    def alert(self, matches):
        body = self.create_alert_body(matches)

        body = self.format_body(body)
        # post to Teams
        headers = {'content-type': 'application/json'}
        # set https proxy, if it was provided
        proxies = {
            'https': self.ms_teams_proxy
        } if self.ms_teams_proxy else None
        payload = {
            '@type': 'MessageCard',
            '@context': 'http://schema.org/extensions',
            'summary': self.ms_teams_alert_summary,
            'title': self.create_title(matches),
            'text': body
        }
        if self.ms_teams_theme_color != '':
            payload['themeColor'] = self.ms_teams_theme_color

        for url in self.ms_teams_webhook_url:
            try:
                response = requests.post(url,
                                         data=json.dumps(payload,
                                                         cls=DateTimeEncoder),
                                         headers=headers,
                                         proxies=proxies)
                response.raise_for_status()
            except RequestException as e:
                raise EAException("Error posting to ms teams: %s" % e)
        elastalert_logger.info("Alert sent to MS Teams")
    def alert(self, matches):
        headers = {
            'content-type': 'application/json',
            'Accept': 'application/json;charset=utf-8',
        }

        body = self.create_alert_body(matches)

        data = {
            "at": {
                "atMobiles": self.mobiles,
                "isAtAll": self.at_all,
            },
            "msgtype": self.msgtype,
        }
        if self.msgtype == 'markdown':
            content = {'title': self.create_title(matches), 'text': body}
        else:
            content = {'content': body}

        data[self.msgtype] = content

        webhook_url = 'https://oapi.dingtalk.com/robot/send?access_token=%s' % (
            self.access_token)

        if self.security_type == "sign":
            webhook_url = '%s%s' % (webhook_url, self.sign())

        try:
            response = requests.post(webhook_url,
                                     data=json.dumps(data),
                                     headers=headers)
            response.raise_for_status()
        except RequestException as e:
            raise EAException("send message has error: %s" % e)
Exemple #22
0
    def get_names(self, conf, use_rule=None):
        # Passing a filename directly can bypass rules_folder and .yaml checks
        if use_rule and os.path.isfile(use_rule):
            return [use_rule]

        # In case of a bad type, convert string to list:
        rule_folders = conf['rules_folder'] if isinstance(
            conf['rules_folder'], list) else [conf['rules_folder']]
        rule_files = []
        if 'scan_subdirectories' in conf and conf['scan_subdirectories']:
            for ruledir in rule_folders:
                if not os.path.exists(ruledir):
                    raise EAException(
                        'Specified rule_folder does not exist: %s ' % ruledir)
                for root, folders, files in os.walk(ruledir, followlinks=True):
                    # Openshift/k8s configmap fix for ..data and ..2021_05..date directories that loop with os.walk()
                    folders[:] = [d for d in folders if not d.startswith('..')]
                    for filename in files:
                        if use_rule and use_rule != filename:
                            continue
                        if self.is_yaml(filename):
                            rule_files.append(os.path.join(root, filename))
        else:
            for ruledir in rule_folders:
                if not os.path.isdir(ruledir):
                    continue
                for file in os.scandir(ruledir):
                    fullpath = os.path.join(ruledir, file.name)
                    if os.path.isfile(fullpath) and self.is_yaml(file.name):
                        rule_files.append(fullpath)
        return rule_files
Exemple #23
0
    def alert(self, matches):
        body = self.create_alert_body(matches)
        body = self.format_body(body)
        headers = {'content-type': 'application/json'}
        proxies = {
            'https': self.rocket_chat_proxy
        } if self.rocket_chat_proxy else None
        payload = {
            'username':
            self.rocket_chat_username_override,
            'text':
            self.rocket_chat_text_string,
            'attachments': [{
                'color': self.rocket_chat_msg_color,
                'title': self.create_title(matches),
                'text': body,
                'fields': []
            }]
        }

        # if we have defined fields, populate noteable fields for the alert
        if self.rocket_chat_alert_fields != '':
            payload['attachments'][0]['fields'] = self.populate_fields(matches)

        if self.rocket_chat_emoji_override != '':
            payload['emoji'] = self.rocket_chat_emoji_override

        if self.rocket_chat_attach_kibana_discover_url:
            kibana_discover_url = lookup_es_key(matches[0],
                                                'kibana_discover_url')
            if kibana_discover_url:
                payload['attachments'].append({
                    'color': self.rocket_chat_kibana_discover_color,
                    'title': self.rocket_chat_kibana_discover_title,
                    'title_link': kibana_discover_url
                })

        for url in self.rocket_chat_webhook_url:
            for channel_override in self.rocket_chat_channel_override:
                try:
                    if self.rocket_chat_ca_certs:
                        verify = self.rocket_chat_ca_certs
                    else:
                        verify = not self.rocket_chat_ignore_ssl_errors
                    if self.rocket_chat_ignore_ssl_errors:
                        requests.packages.urllib3.disable_warnings()
                    payload['channel'] = channel_override
                    response = requests.post(url,
                                             data=json.dumps(
                                                 payload, cls=DateTimeEncoder),
                                             headers=headers,
                                             verify=verify,
                                             proxies=proxies,
                                             timeout=self.rocket_chat_timeout)
                    warnings.resetwarnings()
                    response.raise_for_status()
                except RequestException as e:
                    raise EAException("Error posting to Rocket.Chat: %s" % e)
            elastalert_logger.info("Alert sent to Rocket.Chat")
Exemple #24
0
    def alert(self, matches):
        # Build TheHive alert object, starting with some defaults, updating with any
        # user-specified config
        alert_config = {
            'artifacts': [],
            'customFields': {},
            'date': int(time.time()) * 1000,
            'description': self.create_alert_body(matches),
            'sourceRef': str(uuid.uuid4()),
            'tags': [],
            'title': self.create_title(matches),
        }
        alert_config.update(self.rule.get('hive_alert_config', {}))

        # Iterate through each match found, populating the alert tags and observables as required
        tags = set()
        artifacts = []
        for match in matches:
            artifacts = artifacts + self.load_observable_artifacts(match)
            tags.update(self.load_tags(alert_config['tags'], match))

        alert_config['artifacts'] = artifacts
        alert_config['tags'] = list(tags)

        # Populate the customFields
        alert_config['customFields'] = self.load_custom_fields(
            alert_config['customFields'], matches[0])

        # POST the alert to TheHive
        connection_details = self.rule['hive_connection']

        api_key = connection_details.get('hive_apikey', '')
        hive_host = connection_details.get('hive_host', 'http://localhost')
        hive_port = connection_details.get('hive_port', 9000)
        proxies = connection_details.get('hive_proxies', {
            'http': '',
            'https': ''
        })
        verify = connection_details.get('hive_verify', False)

        alert_body = json.dumps(alert_config, indent=4, sort_keys=True)
        req = f'{hive_host}:{hive_port}/api/alert'
        headers = {
            'Content-Type': 'application/json',
            'Authorization': f'Bearer {api_key}'
        }

        try:
            response = requests.post(req,
                                     headers=headers,
                                     data=alert_body,
                                     proxies=proxies,
                                     verify=verify)
            response.raise_for_status()
        except RequestException as e:
            raise EAException(f"Error posting to TheHive: {e}")
        elastalert_logger.info("Alert sent to TheHive")
Exemple #25
0
    def load_modules(self, rule, args=None):
        """ Loads things that could be modules. Enhancements, alerts and rule type. """
        # Set match enhancements
        match_enhancements = []
        for enhancement_name in rule.get('match_enhancements', []):
            if enhancement_name in dir(enhancements):
                enhancement = getattr(enhancements, enhancement_name)
            else:
                enhancement = get_module(enhancement_name)
            if not issubclass(enhancement, enhancements.BaseEnhancement):
                raise EAException(
                    "Enhancement module %s not a subclass of BaseEnhancement" %
                    enhancement_name)
            match_enhancements.append(enhancement(rule))
        rule['match_enhancements'] = match_enhancements

        # Convert rule type into RuleType object
        if rule['type'] in self.rules_mapping:
            rule['type'] = self.rules_mapping[rule['type']]
        else:
            rule['type'] = get_module(rule['type'])
            if not issubclass(rule['type'], ruletypes.RuleType):
                raise EAException(
                    'Rule module %s is not a subclass of RuleType' %
                    (rule['type']))

        # Make sure we have required alert and type options
        reqs = rule['type'].required_options

        if reqs - frozenset(list(rule.keys())):
            raise EAException('Missing required option(s): %s' %
                              (', '.join(reqs - frozenset(list(rule.keys())))))
        # Instantiate rule
        try:
            rule['type'] = rule['type'](rule, args)
        except (KeyError, EAException) as e:
            raise EAException('Error initializing rule %s: %s' %
                              (rule['name'], e)).with_traceback(
                                  sys.exc_info()[2])
        # Instantiate alerts only if we're not in debug mode
        # In debug mode alerts are not actually sent so don't bother instantiating them
        if not args or not args.debug:
            rule['alert'] = self.load_alerts(rule, alert_field=rule['alert'])
Exemple #26
0
    def alert(self, matches):
        body = self.create_alert_body(matches)

        to_addr = self.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
                    ]

        try:
            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')

            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 Amazon SES: %s" % e)

        elastalert_logger.info("Sent Amazon SES to %s" % (to_addr, ))
Exemple #27
0
    def __init__(self, rule):
        super(HttpPostAlerter, self).__init__(rule)
        self.url = self.rule['http_post_url']

        self.post_data = self.rule.get('http_post_data')
        self.send_as_json = self.rule.get('http_post_data_as_json', True)
        self.headers = self.rule.get('http_post_headers')
        self.include_alert_subject = self.rule.get(
            'http_post_include_alert_subject', False)
        self.include_alert_body = self.rule.get('http_post_include_alert_body',
                                                False)
        self.new_style_string_format = self.rule.get('new_style_string_format',
                                                     False)

        if self.post_data and not isinstance(self.post_data,
                                             (basestring, dict)):
            raise EAException(
                'http_post_data must be either a string or a dict.')
        if self.headers and not isinstance(self.headers, dict):
            raise EAException('http_post_headers must be a dict.')
Exemple #28
0
    def alert(self, matches):
        body = self.create_alert_body(matches)

        body = self.format_body(body)
        # post to Teams
        headers = {'content-type': 'application/json'}

        if self.ms_teams_ca_certs:
            verify = self.ms_teams_ca_certs
        else:
            verify = not self.ms_teams_ignore_ssl_errors
        if self.ms_teams_ignore_ssl_errors:
            requests.packages.urllib3.disable_warnings()

        # set https proxy, if it was provided
        proxies = {'https': self.ms_teams_proxy} if self.ms_teams_proxy else None
        payload = {
            '@type': 'MessageCard',
            '@context': 'http://schema.org/extensions',
            'summary': self.ms_teams_alert_summary,
            'title': self.create_title(matches),
            'sections': [{'text': body}],
        }

        if self.ms_teams_alert_facts != '':
            payload['sections'][0]['facts'] = self.populate_facts(matches)

        if self.ms_teams_theme_color != '':
            payload['themeColor'] = self.ms_teams_theme_color

        if self.ms_teams_attach_kibana_discover_url:
            kibana_discover_url = lookup_es_key(matches[0], 'kibana_discover_url')
            if kibana_discover_url:
                payload['potentialAction'] = [
                    {
                        '@type': 'OpenUri',
                        'name': self.ms_teams_kibana_discover_title,
                        'targets': [
                            {
                                'os': 'default',
                                'uri': kibana_discover_url,
                            }
                        ],
                    }
                ]

        for url in self.ms_teams_webhook_url:
            try:
                response = requests.post(url, data=json.dumps(payload, cls=DateTimeEncoder),
                                         headers=headers, proxies=proxies, verify=verify)
                response.raise_for_status()
            except RequestException as e:
                raise EAException("Error posting to MS Teams: %s" % e)
        elastalert_logger.info("Alert sent to MS Teams")
 def get_tenant_access_token(self):
     body = {"app_id": self.app_id, "app_secret": self.app_secret}
     try:
         r = get(
             "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/",
             json=body).json()
         if r['code'] == 0:
             # elastalert_logger.debug("token: " + r['tenant_access_token'])
             return r['tenant_access_token']
     except Exception as e:
         raise EAException("Get tenant_access_token failed:".format(e))
Exemple #30
0
    def alert(self, matches):
        body = ''
        title = u'%s' % (self.create_title(matches))
        for match in matches:
            body += str(BasicMatchString(self.rule, match))
            if len(matches) > 1:
                body += '\n----------------------------------------\n'
        if len(body) > 2047:
            body = body[
                0:
                1950] + '\n *message was cropped according to discord embed description limits!*'

        proxies = {'https': self.discord_proxy} if self.discord_proxy else None
        auth = HTTPProxyAuth(
            self.discord_proxy_login,
            self.discord_proxy_password) if self.discord_proxy_login else None
        headers = {"Content-Type": "application/json"}

        data = {}
        data["content"] = "%s %s %s" % (self.discord_emoji_title, title,
                                        self.discord_emoji_title)
        data["embeds"] = []
        embed = {}
        embed["description"] = "%s" % (body)
        embed["color"] = (self.discord_embed_color)

        if self.discord_embed_footer:
            embed["footer"] = {}
            embed["footer"]["text"] = (self.discord_embed_footer
                                       ) if self.discord_embed_footer else None
            embed["footer"]["icon_url"] = (
                self.discord_embed_icon_url
            ) if self.discord_embed_icon_url else None
        else:
            None

        data["embeds"].append(embed)

        try:
            response = requests.post(self.discord_webhook_url,
                                     data=json.dumps(data),
                                     headers=headers,
                                     proxies=proxies,
                                     auth=auth)
            warnings.resetwarnings()
            response.raise_for_status()
        except RequestException as e:
            raise EAException(
                "Error posting to Discord: %s. Details: %s" %
                (e, "" if e.response is None else e.response.text))

        elastalert_logger.info("Alert sent to the webhook %s" %
                               self.discord_webhook_url)