# # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # AWSTemplateFormatVersion: 2010-09-09 Description: Cloud Formation template for incident response actions Parameters: S3BucketSources: Type: String Description: S3 bucket with sources MaxLength: 63 MinLength: 3 Default: awsiammedia S3SourcesPrefix: Type: String Description: S3 prefix with sources WITH ending slash if not empty. example myprefix/ Default: public/sample/AutomatedIncidentResponse319/ TargetAccountSecurityRoleName: Type: String Description: Role to be assumed in the target account for incident response(IR) Default: Security-IR-Role SecurityTagKey: Type: String Description: Tag Key marker for approved security exception Default: SecurityException AllowedNetworkRangeIPv4: Type: String Description: Allowed CIDRv4 for Security Group to confine 0.0.0.0/0 Default: 172.31.0.0/16 AllowedNetworkRangeIPv6: Type: String Description: Allowed CIDRv6 for Security Group to confine ::/0 Default: fe80::/10 IsolateEc2Findings: Description: Define Guard Duty findings that lead to EC2 isolation. Comma delimited Type: CommaDelimitedList Default: Trojan:EC2/DNSDataExfiltration, Backdoor:EC2/Spambot, Backdoor:EC2/C&CActivity.B!DNS, Backdoor:EC2/DenialOfService.Tcp, Backdoor:EC2/DenialOfService.Udp, Backdoor:EC2/DenialOfService.Dns, Backdoor:EC2/DenialOfService.UdpOnTcpPorts, Backdoor:EC2/DenialOfService.UnusualProtocol, Trojan:EC2/BlackholeTraffic, Trojan:EC2/DropPoint, Trojan:EC2/BlackholeTraffic!DNS, Trojan:EC2/DriveBySourceTraffic!DNS, Trojan:EC2/DropPoint!DNS, Trojan:EC2/DGADomainRequest.B, Trojan:EC2/DGADomainRequest.C!DNS, Trojan:EC2/DNSDataExfiltration, Trojan:EC2/PhishingDomainRequest!DNS BlockPrincipalFindings: Description: Define Guard Duty findings that lead to blocking principle. Comma delimited Type: CommaDelimitedList Default: UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration,UnauthorizedAccess:IAMUser/TorIPCaller, UnauthorizedAccess:IAMUser/MaliciousIPCaller.Custom, UnauthorizedAccess:IAMUser/ConsoleLoginSuccess.B, UnauthorizedAccess:IAMUser/MaliciousIPCaller PrincipalOrgID: Type: String Description: Provide your Organization ID. It will be authorized to send Findings to Cloud Watch Event Bus. Put Star(*) to authorize everyone(dangerous) Default: o-1234567890 Metadata: 'AWS::CloudFormation::Interface': ParameterGroups: - Label: default: General Parameters: - S3BucketSources - S3SourcesPrefix - Label: default: Security Parameters: - TargetAccountSecurityRoleName - SecurityTagKey - PrincipalOrgID - Label: default: Incident Response Parameters: - AllowedNetworkRangeIPv4 - AllowedNetworkRangeIPv6 - IsolateEc2Findings - BlockPrincipalFindings ParameterLabels: S3BucketSources: default: S3 Bucket with sources S3SourcesPrefix: default: Prefix for S3 bucket with sources TargetAccountSecurityRoleName: default: Security IR Role Name SecurityTagKey: default: Security Exception Tag PrincipalOrgID: default: Organization Id AllowedNetworkRangeIPv4: default: Allowed Network Range IPv4 BlockPrincipalFindings: default: Block Principal Finding IsolateEc2Findings: default: Isolate EC2 Findings AllowedNetworkRangeIPv6: default: Allowed Network Range IPv6 Resources: # Copy the regional S3 lambda functions RegionalS3Objects: Type: "AWS::CloudFormation::Stack" Properties: TemplateURL: !Sub "https://s3.amazonaws.com/${S3BucketSources}/${S3SourcesPrefix}copy-s3obj-to-regional-s3bucket.yaml" Parameters: S3BucketSources: !Ref S3BucketSources S3SourcesPrefix: !Ref S3SourcesPrefix S3Objects: "master_lambda_functions.zip,copy-s3obj-to-regional-s3bucket.yaml,master-account-custom-actions-sec-hub.yaml" Tags: - Key: Name Value: !Sub '${AWS::StackName}-CopyRegionalS3Bucket-NestedStack' SecurityHubActionsStack: Type: "AWS::CloudFormation::Stack" Properties: TemplateURL: !Sub "https://s3.amazonaws.com/${RegionalS3Objects.Outputs.RegionalS3Bucket}/${S3SourcesPrefix}master-account-custom-actions-sec-hub.yaml" Parameters: LambdaIsolateEc2Arn: !GetAtt IsolateEC2Lambda.Arn LambdaBlockPrincipalArn: !GetAtt BlockPrincipalGroupLambda.Arn AlertSnsArn: !Ref Alerts S3BucketSources: !GetAtt RegionalS3Objects.Outputs.RegionalS3Bucket S3SourcesPrefix: !Ref S3SourcesPrefix Tags: - Key: Name Value: !Sub '${AWS::StackName}-SecurityHubActions-NestedStack' IsolateEC2Lambda: Type: 'AWS::Lambda::Function' Properties: FunctionName: !Sub "IsolateEc2-${AWS::StackName}" Handler: isolate_ec2_lambda_function.lambda_handler Runtime: python3.7 Code: S3Bucket: !GetAtt RegionalS3Objects.Outputs.RegionalS3Bucket S3Key: !Sub '${S3SourcesPrefix}master_lambda_functions.zip' Description: 'Isolates an EC2 with empty secyrity group' Timeout: 21 Role: !GetAtt IncidentResponseLambdaRole.Arn Environment: Variables: SecurityTagKey: !Ref SecurityTagKey TargetAccountSecurityRoleName: !Ref TargetAccountSecurityRoleName AlertsSns: !Ref Alerts StartSsmAutomatonLambda: Type: 'AWS::Lambda::Function' Properties: FunctionName: !Sub "StratSsmAutomation-${AWS::StackName}" Handler: start_ssm_automation_lambda_function.lambda_handler Runtime: python3.7 Code: S3Bucket: !GetAtt RegionalS3Objects.Outputs.RegionalS3Bucket S3Key: !Sub '${S3SourcesPrefix}master_lambda_functions.zip' Description: 'Execute SSM automation document' Timeout: 22 Role: !GetAtt IncidentResponseLambdaRole.Arn Environment: Variables: SecurityTagKey: !Ref SecurityTagKey TargetAccountSecurityRoleName: !Ref TargetAccountSecurityRoleName AlertsSns: !Ref Alerts ConfineSecurityGroupLambda: Type: 'AWS::Lambda::Function' Properties: FunctionName: !Sub "ConfineSecurityGroup-${AWS::StackName}" Handler: confine_secgr_lambda_function.lambda_handler Runtime: python3.7 Code: S3Bucket: !GetAtt RegionalS3Objects.Outputs.RegionalS3Bucket S3Key: !Sub '${S3SourcesPrefix}master_lambda_functions.zip' Description: 'Confines a Security Group if open to public' Timeout: 22 Role: !GetAtt IncidentResponseLambdaRole.Arn Environment: Variables: SecurityTagKey: !Ref SecurityTagKey AllowedNetworkRangeIPv4: !Ref AllowedNetworkRangeIPv4 AllowedNetworkRangeIPv6: !Ref AllowedNetworkRangeIPv6 TargetAccountSecurityRoleName: !Ref TargetAccountSecurityRoleName AlertsSns: !Ref Alerts BlockPrincipalGroupLambda: Type: 'AWS::Lambda::Function' Properties: FunctionName: !Sub "BlockPrincipal-${AWS::StackName}" Handler: deactivate_principal_lambda_function.lambda_handler Runtime: python3.7 Code: S3Bucket: !GetAtt RegionalS3Objects.Outputs.RegionalS3Bucket S3Key: !Sub '${S3SourcesPrefix}master_lambda_functions.zip' Description: 'Attaches deny all policy to a principal' Timeout: 22 Role: !GetAtt IncidentResponseLambdaRole.Arn Environment: Variables: SecurityTagKey: !Ref SecurityTagKey TargetAccountSecurityRoleName: !Ref TargetAccountSecurityRoleName BlockPolicyArn: arn:aws:iam::aws:policy/AWSDenyAll AlertsSns: !Ref Alerts IncidentResponseLambdaRole: Type: 'AWS::IAM::Role' Properties: RoleName: !Sub "IncidentResponse-${AWS::Region}" Path: / AssumeRolePolicyDocument: Version: "2012-10-17" Statement: Effect: Allow Principal: Service: "lambda.amazonaws.com" Action: "sts:AssumeRole" ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Path: / Policies: - PolicyName: lambda-sec-IR PolicyDocument: Version: '2012-10-17' Statement: - Action: - sts:AssumeRole Resource: - arn:aws:iam::*:role/AWS-SystemsManager-AutomationExecutionRole - !Sub "arn:aws:iam::*:role/${TargetAccountSecurityRoleName}" Effect: Allow - Action: - organizations:ListAccountsForParent Resource: - !Sub "arn:aws:organizations::${AWS::AccountId}:account/*" Effect: Allow - Effect: Allow Action: - ssm:StartAutomationExecution Resource: "*" - Effect: Allow Action: - sns:Publish Resource: !Ref Alerts eventGuardDutyIsolate: Type: "AWS::Events::Rule" Properties: Description: Event to isolate an instance if no exception defined Name: !Sub "IsolateEc2onGD-${AWS::StackName}" EventPattern: source: - aws.guardduty detail-type: - GuardDuty Finding detail: type: !Ref IsolateEc2Findings Targets: - Arn: !GetAtt IsolateEC2Lambda.Arn Id: 'IsolateEC2-lmb' - Arn: !Ref Alerts Id: "IsolateEC2-sns" PermissionForEventsIsolate: Type: "AWS::Lambda::Permission" Properties: FunctionName: !Sub "IsolateEc2-${AWS::StackName}" Action: "lambda:InvokeFunction" Principal: "events.amazonaws.com" SourceArn: !GetAtt eventGuardDutyIsolate.Arn eventGuardDutyBlockPrincipal: Type: "AWS::Events::Rule" Properties: Description: Event block principals Name: !Sub "DisablePrincipalOnGD-${AWS::StackName}" EventPattern: source: - aws.guardduty detail-type: - GuardDuty Finding detail: type: !Ref BlockPrincipalFindings Targets: - Arn: !GetAtt BlockPrincipalGroupLambda.Arn Id: 'BlockPrincipal-lmb' - Arn: !Ref Alerts Id: "BlockPrincipal-sns" PermissionForBlockPrincipal: Type: "AWS::Lambda::Permission" Properties: FunctionName: !Sub "BlockPrincipal-${AWS::StackName}" Action: "lambda:InvokeFunction" Principal: "events.amazonaws.com" SourceArn: !GetAtt eventGuardDutyBlockPrincipal.Arn eventEnableDefaultEncryption: Type: "AWS::Events::Rule" Properties: Description: Enable S3 default encryption Name: !Sub "S3EnableDefaultEncryption-${AWS::StackName}" EventPattern: source: - aws.config detail-type: - Config Rules Compliance Change detail: messageType: - ComplianceChangeNotification configRuleName: - S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED newEvaluationResult: complianceType: - NON_COMPLIANT Targets: - InputTransformer: InputPathsMap: resourceId: "$.detail.resourceId" account: "$.account" InputTemplate: '{ "account": <account>, "resourseType": "s3", "resourceId": <resourceId>, "AutomationDocumentName": "AWS-EnableS3BucketEncryption", "AutomationParameters": { "BucketName": [ <resourceId> ] } }' Arn: !GetAtt StartSsmAutomatonLambda.Arn Id: 'EnableS3Encryption-lmb' - Arn: !Ref Alerts Id: "S3EnableSSE-sns" PermissionEnableDefaultEncryption: Type: "AWS::Lambda::Permission" Properties: FunctionName: !Sub "StratSsmAutomation-${AWS::StackName}" Action: "lambda:InvokeFunction" Principal: "events.amazonaws.com" SourceArn: !GetAtt eventEnableDefaultEncryption.Arn eventS3DisablePublicReadWrite: Type: "AWS::Events::Rule" Properties: Description: Disable public read write for S3 bucket Name: !Sub "S3BlockPublicWirteRead-${AWS::StackName}" EventPattern: source: - aws.config detail-type: - Config Rules Compliance Change detail: messageType: - ComplianceChangeNotification configRuleName: - S3_BUCKET_PUBLIC_WRITE_PROHIBITED - S3_BUCKET_PUBLIC_READ_PROHIBITED newEvaluationResult: complianceType: - NON_COMPLIANT Targets: - InputTransformer: InputPathsMap: resourceId: "$.detail.resourceId" account: "$.account" InputTemplate: '{ "account": <account>, "resourseType": "s3", "resourceId": <resourceId>, "AutomationDocumentName": "AWS-DisableS3BucketPublicReadWrite", "AutomationParameters": { "S3BucketName": [ <resourceId> ] } }' Arn: !GetAtt StartSsmAutomatonLambda.Arn Id: 'DisablePublicReadWrite-lmb' - Arn: !Ref Alerts Id: "S3PubWriteRead-sns" PermissionDisablePublicReadWrite: Type: "AWS::Lambda::Permission" Properties: FunctionName: !Sub "StratSsmAutomation-${AWS::StackName}" Action: "lambda:InvokeFunction" Principal: "events.amazonaws.com" SourceArn: !GetAtt eventS3DisablePublicReadWrite.Arn eventConfineSecurityGroup: Type: "AWS::Events::Rule" Properties: Description: Confine Security Group if open Name: !Sub "ConfineSecGroup-${AWS::StackName}" EventPattern: source: - aws.config detail-type: - Config Rules Compliance Change detail: messageType: - ComplianceChangeNotification configRuleName: - SECURITY_GROUP_OPEN_PROHIBITED newEvaluationResult: complianceType: - NON_COMPLIANT Targets: - Arn: !GetAtt ConfineSecurityGroupLambda.Arn Id: 'SecurityGroupOpen-lmb' - Arn: !Ref Alerts Id: "SecurityGroupOpenAlert-sns" PermissionEventConfineSecurityGroup: Type: "AWS::Lambda::Permission" Properties: FunctionName: !Sub "ConfineSecurityGroup-${AWS::StackName}" Action: "lambda:InvokeFunction" Principal: "events.amazonaws.com" SourceArn: !GetAtt eventConfineSecurityGroup.Arn eventEncryptedVolumeRDS: Type: "AWS::Events::Rule" Properties: Description: Encryption enabled for RDS DB instances Name: !Sub "EncryptedVolumeRDS-${AWS::StackName}" EventPattern: source: - aws.config detail-type: - Config Rules Compliance Change detail: messageType: - ComplianceChangeNotification configRuleName: - ENCRYPTED_VOLUMES - RDS_STORAGE_ENCRYPTED newEvaluationResult: complianceType: - NON_COMPLIANT Targets: - Arn: !Ref Alerts Id: "S3PubWriteRead-sns" Alerts: Type: "AWS::SNS::Topic" Properties: DisplayName: "Security Automation" TopicName: !Sub "Security_Alerts_${AWS::StackName}" EventTopicPolicy: Type: 'AWS::SNS::TopicPolicy' Properties: PolicyDocument: Statement: - Sid: "default" Effect: Allow Principal: AWS: "*" Action: - sns:GetTopicAttributes - sns:SetTopicAttributes - sns:AddPermission - sns:RemovePermission - sns:DeleteTopic - sns:Subscribe - sns:ListSubscriptionsByTopic - sns:Publish - sns:Receive Condition: StringEquals: AWS:SourceOwner: !Ref AWS::AccountId Resource: - !Ref Alerts - Sid: "send" Effect: Allow Principal: Service: events.amazonaws.com Action: 'sns:Publish' Resource: - !Ref Alerts Topics: - !Ref Alerts EventBusPolicy: Type: AWS::Events::EventBusPolicy Properties: Action: "events:PutEvents" StatementId: "AllowAllOrg" Principal: "*" Condition: Type: "StringEquals" Key: "aws:PrincipalOrgID" Value: !Ref PrincipalOrgID BlockPrincipalErrorAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmDescription: "Alarm block principal functions" AlarmName: !Sub "Block principal function (${AWS::StackName})" Namespace: AWS/Lambda MetricName: Errors Dimensions: - Name: FunctionName Value: !Ref BlockPrincipalGroupLambda Statistic: Sum Period: 3600 EvaluationPeriods: 1 Threshold: 1 ComparisonOperator: GreaterThanOrEqualToThreshold TreatMissingData: notBreaching AlarmActions: - !Ref Alerts ConfineSecGrErrorAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmDescription: "Alarm confine security group functions" AlarmName: !Sub "Confine SecGr function (${AWS::StackName})" Namespace: AWS/Lambda MetricName: Errors Dimensions: - Name: FunctionName Value: !Ref ConfineSecurityGroupLambda Statistic: Sum Period: 3600 EvaluationPeriods: 1 Threshold: 1 ComparisonOperator: GreaterThanOrEqualToThreshold TreatMissingData: notBreaching AlarmActions: - !Ref Alerts StartSsmErrorAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmDescription: "Alarm start SSM automation functions" AlarmName: !Sub "Start SSM automation function (${AWS::StackName})" Namespace: AWS/Lambda MetricName: Errors Dimensions: - Name: FunctionName Value: !Ref StartSsmAutomatonLambda Statistic: Sum Period: 3600 EvaluationPeriods: 1 Threshold: 1 ComparisonOperator: GreaterThanOrEqualToThreshold TreatMissingData: notBreaching AlarmActions: - !Ref Alerts IsolateEC2ErrorAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmDescription: "Alarm EC2 isolate functions" AlarmName: !Sub "EC2 isolate function (${AWS::StackName})" Namespace: AWS/Lambda MetricName: Errors Dimensions: - Name: FunctionName Value: !Ref IsolateEC2Lambda Statistic: Sum Period: 3600 EvaluationPeriods: 1 Threshold: 1 ComparisonOperator: GreaterThanOrEqualToThreshold TreatMissingData: notBreaching AlarmActions: - !Ref Alerts