Skip to content

Commit

Permalink
More generalization for the Lambda module (airbnb#638)
Browse files Browse the repository at this point in the history
  • Loading branch information
austinbyers authored Mar 16, 2018
1 parent 869b337 commit 5289ecd
Show file tree
Hide file tree
Showing 26 changed files with 324 additions and 170 deletions.
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ confidence=
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=fixme,I0011,E1102
disable=similarities,fixme,I0011,E1102

# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
Expand Down
19 changes: 12 additions & 7 deletions conf/lambda.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@
"log_retention_days": 14,
"memory": 128,
"metric_alarms": {
"enabled": true,
"errors_alarm_evaluation_periods": 1,
"errors_alarm_period_secs": 120,
"errors_alarm_threshold": 0,
"throttles_alarm_evaluation_periods": 1,
"throttles_alarm_period_secs": 120,
"throttles_alarm_threshold": 0
"errors": {
"enabled": true,
"evaluation_periods": 1,
"period_secs": 120,
"threshold": 0
},
"throttles": {
"enabled": true,
"evaluation_periods": 1,
"period_secs": 120,
"threshold": 0
}
},
"source_bucket": "PREFIX_GOES_HERE.streamalert.source",
"source_current_hash": "<auto_generated>",
Expand Down
15 changes: 8 additions & 7 deletions stream_alert_cli/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ class SuppressNoise(logging.Filter):

def filter(self, record):
suppressed_messages = (
'Starting download from S3*',
'*Starting download from S3*',
'Completed download in*',
'*triggered an alert on log type*',
'*triggered alert*',
'*Firehose*',
'Got * normalized records'
)
Expand Down Expand Up @@ -64,7 +64,8 @@ def filter(self, record):
continue
logging.getLogger(logger).setLevel(logging.CRITICAL)

def get_log_memory_hanlder():

def get_log_memory_handler():
"""Get a logging MemoryHandler with a default buffer size of 1000
We don't care about assigning a target to this handler since these logs
will not actually be written out to disk, etc
Expand All @@ -73,12 +74,12 @@ def get_log_memory_hanlder():
logging.handlers.MemoryHandler: In memory logging handler that caches
all messages going through the root logger to a buffer
"""
log_mem_hanlder = logging.handlers.MemoryHandler(1000)
log_mem_handler = logging.handlers.MemoryHandler(1000)

# Add a filter to suppress everything that is not an error
log_mem_hanlder.addFilter(SuppressNonErrors())
log_mem_handler.addFilter(SuppressNonErrors())

# Add the MemoryHandler to the root logger to capture all logs in all loggers
logging.getLogger().addHandler(log_mem_hanlder)
logging.getLogger().addHandler(log_mem_handler)

return log_mem_hanlder
return log_mem_handler
77 changes: 8 additions & 69 deletions stream_alert_cli/terraform/alert_processor.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
"""
Copyright 2017-present, Airbnb Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from stream_alert_cli.terraform._common import infinitedict, monitoring_topic_arn
from stream_alert.shared import ALERT_PROCESSOR_NAME
from stream_alert_cli.terraform.common import infinitedict
from stream_alert_cli.terraform.lambda_module import generate_lambda


def generate_alert_processor(config):
Expand All @@ -19,50 +24,10 @@ def generate_alert_processor(config):
Args:
config (dict): The loaded config from the 'conf/' directory
Example Alert Processor config:
"alert_processor_config": {
"current_version": "$LATEST",
"handler": "main.handler",
"log_level": "info",
"log_retention_days": 14,
"memory": 128,
"metric_alarms": {
"enabled": True,
"errors_alarm_threshold": 0,
"errors_alarm_evaluation_periods": 1,
"errors_alarm_period_secs": 120,
"throttles_alarm_threshold": 0,
"throttles_alarm_evaluation_periods": 1,
"throttles_alarm_period_secs": 120
},
"source_bucket": "BUCKET",
"source_object_key": "OBJECT_KEY",
"outputs": {
"aws-lambda": [
"lambda_function_name"
],
"aws-s3": [
"s3.bucket.name"
]
},
"timeout": 10,
"vpc_config": {
"security_group_ids": [
"sg-id"
],
"subnet_ids": [
"subnet-id"
]
}
}
Returns:
dict: Alert Processor dict to be marshaled to JSON
"""
prefix = config['global']['account']['prefix']
alert_processor_config = config['lambda']['alert_processor_config']
alarms_config = alert_processor_config.get('metric_alarms', {})
vpc_config = alert_processor_config.get('vpc_config', {})

result = infinitedict()

Expand All @@ -84,33 +49,7 @@ def generate_alert_processor(config):
}

