コード例 #1
0
def rule_staging_handler(options, config):
    """Handle operations related to the rule table (listing, updating, etc)

    Args:
        options (argparse.Namespace): Various options needed by subcommand
            handlers
        config (CLIConfig): Loaded configuration from 'conf/' directory

    Returns:
        bool: False if errors occurred, True otherwise
    """
    if options.subcommand == 'enable':
        config.toggle_rule_staging(options.enable)

    table_name = '{}_streamalert_rules'.format(
        config['global']['account']['prefix'])
    if options.subcommand == 'status':
        print RuleTable(table_name).__str__(options.verbose)

    if options.subcommand in {'stage', 'unstage'}:
        stage = (options.subcommand == 'stage')
        table = RuleTable(table_name)
        for rule in options.rules:
            table.toggle_staged_state(rule, stage)

    return True
コード例 #2
0
    def __init__(self):
        config = load_config()
        prefix = config['global']['account']['prefix']

        # Create the rule table class for getting staging information
        self._rule_table = RuleTable('{}_streamalert_rules'.format(prefix))

        athena_config = config['lambda']['athena_partition_refresh_config']

        # Get the name of the athena database to access
        db_name = athena_config.get('database_name', self.STREAMALERT_DATABASE.format(prefix))

        # Get the S3 bucket to store Athena query results
        results_bucket = athena_config.get(
            'results_bucket',
            's3://{}.streamalert.athena-results'.format(prefix)
        )

        self._athena_client = AthenaClient(db_name, results_bucket, self.ATHENA_S3_PREFIX)
        self._current_time = datetime.utcnow()

        # Store the SNS topic arn to send alert stat information to
        self._publisher = StatsPublisher(config, self._athena_client, self._current_time)

        self._staging_stats = dict()
コード例 #3
0
ファイル: rules_engine.py プロジェクト: ykv-name/streamalert
    def _load_rule_table(cls, config):
        """Load and return a RuleTable class for communicating with the DynamoDB rule table

        Args:
            config (dict): Loaded configuration from 'conf/' directory

        Returns:
            rule_table.RuleTable: Loaded frontend for DynamoDB rules table
        """
        # Ensure the rules table is enabled
        rt_config = config['global']['infrastructure']['rules_table']
        if not rt_config.get('enabled', False):
            return

        now = datetime.utcnow()
        refresh_delta = timedelta(
            minutes=rt_config.get('cache_refresh_minutes', 10))

        # The rule table will need 'refreshed' if the refresh interval has been surpassed
        needs_refresh = cls._RULE_TABLE_LAST_REFRESH + refresh_delta < now

        if not needs_refresh:
            LOGGER.debug(
                'Rule table does not need refreshed (last refresh time: %s; '
                'current time: %s)', cls._RULE_TABLE_LAST_REFRESH, now)
            return

        LOGGER.info(
            'Refreshing rule table (last refresh time: %s; current time: %s)',
            cls._RULE_TABLE_LAST_REFRESH, now)

        table_name = '{}_streamalert_rules'.format(
            config['global']['account']['prefix'])
        cls._RULE_TABLE = RuleTable(table_name)
        cls._RULE_TABLE_LAST_REFRESH = now
コード例 #4
0
def rule_table_handler(options, config):
    """Handle operations related to the rule table (listing, updating, etc)

    Args:
        options (argparser.Namespace): Various options needed by subcommand
            handlers
        config (CLIConfig): Loaded configuration from 'conf/' directory
    """
    table_name = '{}_streamalert_rules'.format(
        config['global']['account']['prefix'])
    if options.subcommand == 'status':
        print RuleTable(table_name).__str__(options.verbose)

    if options.subcommand in {'stage', 'unstage'}:
        stage = (options.subcommand == 'stage')
        table = RuleTable(table_name)
        for rule in options.rules:
            table.toggle_staged_state(rule, stage)
コード例 #5
0
    def test_rule_staged_only(self):
        """Rules Engine - Staged Rule"""
        @rule(logs=['cloudwatch:test_match_types'], outputs=['foobar'])
        def rule_staged_only(_):  # pylint: disable=unused-variable
            """Modify context rule"""
            return True

        kinesis_data = json.dumps({
            'account': 123456,
            'region': '123456123456',
            'source': '1.1.1.2',
            'detail': {
                'eventName': 'ConsoleLogin',
                'sourceIPAddress': '1.1.1.2',
                'recipientAccountId': '654321'
            }
        })
        table = RuleTable('table')
        table._remote_rule_info = {'rule_staged_only': {'Staged': True}}
        self.config['global']['infrastructure']['rules_table'][
            'enabled'] = True
        with patch.object(RulesEngine, '_RULE_TABLE', table), \
                patch.object(RulesEngine, '_RULE_TABLE_LAST_REFRESH', datetime.utcnow()):

            self.rules_engine._load_rule_table(self.config)

            # prepare the payloads
            service, entity = 'kinesis', 'test_kinesis_stream'
            raw_record = make_kinesis_raw_record(entity, kinesis_data)
            payload = load_and_classify_payload(self.config, service, entity,
                                                raw_record)

            # process payloads
            alerts, _ = self.rules_engine.run(payload)

            # alert tests
            assert_equal(list(alerts[0].outputs)[0], 'aws-firehose:alerts')
