hasher-matcher-actioner/terraform/actions/main.tf (356 lines of code) (raw):

# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved locals { common_tags = { "HMAPrefix" = var.prefix } } # Define a queue for the InputSNS topic to push messages to. resource "aws_sqs_queue" "matches_queue_dlq" { name_prefix = "${var.prefix}-matches-deadletter-" visibility_timeout_seconds = 300 message_retention_seconds = var.deadletterqueue_message_retention_seconds tags = merge( var.additional_tags, { Name = "MatchesDLQ" } ) } resource "aws_sqs_queue" "matches_queue" { name_prefix = "${var.prefix}-matches-" visibility_timeout_seconds = 300 redrive_policy = jsonencode({ deadLetterTargetArn = aws_sqs_queue.matches_queue_dlq.arn maxReceiveCount = 4 }) tags = merge( var.additional_tags, local.common_tags ) } data "aws_iam_policy_document" "matches_queue" { statement { effect = "Allow" actions = ["sqs:SendMessage"] resources = [aws_sqs_queue.matches_queue.arn] principals { type = "Service" identifiers = ["sns.amazonaws.com"] } condition { test = "ArnEquals" variable = "aws:SourceArn" values = [var.matches_sns_topic_arn] } } } resource "aws_sqs_queue_policy" "matches_queue" { queue_url = aws_sqs_queue.matches_queue.id policy = data.aws_iam_policy_document.matches_queue.json } # Blocks dedicated to the queue ends. # Connects InputSNS -> SQS Queue. InputSNS is an externally configured SNS Topic # which collects matches from various matcher lambdas. resource "aws_sns_topic_subscription" "new_matches_topic" { topic_arn = var.matches_sns_topic_arn protocol = "sqs" endpoint = aws_sqs_queue.matches_queue.arn } # Set up the queue for sending messages from the action evaluator to the action performer resource "aws_sqs_queue" "actions_queue_dlq" { name_prefix = "${var.prefix}-actions-deadletter-" visibility_timeout_seconds = 300 message_retention_seconds = var.deadletterqueue_message_retention_seconds tags = merge( var.additional_tags, { Name = "ActionsDLQ" } ) } resource "aws_sqs_queue" "actions_queue" { name_prefix = "${var.prefix}-actions" visibility_timeout_seconds = 300 redrive_policy = jsonencode({ deadLetterTargetArn = aws_sqs_queue.actions_queue_dlq.arn maxReceiveCount = 4 }) tags = merge( var.additional_tags, { Name = "ActionsQueue" } ) } # Set up the queue for sending messages from the action evaluator to the writebacker resource "aws_sqs_queue" "writebacks_queue_dlq" { name_prefix = "${var.prefix}-writebacks-deadletter-" visibility_timeout_seconds = 300 message_retention_seconds = var.deadletterqueue_message_retention_seconds tags = merge( var.additional_tags, { Name = "WritebacksDLQ" } ) } resource "aws_sqs_queue" "writebacks_queue" { name_prefix = "${var.prefix}-writebacks" visibility_timeout_seconds = 300 redrive_policy = jsonencode({ deadLetterTargetArn = aws_sqs_queue.writebacks_queue_dlq.arn maxReceiveCount = 4 }) tags = merge( var.additional_tags, { Name = "WritebacksQueue" } ) } # Lambda functions # Action evaluator evaluates which actions to perform as a result of a match. resource "aws_lambda_function" "action_evaluator" { function_name = "${var.prefix}_action_evaluator" package_type = "Image" role = aws_iam_role.action_evaluator.arn image_uri = var.lambda_docker_info.uri image_config { command = [var.lambda_docker_info.commands.action_evaluator] } timeout = 300 memory_size = 512 environment { variables = { ACTIONS_QUEUE_URL = aws_sqs_queue.actions_queue.id, WRITEBACKS_QUEUE_URL = aws_sqs_queue.writebacks_queue.id, CONFIG_TABLE_NAME = var.config_table.name, DYNAMODB_TABLE = var.datastore.name } } } # Action performer performs actions decided on by the action evaluator. resource "aws_lambda_function" "action_performer" { function_name = "${var.prefix}_action_performer" package_type = "Image" role = aws_iam_role.action_performer.arn image_uri = var.lambda_docker_info.uri image_config { command = [var.lambda_docker_info.commands.action_performer] } timeout = 300 memory_size = 512 environment { variables = { CONFIG_TABLE_NAME = var.config_table.name, DYNAMODB_TABLE = var.datastore.name } } } # Writebacker sends data back to the data source (eg. ThreatExchange) resource "aws_lambda_function" "writebacker" { function_name = "${var.prefix}_writebacker" package_type = "Image" role = aws_iam_role.writebacker.arn image_uri = var.lambda_docker_info.uri image_config { command = [var.lambda_docker_info.commands.writebacker] } environment { variables = { THREAT_EXCHANGE_API_TOKEN_SECRET_NAME = var.te_api_token_secret.name CONFIG_TABLE_NAME = var.config_table.name, } } timeout = 300 memory_size = 512 } # Log groups resource "aws_cloudwatch_log_group" "action_evaluator" { name = "/aws/lambda/${aws_lambda_function.action_evaluator.function_name}" retention_in_days = var.log_retention_in_days tags = merge( var.additional_tags, { Name = "ActionEvaluatorLambdaLogGroup" } ) } resource "aws_cloudwatch_log_group" "action_performer" { name = "/aws/lambda/${aws_lambda_function.action_performer.function_name}" retention_in_days = var.log_retention_in_days tags = merge( var.additional_tags, { Name = "ActionPerformerLambdaLogGroup" } ) } resource "aws_cloudwatch_log_group" "writebacker" { name = "/aws/lambda/${aws_lambda_function.writebacker.function_name}" retention_in_days = var.log_retention_in_days tags = merge( var.additional_tags, { Name = "WritebackerLambdaLogGroup" } ) } # Common "assume role" policy document data "aws_iam_policy_document" "lambda_assume_role" { statement { effect = "Allow" actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["lambda.amazonaws.com"] } } } # Role and policy for action evaluator resource "aws_iam_role" "action_evaluator" { name_prefix = "${var.prefix}_action_evaluator" assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json tags = var.additional_tags } resource "aws_iam_policy" "action_evaluator" { name_prefix = "${var.prefix}_action_evaluator_role_policy" policy = data.aws_iam_policy_document.action_evaluator.json } data "aws_iam_policy_document" "action_evaluator" { statement { effect = "Allow" actions = ["sqs:GetQueueAttributes", "sqs:ReceiveMessage", "sqs:DeleteMessage"] resources = [aws_sqs_queue.matches_queue.arn] } statement { effect = "Allow" actions = ["sqs:SendMessage"] resources = [aws_sqs_queue.actions_queue.arn, aws_sqs_queue.writebacks_queue.arn] } statement { effect = "Allow" actions = ["dynamodb:Scan"] resources = [var.config_table.arn] } statement { effect = "Allow" actions = [ "logs:CreateLogStream", "logs:PutLogEvents", "logs:DescribeLogStreams" ] resources = ["${aws_cloudwatch_log_group.action_evaluator.arn}:*"] } statement { effect = "Allow" actions = ["cloudwatch:PutMetricData"] resources = ["*"] } statement { effect = "Allow" actions = [ "dynamodb:Get*", ] resources = [var.datastore.arn] } } resource "aws_iam_role_policy_attachment" "action_evaluator" { role = aws_iam_role.action_evaluator.name policy_arn = aws_iam_policy.action_evaluator.arn } # Role and policy for action performer resource "aws_iam_role" "action_performer" { name_prefix = "${var.prefix}_action_performer" assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json tags = var.additional_tags } resource "aws_iam_policy" "action_performer" { name_prefix = "${var.prefix}_action_performer_role_policy" policy = data.aws_iam_policy_document.action_performer.json } data "aws_iam_policy_document" "action_performer" { statement { effect = "Allow" actions = ["sqs:GetQueueAttributes", "sqs:ReceiveMessage", "sqs:DeleteMessage"] resources = [aws_sqs_queue.actions_queue.arn] } statement { effect = "Allow" actions = [ "logs:CreateLogStream", "logs:PutLogEvents", "logs:DescribeLogStreams" ] resources = ["${aws_cloudwatch_log_group.action_performer.arn}:*"] } statement { effect = "Allow" actions = ["cloudwatch:PutMetricData"] resources = ["*"] } statement { effect = "Allow" actions = ["secretsmanager:GetSecretValue"] resources = [var.te_api_token_secret.arn] } statement { effect = "Allow" actions = [ "dynamodb:Get*", "dynamodb:PutItem" ] resources = [var.config_table.arn, var.datastore.arn] } } resource "aws_iam_role_policy_attachment" "action_performer" { role = aws_iam_role.action_performer.name policy_arn = aws_iam_policy.action_performer.arn } # Role and policy for action writebacker resource "aws_iam_role" "writebacker" { name_prefix = "${var.prefix}_writebacker" assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json tags = var.additional_tags } resource "aws_iam_policy" "writebacker" { name_prefix = "${var.prefix}_writebacker_role_policy" policy = data.aws_iam_policy_document.writebacker.json } data "aws_iam_policy_document" "writebacker" { statement { effect = "Allow" actions = ["sqs:GetQueueAttributes", "sqs:ReceiveMessage", "sqs:DeleteMessage"] resources = [aws_sqs_queue.writebacks_queue.arn] } statement { effect = "Allow" actions = [ "logs:CreateLogStream", "logs:PutLogEvents", "logs:DescribeLogStreams" ] resources = ["${aws_cloudwatch_log_group.writebacker.arn}:*"] } statement { effect = "Allow" actions = ["cloudwatch:PutMetricData"] resources = ["*"] } statement { effect = "Allow" actions = ["secretsmanager:GetSecretValue"] resources = [var.te_api_token_secret.arn] } statement { effect = "Allow" actions = ["dynamodb:GetItem"] resources = [var.config_table.arn] } } resource "aws_iam_role_policy_attachment" "writebacker" { role = aws_iam_role.writebacker.name policy_arn = aws_iam_policy.writebacker.arn } # Connect sqs -> lambda resource "aws_lambda_event_source_mapping" "matches_queue_to_action_evaluator" { event_source_arn = aws_sqs_queue.matches_queue.arn function_name = aws_lambda_function.action_evaluator.arn batch_size = var.queue_batch_size maximum_batching_window_in_seconds = var.queue_window_in_seconds } resource "aws_lambda_event_source_mapping" "actions_queue_to_action_performer" { event_source_arn = aws_sqs_queue.actions_queue.arn function_name = aws_lambda_function.action_performer.arn batch_size = var.queue_batch_size maximum_batching_window_in_seconds = var.queue_window_in_seconds } resource "aws_lambda_event_source_mapping" "writebacks_queue_to_writebacker" { event_source_arn = aws_sqs_queue.writebacks_queue.arn function_name = aws_lambda_function.writebacker.arn batch_size = var.queue_batch_size maximum_batching_window_in_seconds = var.queue_window_in_seconds }