# Set variables for the Lambda module
lambda_module = {
'source': 'modules/tf_lambda',
'function_name': '{}_streamalert_alert_processor'.format(prefix),
'description': 'StreamAlert Alert Processor',
'handler': alert_processor_config['handler'],
'memory_size_mb': alert_processor_config['memory'],
'timeout_sec': alert_processor_config['timeout'],
'source_bucket': alert_processor_config['source_bucket'],
'source_object_key': alert_processor_config['source_object_key'],
'environment_variables': {
'LOGGER_LEVEL': alert_processor_config.get('log_level', 'info')
},
'vpc_subnet_ids': vpc_config.get('subnet_ids', []),
'vpc_security_group_ids': vpc_config.get('security_group_ids', []),
'aliased_version': alert_processor_config['current_version'],
'log_retention_days': alert_processor_config.get('log_retention_days', 14)
}

# Add metric alarms configuration
if alarms_config.get('enabled', True):
lambda_module['enable_metric_alarms'] = True
lambda_module['alarm_actions'] = [monitoring_topic_arn(config)]
for var_name, var_value in alarms_config.iteritems():
if 'errors' in var_name or 'throttles' in var_name:
lambda_module[var_name] = var_value
else:
lambda_module['enable_metric_alarms'] = False
result['module']['alert_processor_lambda'] = generate_lambda(
ALERT_PROCESSOR_NAME, config, {'ALERTS_TABLE': '{}_streamalert_alerts'.format(prefix)})

result['module']['alert_processor_lambda'] = lambda_module
return result
2 changes: 1 addition & 1 deletion stream_alert_cli/terraform/app_integrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"""
import json

from stream_alert_cli.terraform._common import DEFAULT_SNS_MONITORING_TOPIC
from stream_alert_cli.terraform.common import DEFAULT_SNS_MONITORING_TOPIC


def generate_app_integrations(cluster_name, cluster_dict, config):
Expand Down
2 changes: 1 addition & 1 deletion stream_alert_cli/terraform/athena.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
limitations under the License.
"""
from stream_alert.shared import metrics
from stream_alert_cli.terraform._common import DEFAULT_SNS_MONITORING_TOPIC, infinitedict
from stream_alert_cli.terraform.common import DEFAULT_SNS_MONITORING_TOPIC, infinitedict


