diff --git a/_data.tf b/_data.tf index 8fc4b38..038d1e2 100644 --- a/_data.tf +++ b/_data.tf @@ -1 +1,2 @@ data "aws_caller_identity" "current" {} +data "aws_region" "current" {} diff --git a/_variables.tf b/_variables.tf index 4302ba3..d881a80 100644 --- a/_variables.tf +++ b/_variables.tf @@ -27,6 +27,24 @@ variable "sns_topic_name" { default = "CISAlarmV2" } +variable "chatbot_sns_topic" { + description = "The arn of the SNS Topic which will be notified when any alarm is performed." + type = string + default = "" +} + +# variable "email_sns_topic" { +# description = "The arn of the SNS Topic which will be notified when any alarm is performed via email." +# type = string +# default = "" +# } + +variable "emails" { + default = [] + type = list(string) +} + + variable "alarm_account_ids" { default = [] type = list(string) @@ -45,3 +63,9 @@ variable "tags" { "Terraform" = true } } + +variable "kms_key" { + default = "" + type = string + description = "kms used to encrypt SNS topic" +} diff --git a/_versions.tf b/_versions.tf index 93d219e..2a4c0d3 100644 --- a/_versions.tf +++ b/_versions.tf @@ -3,7 +3,8 @@ terraform { required_providers { aws = { - source = "hashicorp/aws" + source = "hashicorp/aws" + version = ">= 4.0.0" } random = { source = "hashicorp/random" diff --git a/alarms.tf b/alarms.tf index ac422ad..8b0564f 100644 --- a/alarms.tf +++ b/alarms.tf @@ -1,79 +1,3 @@ -# -------------------------------------------------------------------------------------------------- -# The SNS topic to which CloudWatch alarms send events. -# -------------------------------------------------------------------------------------------------- -resource "aws_sns_topic" "alarms" { - count = var.enabled ? 1 : 0 - name = var.sns_topic_name - kms_master_key_id = aws_kms_key.sns[0].id # default key does not allow cloudwatch alarms to publish - tags = var.tags -} - -data "aws_iam_policy_document" "kms_policy_sns" { - count = var.enabled ? 1 : 0 - statement { - sid = "Enable IAM User Permissions" - effect = "Allow" - principals { - type = "AWS" - identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] - } - actions = ["kms:*"] - resources = ["*"] - } - statement { - actions = ["kms:Decrypt", "kms:GenerateDataKey*"] - principals { - type = "Service" - identifiers = ["cloudwatch.amazonaws.com"] - } - resources = ["*"] - sid = "allow-cloudwatch-kms" - } -} - -resource "aws_kms_key" "sns" { - count = var.enabled ? 1 : 0 - deletion_window_in_days = 7 - description = "SNS CMK Encryption Key" - enable_key_rotation = true - policy = data.aws_iam_policy_document.kms_policy_sns[0].json -} - -resource "aws_sns_topic_policy" "alarms" { - count = var.enabled ? 1 : 0 - arn = aws_sns_topic.alarms[0].arn - policy = data.aws_iam_policy_document.alarms_policy[0].json -} - -data "aws_iam_policy_document" "alarms_policy" { - count = var.enabled ? 1 : 0 - policy_id = "allow-org-accounts" - - statement { - actions = [ - "SNS:GetTopicAttributes", - "SNS:SetTopicAttributes", - "SNS:AddPermission", - "SNS:RemovePermission", - "SNS:DeleteTopic", - "SNS:Subscribe", - "SNS:ListSubscriptionsByTopic", - "SNS:Publish", - "SNS:Receive" - ] - condition { - test = "StringEquals" - variable = "AWS:SourceOwner" - values = var.alarm_account_ids - } - principals { - type = "AWS" - identifiers = ["*"] - } - resources = [aws_sns_topic.alarms[0].arn] - sid = "allow-org-accounts" - } -} resource "random_string" "cloudtrail_alarm_suffix" { count = var.enabled ? 1 : 0 @@ -81,6 +5,7 @@ resource "random_string" "cloudtrail_alarm_suffix" { special = false lower = true upper = false + numeric = false } resource "aws_cloudformation_stack" "cloudtrail_alarm" { @@ -90,6 +15,6 @@ resource "aws_cloudformation_stack" "cloudtrail_alarm" { parameters = { CloudTrailLogGroupName = var.cloudtrail_log_group_name - AlarmNotificationTopic = aws_sns_topic.alarms[0].id + AlarmNotificationTopic = var.chatbot_sns_topic # aws_sns_topic.alarms[0].id } } diff --git a/cloudtrail-alarms-full.cf.json b/cloudtrail-alarms-full.cf.json index 085a047..de3bee2 100644 --- a/cloudtrail-alarms-full.cf.json +++ b/cloudtrail-alarms-full.cf.json @@ -31,6 +31,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "CloudTrailSecurityGroupChangesV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Alarms when an API call is made to create, update or delete a Security Group.", "MetricName" : "SecurityGroupEventCount", "Namespace" : "CloudTrailMetrics", @@ -60,6 +61,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "CloudTrailNetworkAclChangesV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Alarms when an API call is made to create, update or delete a Network ACL.", "MetricName" : "NetworkAclEventCount", "Namespace" : "CloudTrailMetrics", @@ -89,6 +91,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "CloudTrailGatewayChangesV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Alarms when an API call is made to create, update or delete a Customer or Internet Gateway.", "MetricName" : "GatewayEventCount", "Namespace" : "CloudTrailMetrics", @@ -118,6 +121,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "CloudTrailVpcChangesV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Alarms when an API call is made to create, update or delete a VPC, VPC peering connection or VPC connection to classic.", "MetricName" : "VpcEventCount", "Namespace" : "CloudTrailMetrics", @@ -147,6 +151,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "CloudTrailEC2InstanceChangesV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Alarms when an API call is made to create, terminate, start, stop or reboot an EC2 instance.", "MetricName" : "EC2InstanceEventCount", "Namespace" : "CloudTrailMetrics", @@ -176,6 +181,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "CloudTrailEC2LargeInstanceChangesV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Alarms when an API call is made to create, terminate, start, stop or reboot a 4x or 8x-large EC2 instance.", "MetricName" : "EC2LargeInstanceEventCount", "Namespace" : "CloudTrailMetrics", @@ -205,6 +211,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "CloudTrailChangesV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Alarms when an API call is made to create, update or delete a CloudTrail trail, or to start or stop logging to a trail.", "MetricName" : "CloudTrailEventCount", "Namespace" : "CloudTrailMetrics", @@ -235,6 +242,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "CloudTrailConsoleSignInFailuresV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Alarms when an unauthenticated API call is made to sign into the console.", "MetricName" : "ConsoleSignInFailureCount", "Namespace" : "CloudTrailMetrics", @@ -265,6 +273,7 @@ "Properties": { "AlarmName" : "CloudTrailAuthorizationFailuresV2", "AlarmDescription" : "Alarms when an unauthorized API call is made.", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "MetricName" : "AuthorizationFailureCount", "Namespace" : "CloudTrailMetrics", "ComparisonOperator" : "GreaterThanOrEqualToThreshold", @@ -294,6 +303,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "CloudTrailIAMPolicyChangesV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Alarms when an API call is made to change an IAM policy.", "MetricName" : "IAMPolicyEventCount", "Namespace" : "CloudTrailMetrics", @@ -323,6 +333,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "CloudTrailRouteTableChangesV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Alarms when an API call is made to create, update or delete a Route Table.", "MetricName" : "RouteTableEventCount", "Namespace" : "CloudTrailMetrics", @@ -352,6 +363,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "CMKChangesV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Alarms each time when a CMK configuration change is made.", "MetricName" : "CMKEventCount", "Namespace" : "CloudTrailMetrics", @@ -381,6 +393,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "RootAccountUsageAlarmV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Alarms each time when Root Account is used.", "MetricName" : "RootAccountUsageEventCount", "Namespace" : "CloudTrailMetrics", @@ -412,6 +425,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "AWSConfigChangesAlarmV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Triggered by AWS Config changes.", "MetricName" : "ConfigEventCount", "Namespace" : "CloudTrailMetrics", @@ -444,6 +458,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "S3BucketConfigChangesAlarmV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Triggered by AWS S3 Bucket config changes.", "MetricName" : "S3BucketEventCount", "Namespace" : "CloudTrailMetrics", @@ -477,6 +492,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "ConsoleSignInWithoutMfaAlarmV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Triggered by sign-in requests made without MFA.", "MetricName" : "ConsoleSignInWithoutMfaCount", "Namespace" : "CloudTrailMetrics", @@ -510,6 +526,7 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "AlarmName" : "OrganizationsChangesAlarmV2", + "AlarmActions": [{"Ref" : "AlarmNotificationTopic"}], "AlarmDescription" : "Triggered by AWS Organizations events.", "MetricName" : "OrganizationsEvents", "Namespace" : "CloudTrailMetrics", diff --git a/iam.tf b/iam.tf index 9183358..655ef3d 100644 --- a/iam.tf +++ b/iam.tf @@ -9,13 +9,13 @@ data "aws_iam_policy_document" "lambda_assume_role" { } resource "aws_iam_role" "iam_for_lambda" { - name = "iam_for_lambda" + name = "cloudtrail-cn-role-${data.aws_region.current.name}" assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json tags = var.tags } resource "aws_iam_policy" "lambda_cw" { - name = "lambda_cw" + name = "cloudtrail-cn-policy-${data.aws_region.current.name}" path = "/" description = "IAM policy for logging from a lambda" policy = jsonencode({ diff --git a/main.tf b/main.tf index bd5ba90..5e8162b 100644 --- a/main.tf +++ b/main.tf @@ -5,7 +5,7 @@ resource "aws_lambda_function" "lambda" { handler = "index.handler" timeout = var.lambda_timeout source_code_hash = filebase64sha256("${path.module}/lambda.zip") - runtime = "nodejs20.x" + runtime = "nodejs18.x" tags = var.tags tracing_config { mode = "Active" diff --git a/sns.tf b/sns.tf new file mode 100644 index 0000000..6f4fa97 --- /dev/null +++ b/sns.tf @@ -0,0 +1,55 @@ +# -------------------------------------------------------------------------------------------------- +# The SNS topic to which CloudWatch alarms send events. +# -------------------------------------------------------------------------------------------------- +resource "aws_sns_topic" "alarms" { + count = var.enabled ? 1 : 0 + name = var.sns_topic_name + kms_master_key_id = var.kms_key #aws_kms_key.sns[0].id # default key does not allow cloudwatch alarms to publish + tags = var.tags +} + + +resource "aws_sns_topic_policy" "alarms" { + count = var.enabled ? 1 : 0 + arn = aws_sns_topic.alarms[0].arn + policy = data.aws_iam_policy_document.alarms_policy[0].json +} + +data "aws_iam_policy_document" "alarms_policy" { + count = var.enabled ? 1 : 0 + policy_id = "allow-org-accounts" + + statement { + actions = [ + "SNS:GetTopicAttributes", + "SNS:SetTopicAttributes", + "SNS:AddPermission", + "SNS:RemovePermission", + "SNS:DeleteTopic", + "SNS:Subscribe", + "SNS:ListSubscriptionsByTopic", + "SNS:Publish", + "SNS:Receive" + ] + condition { + test = "StringEquals" + variable = "AWS:SourceOwner" + values = var.alarm_account_ids + } + principals { + type = "AWS" + identifiers = ["*"] + } + resources = [aws_sns_topic.alarms[0].arn] + sid = "allow-org-accounts" + } +} + + +resource "aws_sns_topic_subscription" "cloudtrail_cutom_alarm_email" { + #for_each = {for email in var.emails : var.emails => email} + for_each = toset(var.emails) + topic_arn = aws_sns_topic.alarms[0].arn + protocol = "email" + endpoint = each.value +} \ No newline at end of file