diff --git a/_data.tf b/_data.tf new file mode 100644 index 0000000..7a94467 --- /dev/null +++ b/_data.tf @@ -0,0 +1,3 @@ +data "aws_cloudwatch_log_group" "cloudtrail" { + name = var.cloudtrail_log_group_name +} diff --git a/_outputs.tf b/_outputs.tf index 42078dd..6f5b8a3 100644 --- a/_outputs.tf +++ b/_outputs.tf @@ -2,3 +2,8 @@ output "lambda_arn" { description = "The ARN from lambda custom message" value = aws_lambda_function.lambda.arn } + +output "alarm_sns_topic" { + description = "The SNS topic to which CloudWatch Alarms will be sent." + value = aws_sns_topic.alarms +} diff --git a/_variables.tf b/_variables.tf index 463b5c2..8c10b24 100644 --- a/_variables.tf +++ b/_variables.tf @@ -12,17 +12,23 @@ variable "cloudtrail_log_group_name" { description = "The name of the loggroup that will get information from" } -variable "aws_sns_topic_arn" { - description = "The ARN of SNS Topic where the notification will be sent" -} - variable "lambda_timeout" { description = "Set lambda Timeout" default = 3 } -variable "cloudwatch_log_cloudtrail_arn" { - description = "Cloudwatch Loggroup ARN" +variable "sns_topic_name" { + description = "The name of the SNS Topic which will be notified when any alarm is performed." + default = "CISAlarmV2" +} + +variable "alarm_account_ids" { + default = [] +} + +variable "alarm_mode" { + default = "light" + description = "Version of alarms to use. 'light' or 'full' available" } variable "tags" { @@ -31,3 +37,4 @@ variable "tags" { "Terraform" = true } } + diff --git a/alarms.tf b/alarms.tf new file mode 100644 index 0000000..3adda66 --- /dev/null +++ b/alarms.tf @@ -0,0 +1,100 @@ +# -------------------------------------------------------------------------------------------------- +# The SNS topic to which CloudWatch alarms send events. +# -------------------------------------------------------------------------------------------------- +data "aws_caller_identity" "current" {} + +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 + length = 8 + special = false + lower = true + upper = false + number = false +} + +resource "aws_cloudformation_stack" "cloudtrail_alarm" { + count = var.enabled ? 1 : 0 + name = "cloudtrail-alarm-${random_string.cloudtrail_alarm_suffix[0].result}" + template_body = var.alarm_mode == "full" ? file("${path.module}/cloudtrail-alarms-full.cf.json") : file("${path.module}/cloudtrail-alarms-light.cf.yml") + + parameters = { + CloudTrailLogGroupName = data.aws_cloudwatch_log_group.cloudtrail.name + AlarmNotificationTopic = aws_sns_topic.alarms[0].id + } +} diff --git a/cloudtrail-alarms-full.cf.json b/cloudtrail-alarms-full.cf.json new file mode 100644 index 0000000..085a047 --- /dev/null +++ b/cloudtrail-alarms-full.cf.json @@ -0,0 +1,524 @@ +{ + "AWSTemplateFormatVersion" : "2010-09-09", + "Description" : "AWS CloudTrail API Activity Alarm Template for CloudWatch Logs", + "Parameters" : { + "CloudTrailLogGroupName" : { + "Type" : "String", + "Default" : "CloudTrail/DefaultLogGroup", + "Description" : "Enter CloudWatch Logs log group name. Default is CloudTrail/DefaultLogGroup" + }, + "AlarmNotificationTopic" : { + "Type": "String", + "Description": "SNS Notification Topic to send alarms to" + } + }, + "Resources" : { + "SecurityGroupChangesMetricFilter": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ ($.eventName = AuthorizeSecurityGroupIngress) || ($.eventName = AuthorizeSecurityGroupEgress) || ($.eventName = RevokeSecurityGroupIngress) || ($.eventName = RevokeSecurityGroupEgress) || ($.eventName = CreateSecurityGroup) || ($.eventName = DeleteSecurityGroup) }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "SecurityGroupEventCount", + "MetricValue": "1" + } + ] + } + }, + "SecurityGroupChangesAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "CloudTrailSecurityGroupChangesV2", + "AlarmDescription" : "Alarms when an API call is made to create, update or delete a Security Group.", + "MetricName" : "SecurityGroupEventCount", + "Namespace" : "CloudTrailMetrics", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + } + }, + + "NetworkAclChangesMetricFilter": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ ($.eventName = CreateNetworkAcl) || ($.eventName = CreateNetworkAclEntry) || ($.eventName = DeleteNetworkAcl) || ($.eventName = DeleteNetworkAclEntry) || ($.eventName = ReplaceNetworkAclEntry) || ($.eventName = ReplaceNetworkAclAssociation) }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "NetworkAclEventCount", + "MetricValue": "1" + } + ] + } + }, + "NetworkAclChangesAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "CloudTrailNetworkAclChangesV2", + "AlarmDescription" : "Alarms when an API call is made to create, update or delete a Network ACL.", + "MetricName" : "NetworkAclEventCount", + "Namespace" : "CloudTrailMetrics", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + } + }, + + "GatewayChangesMetricFilter": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ ($.eventName = CreateCustomerGateway) || ($.eventName = DeleteCustomerGateway) || ($.eventName = AttachInternetGateway) || ($.eventName = CreateInternetGateway) || ($.eventName = DeleteInternetGateway) || ($.eventName = DetachInternetGateway) }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "GatewayEventCount", + "MetricValue": "1" + } + ] + } + }, + "GatewayChangesAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "CloudTrailGatewayChangesV2", + "AlarmDescription" : "Alarms when an API call is made to create, update or delete a Customer or Internet Gateway.", + "MetricName" : "GatewayEventCount", + "Namespace" : "CloudTrailMetrics", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + } + }, + + "VpcChangesMetricFilter": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ ($.eventName = CreateVpc) || ($.eventName = DeleteVpc) || ($.eventName = ModifyVpcAttribute) || ($.eventName = AcceptVpcPeeringConnection) || ($.eventName = CreateVpcPeeringConnection) || ($.eventName = DeleteVpcPeeringConnection) || ($.eventName = RejectVpcPeeringConnection) || ($.eventName = AttachClassicLinkVpc) || ($.eventName = DetachClassicLinkVpc) || ($.eventName = DisableVpcClassicLink) || ($.eventName = EnableVpcClassicLink) }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "VpcEventCount", + "MetricValue": "1" + } + ] + } + }, + "VpcChangesAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "CloudTrailVpcChangesV2", + "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", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + } + }, + + "EC2InstanceChangesMetricFilter": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ ($.eventName = RunInstances) || ($.eventName = RebootInstances) || ($.eventName = StartInstances) || ($.eventName = StopInstances) || ($.eventName = TerminateInstances) }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "EC2InstanceEventCount", + "MetricValue": "1" + } + ] + } + }, + "EC2InstanceChangesAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "CloudTrailEC2InstanceChangesV2", + "AlarmDescription" : "Alarms when an API call is made to create, terminate, start, stop or reboot an EC2 instance.", + "MetricName" : "EC2InstanceEventCount", + "Namespace" : "CloudTrailMetrics", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + } + }, + + "EC2LargeInstanceChangesMetricFilter": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ ($.eventName = RunInstances) && (($.requestParameters.instanceType = *.8xlarge) || ($.requestParameters.instanceType = *.4xlarge)) }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "EC2LargeInstanceEventCount", + "MetricValue": "1" + } + ] + } + }, + "EC2LargeInstanceChangesAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "CloudTrailEC2LargeInstanceChangesV2", + "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", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + } + }, + + "CloudTrailChangesMetricFilter": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ ($.eventName = CreateTrail) || ($.eventName = UpdateTrail) || ($.eventName = DeleteTrail) || ($.eventName = StartLogging) || ($.eventName = StopLogging) }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "CloudTrailEventCount", + "MetricValue": "1" + } + ] + } + }, + "CloudTrailChangesAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "CloudTrailChangesV2", + "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", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + } + }, + + + "ConsoleSignInFailuresMetricFilter": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ ($.eventName = ConsoleLogin) && ($.errorMessage = \"Failed authentication\") }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "ConsoleSignInFailureCount", + "MetricValue": "1" + } + ] + } + }, + "ConsoleSignInFailuresAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "CloudTrailConsoleSignInFailuresV2", + "AlarmDescription" : "Alarms when an unauthenticated API call is made to sign into the console.", + "MetricName" : "ConsoleSignInFailureCount", + "Namespace" : "CloudTrailMetrics", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "3" + } + }, + + "AuthorizationFailuresMetricFilter": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ ($.errorCode = \"*UnauthorizedOperation\") || ($.errorCode = \"AccessDenied*\") }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "AuthorizationFailureCount", + "MetricValue": "1" + } + ] + } + }, + "AuthorizationFailuresAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "CloudTrailAuthorizationFailuresV2", + "AlarmDescription" : "Alarms when an unauthorized API call is made.", + "MetricName" : "AuthorizationFailureCount", + "Namespace" : "CloudTrailMetrics", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + + } + }, + + "IAMPolicyChangesMetricFilter": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{($.eventName=DeleteGroupPolicy)||($.eventName=DeleteRolePolicy)||($.eventName=DeleteUserPolicy)||($.eventName=PutGroupPolicy)||($.eventName=PutRolePolicy)||($.eventName=PutUserPolicy)||($.eventName=CreatePolicy)||($.eventName=DeletePolicy)||($.eventName=CreatePolicyVersion)||($.eventName=DeletePolicyVersion)||($.eventName=AttachRolePolicy)||($.eventName=DetachRolePolicy)||($.eventName=AttachUserPolicy)||($.eventName=DetachUserPolicy)||($.eventName=AttachGroupPolicy)||($.eventName=DetachGroupPolicy)}", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "IAMPolicyEventCount", + "MetricValue": "1" + } + ] + } + }, + "IAMPolicyChangesAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "CloudTrailIAMPolicyChangesV2", + "AlarmDescription" : "Alarms when an API call is made to change an IAM policy.", + "MetricName" : "IAMPolicyEventCount", + "Namespace" : "CloudTrailMetrics", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + } + }, + + "RouteTableConfigChanges": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ ($.eventName = CreateRoute) || ($.eventName = CreateRouteTable) || ($.eventName = ReplaceRoute) || ($.eventName = ReplaceRouteTableAssociation) || ($.eventName = DeleteRouteTable) || ($.eventName = DeleteRoute) || ($.eventName = DisassociateRouteTable) }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "RouteTableEventCount", + "MetricValue": "1" + } + ] + } + }, + "RouteTableConfigChangesAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "CloudTrailRouteTableChangesV2", + "AlarmDescription" : "Alarms when an API call is made to create, update or delete a Route Table.", + "MetricName" : "RouteTableEventCount", + "Namespace" : "CloudTrailMetrics", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + } + }, + + "CMKChanges": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ ($.eventSource = kms.amazonaws.com) && (($.eventName = DisableKey) || ($.eventName = ScheduleKeyDeletion)) }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "CMKEventCount", + "MetricValue": "1" + } + ] + } + }, + "CMKChangesAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "CMKChangesV2", + "AlarmDescription" : "Alarms each time when a CMK configuration change is made.", + "MetricName" : "CMKEventCount", + "Namespace" : "CloudTrailMetrics", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + } + }, + + "RootAccountUsage": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ $.userIdentity.type = Root && $.userIdentity.invokedBy NOT EXISTS && $.eventType != AwsServiceEvent }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "RootAccountUsageEventCount", + "MetricValue": "1" + } + ] + } + }, + "RootAccountUsageAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "RootAccountUsageAlarmV2", + "AlarmDescription" : "Alarms each time when Root Account is used.", + "MetricName" : "RootAccountUsageEventCount", + "Namespace" : "CloudTrailMetrics", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + } + }, + + + + "AWSConfigChanges": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ ($.eventSource = config.amazonaws.com) && (($.eventName = StopConfigurationRecorder)||($.eventName = DeleteDeliveryChannel)||($.eventName = PutDeliveryChannel)||($.eventName = PutConfigurationRecorder)) }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "ConfigEventCount", + "MetricValue": "1" + } + ] + } + }, + "AWSConfigChangesAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "AWSConfigChangesAlarmV2", + "AlarmDescription" : "Triggered by AWS Config changes.", + "MetricName" : "ConfigEventCount", + "Namespace" : "CloudTrailMetrics", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + } + }, + + + + + "S3BucketConfigChanges": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ ($.eventSource = s3.amazonaws.com) && (($.eventName = PutBucketAcl) || ($.eventName = PutBucketPolicy) || ($.eventName = PutBucketCors) || ($.eventName = PutBucketLifecycle) || ($.eventName = PutBucketReplication) || ($.eventName = DeleteBucketPolicy) || ($.eventName = DeleteBucketCors) || ($.eventName = DeleteBucketLifecycle) || ($.eventName = DeleteBucketReplication)) }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "S3BucketEventCount", + "MetricValue": "1" + } + ] + } + }, + "S3BucketConfigChangesAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "S3BucketConfigChangesAlarmV2", + "AlarmDescription" : "Triggered by AWS S3 Bucket config changes.", + "MetricName" : "S3BucketEventCount", + "Namespace" : "CloudTrailMetrics", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + } + }, + + + + + + "ConsoleSignInWithoutMfaCount": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ $.eventName = ConsoleLogin && $.additionalEventData.MFAUsed = No }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "ConsoleSignInWithoutMfaCount", + "MetricValue": "1" + } + ] + } + }, + "ConsoleSignInWithoutMfaAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "ConsoleSignInWithoutMfaAlarmV2", + "AlarmDescription" : "Triggered by sign-in requests made without MFA.", + "MetricName" : "ConsoleSignInWithoutMfaCount", + "Namespace" : "CloudTrailMetrics", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + } + }, + + + + + + "OrganizationsChanges": { + "Type": "AWS::Logs::MetricFilter", + "Properties": { + "LogGroupName": { "Ref" : "CloudTrailLogGroupName" }, + "FilterPattern": "{ ($.eventSource = organizations.amazonaws.com) && ($.eventName = AcceptHandshake) || ($.eventName = AttachPolicy) || ($.eventName = CancelHandshake) || ($.eventName = CreateAccount) || ($.eventName = CreateOrganization) || ($.eventName = CreateOrganizationalUnit) || ($.eventName = CreatePolicy) || ($.eventName = DeclineHandshake) || ($.eventName = DeleteOrganization) || ($.eventName = DeleteOrganizationalUnit) || ($.eventName = DeletePolicy) || ($.eventName = EnableAllFeatures) || ($.eventName = EnablePolicyType) || ($.eventName = InviteAccountToOrganization) || ($.eventName = LeaveOrganization) || ($.eventName = DetachPolicy) || ($.eventName = DisablePolicyType) || ($.eventName = MoveAccount) || ($.eventName = RemoveAccountFromOrganization) || ($.eventName = UpdateOrganizationalUnit) || ($.eventName = UpdatePolicy) }", + "MetricTransformations": [ + { + "MetricNamespace": "CloudTrailMetrics", + "MetricName": "OrganizationsEvents", + "MetricValue": "1" + } + ] + } + }, + "OrganizationsChangesAlarm": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "AlarmName" : "OrganizationsChangesAlarmV2", + "AlarmDescription" : "Triggered by AWS Organizations events.", + "MetricName" : "OrganizationsEvents", + "Namespace" : "CloudTrailMetrics", + "ComparisonOperator" : "GreaterThanOrEqualToThreshold", + "EvaluationPeriods" : "1", + "Period" : "300", + "Statistic" : "Sum", + "Threshold" : "1" + } + } + } + } \ No newline at end of file diff --git a/cloudtrail-alarms-light.cf.yml b/cloudtrail-alarms-light.cf.yml new file mode 100644 index 0000000..9ed8e93 --- /dev/null +++ b/cloudtrail-alarms-light.cf.yml @@ -0,0 +1,69 @@ +--- +AWSTemplateFormatVersion: 2010-09-09 +Description: "Cloudtrail Alarms (rev:2)" + +#==================================================================================================== +Parameters: +#==================================================================================================== + CloudTrailLogGroupName: + Type: String + Description: CloudTrail log group name when CloudTrail is already provisioned + AlarmNotificationTopic: + Type: String + Description: SNS Notification Topic to send alarms to + +#==================================================================================================== +Resources: +#==================================================================================================== + + RootAccountUsageMetricFilter: + Type: 'AWS::Logs::MetricFilter' + Properties: + LogGroupName: !Ref CloudTrailLogGroupName + FilterPattern: '{$.userIdentity.type="Root" && $.userIdentity.invokedBy NOT EXISTS && $.eventType !="AwsServiceEvent"}' + MetricTransformations: + - MetricNamespace: CloudTrailMetrics + MetricName: RootAccountUsageEventCount + MetricValue: '1' + + RootAccountUsageAlarm: + Type: 'AWS::CloudWatch::Alarm' + Properties: + AlarmName: RootAccountUsage + AlarmDescription: (CIS-3.3) Alarms when Root Account is used + AlarmActions: + - !Ref AlarmNotificationTopic + MetricName: RootAccountUsageEventCount + Namespace: CloudTrailMetrics + ComparisonOperator: GreaterThanOrEqualToThreshold + EvaluationPeriods: 1 + Period: 300 + Statistic: Sum + Threshold: 1 + TreatMissingData: notBreaching + + NoMFALoginUsageMetricFilter: + Type: 'AWS::Logs::MetricFilter' + Properties: + LogGroupName: !Ref CloudTrailLogGroupName + FilterPattern: '{ ($.eventName ="ConsoleLogin") && ($.additionalEventData.MFAUsed != "Yes") }' + MetricTransformations: + - MetricNamespace: CloudTrailMetrics + MetricName: NoMFALoginEventCount + MetricValue: '1' + + NoMFALoginUsageAlarm: + Type: 'AWS::CloudWatch::Alarm' + Properties: + AlarmName: NoMFALogin + AlarmDescription: Monitoring for single-factor console logins will increase visibility into accounts that are not protected by MFA. + AlarmActions: + - !Ref AlarmNotificationTopic + MetricName: NoMFALoginEventCount + Namespace: CloudTrailMetrics + ComparisonOperator: GreaterThanOrEqualToThreshold + EvaluationPeriods: 1 + Period: 300 + Statistic: Sum + Threshold: 1 + TreatMissingData: notBreaching diff --git a/iam.tf b/iam.tf index 44cb27a..f7a8352 100644 --- a/iam.tf +++ b/iam.tf @@ -1,5 +1,3 @@ -data "aws_caller_identity" "current" {} - data "aws_iam_policy_document" "lambda_assume_role" { statement { actions = ["sts:AssumeRole"] diff --git a/lambda.zip b/lambda.zip index db6c40c..03cd9a5 100644 Binary files a/lambda.zip and b/lambda.zip differ diff --git a/lambda/index.js b/lambda/index.js index 2383d6b..f8a9c76 100644 --- a/lambda/index.js +++ b/lambda/index.js @@ -15,7 +15,7 @@ var processEvent = function (event, context) { var alarmName = event.detail.alarmName; var time = event.time; var messageNotification = null - + console.log(event) getWatchlogData(alarmName, time, function (err, data, alarm) { clouldTrail = JSON.parse(data.events[0].message); messageNotification = handleCloudWatch(clouldTrail, alarm, data.events) diff --git a/main.tf b/main.tf index 3ec767a..d2e40c4 100644 --- a/main.tf +++ b/main.tf @@ -9,8 +9,8 @@ resource "aws_lambda_function" "lambda" { tags = var.tags environment { variables = { - LOG_GROUP = var.cloudtrail_log_group_name, - TOPIC_ARN=var.aws_sns_topic_arn, + LOG_GROUP = data.aws_cloudwatch_log_group.cloudtrail.name, + TOPIC_ARN= aws_sns_topic.alarms[0].arn, OFFSET=180 } }