def generate_athena(config):
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions stream_alert_cli/terraform/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from stream_alert.shared.metrics import FUNC_PREFIXES
from stream_alert_cli.logger import LOGGER_CLI
from stream_alert_cli.terraform._common import (
from stream_alert_cli.terraform.common import (
DEFAULT_SNS_MONITORING_TOPIC,
InvalidClusterName,
infinitedict,
Expand All @@ -44,7 +44,7 @@
from stream_alert_cli.terraform.threat_intel_downloader import generate_threat_intel_downloader

RESTRICTED_CLUSTER_NAMES = ('main', 'athena')
TERRAFORM_VERSIONS = {'application': '~> 0.10.6', 'provider': {'aws': '~> 1.5.0'}}
TERRAFORM_VERSIONS = {'application': '~> 0.10.6', 'provider': {'aws': '~> 1.11.0'}}


def generate_s3_bucket(**kwargs):
Expand Down
146 changes: 146 additions & 0 deletions stream_alert_cli/terraform/lambda_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
"""
Copyright 2017-present, Airbnb Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from stream_alert import shared
from stream_alert_cli.terraform.common import monitoring_topic_arn


def _lambda_config(function_name, config):
"""Find the config specific to this Lambda function."""
if function_name == shared.ALERT_PROCESSOR_NAME:
return config['lambda']['alert_processor_config']
else:
raise NotImplementedError(
'Lambda modules are not yet supported for {}'.format(function_name))


def _tf_metric_alarms(lambda_config, sns_arn):
"""Compute metric alarm Terraform configuration from the Lambda config."""
result = {}
alarms_config = lambda_config.get('metric_alarms', {})
if not alarms_config:
return result

result['alarm_actions'] = [sns_arn]

for alarm_type in ['errors', 'throttles']:
settings = alarms_config.get(alarm_type)
if not settings:
continue

for key in ['enabled', 'evaluation_periods', 'period_secs', 'threshold']:
if key in settings:
result['{}_alarm_{}'.format(alarm_type, key)] = settings[key]

return result


def _tf_vpc_config(lambda_config):
"""Compute VPC configuration from the Lambda config."""
result = {}
vpc_config = lambda_config.get('vpc_config', {})
if not vpc_config:
return result

if 'security_group_ids' in vpc_config:
result['vpc_security_group_ids'] = vpc_config['security_group_ids']
if 'subnet_ids' in vpc_config:
result['vpc_subnet_ids'] = vpc_config['subnet_ids']

return result


def generate_lambda(function_name, config, environment=None):
"""Generate an instance of the Lambda Terraform module.
Args:
function_name (str): Name of the Lambda function (e.g. 'alert_processor')
config (dict): Parsed config from conf/
environment (dict): Optional environment variables to specify.
LOGGER_LEVEL is included automatically.
Example Lambda config:
{
"concurrency_limit": 1,
"current_version": "$LATEST",
"handler": "main.handler",
"log_level": "info",
"log_retention_days": 14,
"memory": 128,
"metric_alarms": {
"errors": {
"enabled": true,
"evaluation_periods": 1,
"period_secs": 120,
"threshold": 0
},
"throttles": {
"enabled": true,
"evaluation_periods": 1,
"period_secs": 120,
"threshold": 0
}
},
"schedule_expression": "rate(5 minutes)",
"source_bucket": "BUCKET",
"source_object_key": "OBJECT_KEY",
"timeout": 10,
"vpc_config": {
"security_group_ids": [
"sg-id"
],
"subnet_ids": [
"subnet-id"
]
}
}
Returns:
dict: Terraform config for an instance of the tf_lambda module.
"""
lambda_config = _lambda_config(function_name, config)

# Add logger level to any custom environment variables
environment_variables = {
'LOGGER_LEVEL': lambda_config.get('log_level', 'info')
}
if environment:
environment_variables.update(environment)

lambda_module = {
'source': 'modules/tf_lambda',
'function_name': '{}_streamalert_{}'.format(config['global']['account']['prefix'],
function_name),
'description': 'StreamAlert {}'.format(function_name.replace('_', ' ').title()),
'handler': lambda_config['handler'],
'memory_size_mb': lambda_config['memory'],
'timeout_sec': lambda_config['timeout'],
'source_bucket': lambda_config['source_bucket'],
'source_object_key': lambda_config['source_object_key'],
'environment_variables': environment_variables,
'aliased_version': lambda_config['current_version'],
}

# Include optional keys only if they are defined (otherwise use the module defaults)
for key in ['concurrency_limit', 'log_retention_days', 'schedule_expression']:
if key in lambda_config:
lambda_module[key] = lambda_config[key]

# Add metric alarms to the Lambda module definition
lambda_module.update(_tf_metric_alarms(lambda_config, monitoring_topic_arn(config)))

# Add VPC config to the Lambda module definition
lambda_module.update(_tf_vpc_config(lambda_config))

return lambda_module
2 changes: 1 addition & 1 deletion stream_alert_cli/terraform/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"""
from stream_alert.shared import metrics
from stream_alert_cli.logger import LOGGER_CLI
from stream_alert_cli.terraform._common import monitoring_topic_arn
from stream_alert_cli.terraform.common import monitoring_topic_arn


def generate_cloudwatch_metric_filters(cluster_name, cluster_dict, config):
Expand Down
2 changes: 1 addition & 1 deletion stream_alert_cli/terraform/monitoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
limitations under the License.
"""
from stream_alert_cli.logger import LOGGER_CLI
from stream_alert_cli.terraform._common import monitoring_topic_arn
from stream_alert_cli.terraform.common import monitoring_topic_arn


def generate_monitoring(cluster_name, cluster_dict, config):
Expand Down
2 changes: 1 addition & 1 deletion stream_alert_cli/terraform/threat_intel_downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
"""
from stream_alert_cli.terraform._common import DEFAULT_SNS_MONITORING_TOPIC, infinitedict
from stream_alert_cli.terraform.common import DEFAULT_SNS_MONITORING_TOPIC, infinitedict

def generate_threat_intel_downloader(config):
"""Generate Threat Intel Downloader Terrafrom
Expand Down
Loading

0 comments on commit 5289ecd

Please sign in to comment.