コード例 #6
0
class RulePromoter(object):
    """Run queries to generate statistics on alerts."""

    ATHENA_S3_PREFIX = 'rule_promoter'
    STREAMALERT_DATABASE = '{}_streamalert'

    def __init__(self):
        self._config = load_config()
        prefix = self._config['global']['account']['prefix']

        # Create the rule table class for getting staging information
        self._rule_table = RuleTable('{}_streamalert_rules'.format(prefix))

        athena_config = self._config['lambda'][
            'athena_partition_refresh_config']

        # Get the name of the athena database to access
        db_name = athena_config.get('database_name',
                                    self.STREAMALERT_DATABASE.format(prefix))

        # Get the S3 bucket to store Athena query results
        results_bucket = athena_config.get(
            'results_bucket',
            's3://{}.streamalert.athena-results'.format(prefix))

        self._athena_client = AthenaClient(db_name, results_bucket,
                                           self.ATHENA_S3_PREFIX)
        self._current_time = datetime.utcnow()
        self._staging_stats = dict()

    def _get_staging_info(self):
        """Query the Rule table for rule staging info needed to count each rule's alerts

        Example of rule metadata returned by RuleTable.remote_rule_info():
        {
            'example_rule_name':
                {
                    'Staged': True
                    'StagedAt': datetime.datetime object,
                    'StagedUntil': '2018-04-21T02:23:13.332223Z'
                }
        }
        """
        for rule in sorted(self._rule_table.remote_rule_info):
            info = self._rule_table.remote_rule_info[rule]
            # If the rule is not staged, do not get stats on it
            if not info['Staged']:
                continue

            self._staging_stats[rule] = StagingStatistic(
                info['StagedAt'], info['StagedUntil'], self._current_time,
                rule)

        return len(self._staging_stats) != 0

    def _update_alert_count(self):
        """Transform Athena query results into alert counts for rules_engine

        Args:
            query (str): Athena query to run and wait for results

        Returns:
            dict: Representation of alert counts, where key is the rule name
                and value is the alert count (int) since this rule was staged
        """
        query = StagingStatistic.construct_compound_count_query(
            self._staging_stats.values())
        LOGGER.debug('Running compound query for alert count: \'%s\'', query)
        for page, results in enumerate(
                self._athena_client.query_result_paginator(query)):
            for i, row in enumerate(results['ResultSet']['Rows']):
                if page == 0 and i == 0:  # skip header row included in first page only
                    continue

                row_values = [data.values()[0] for data in row['Data']]
                rule_name, alert_count = row_values[0], int(row_values[1])

                LOGGER.debug('Found %d alerts for rule \'%s\'', alert_count,
                             rule_name)

                self._staging_stats[rule_name].alert_count = alert_count

    def run(self, send_digest):
        """Perform statistic analysis of currently staged rules

        Args:
            send_digest (bool): True if the staging statistics digest should be
                published, False otherwise
        """
        if not self._get_staging_info():
            LOGGER.debug('No staged rules to promote')
            return

        self._update_alert_count()

        self._promote_rules()

        if send_digest:
            publisher = StatsPublisher(self._config, self._athena_client,
                                       self._current_time)
            publisher.publish(self._staging_stats.values())
        else:
            LOGGER.debug('Staging statistics digest will not be sent')

    def _promote_rules(self):
        """Promote any rule that has not resulted in any alerts since being staged"""
        for rule in self._rules_to_be_promoted:
            LOGGER.info('Promoting rule \'%s\' at %s', rule,
                        self._current_time)
            self._rule_table.toggle_staged_state(rule, False)

    @property
    def _rules_to_be_promoted(self):
        """Returns a list of rules that are eligible for promotion"""
        return [
            rule for rule, stat in self._staging_stats.iteritems()
            if self._current_time > stat.staged_until and stat.alert_count == 0
        ]

    @property
    def _rules_failing_promotion(self):
        """Returns a list of rules that are ineligible for promotion"""
        return [
            rule for rule, stat in self._staging_stats.iteritems()
            if stat.alert_count != 0
        ]