# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 AWSTemplateFormatVersion: 2010-09-09 Description: Creates an SNS topic and Lambda function to enable SecurityHub in the Security account. Parameters: SecurityAccountId: Type: String Description: Which account will be the SecurityHub Admin account? Enter the AWS account ID. (This is generally the AWS Control Tower Audit account) AllowedPattern: '^[0-9]{12}$' ConstraintDescription: The Security Account ID must be a 12 character string. MinLength: 12 MaxLength: 12 OrganizationId: Type: String Description: AWS Organizations ID for the Control Tower. This is used to restrict permissions to least privilege. AllowedPattern: '^[o][\-][a-z0-9]{10}$' ConstraintDescription: The Org Id must be a 12 character string starting with o- and followed by 10 lower case alphanumeric characters MinLength: 12 MaxLength: 12 RegionFilter: Type: String Description: Should Security Hub be enabled for all Security Hub supported regions, or only Control Tower supported regions Default: ControlTower AllowedValues: - SecurityHub - ControlTower OUFilter: Type: String Description: Should Security Hub be enabled for all accounts, or only accounts Control Tower Managed OUs? Default: All AllowedValues: - All - ControlTower S3SourceBucket: Type: String Description: S3 bucket containing SecurityHubEnabler Lambda deployment package Default: "" S3SourceKey: Type: String Description: S3 object key for SecurityHubEnabler Lambda deployment package Default: securityhub_enabler.zip ComplianceFrequency: Type: Number Description: Frequency (in days between 1 and 30, default is 7) to check organizational compliance Default: 7 ConstraintDescription: Compliance Frequency must be a number between 1 and 30, inclusive. MinValue: 1 MaxValue: 30 RoleToAssume: Type: String Description: IAM role to be assumed in child accounts to enable SecurityHub. The default is AWSControlTowerExecution for a Control Tower environment. Default: 'AWSControlTowerExecution' AWSStandard: Type: String Description: Should Security Hub enable the AWS Foundational Security Best Practices v1.0.0 Security Standard? Default: "Yes" AllowedValues: - "Yes" - "No" CIS120Standard: Type: String Description: Should Security Hub enable the CIS AWS Foundations Benchmark v1.2.0 Security Standard? Default: "Yes" AllowedValues: - "Yes" - "No" CIS140Standard: Type: String Description: Should Security Hub enable the CIS AWS Foundations Benchmark v1.4.0 Security Standard? Default: "No" AllowedValues: - "Yes" - "No" PCIStandard: Type: String Description: Should Security Hub enable the PCI DSS v3.2.1 Security Standard? Default: "No" AllowedValues: - "Yes" - "No" NISTStandard: Type: String Description: Should Security Hub enable the NIST SP 800-53 Rev5 Security Standard? Default: "No" AllowedValues: - "Yes" - "No" Conditions: ComplianceFrequencySingleDay: !Equals - !Ref 'ComplianceFrequency' - 1 Resources: SecurityHubEnablerRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - "lambda.amazonaws.com" Action: - "sts:AssumeRole" Path: "/" Policies: - PolicyName: SecurityHubEnablerPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - organizations:ListAccounts - organizations:DescribeAccount - organizations:ListPoliciesForTarget - organizations:ListParents Resource: '*' Condition: StringEquals: "aws:PrincipalOrgId": !Ref OrganizationId - Effect: Allow Action: - sts:AssumeRole Resource: !Sub 'arn:aws:iam::*:role/${RoleToAssume}' Condition: StringEquals: "aws:PrincipalOrgId": !Ref OrganizationId - Effect: Allow Action: - sns:Publish Resource: !Ref SecurityHubEnablerTopic - Effect: Allow Action: - 'logs:CreateLogGroup' - 'logs:CreateLogStream' - 'logs:PutLogEvents' Resource: - !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*' - Effect: Allow Action: - 'sts:AssumeRole' Resource: !Sub 'arn:aws:iam::*:role/${RoleToAssume}' - Effect: Allow Action: - 'CloudFormation:ListStackInstances' Resource: !Sub 'arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stackset/AWSControlTowerBP-BASELINE-CLOUDWATCH:*' - Effect: Allow Action: - 'iam:CreateServiceLinkedRole' - 'ec2:describeRegions' - 'securityhub:AcceptInvitation' - 'securityhub:AcceptAdministratorInvitation' - 'securityhub:BatchEnableStandards' - 'securityhub:BatchDisableStandards' - 'securityhub:CreateMembers' - 'securityhub:DisassociateFromAdministratorAccount' - 'securityhub:DisassociateMembers' - 'securityhub:DisableSecurityHub' - 'securityhub:DeleteMembers' - 'securityhub:EnableSecurityHub' - 'securityhub:GetEnabledStandards' - 'securityhub:GetFindings' - 'securityhub:GetMasterAccount' - 'securityhub:InviteMembers' - 'securityhub:ListInvitations' - 'securityhub:ListMembers' Resource: '*' Metadata: cfn_nag: rules_to_suppress: - id: W11 reason: "Organizations doesn't have arns, so we have to use an asterisk in the policy" SecurityHubEnablerLambda: Type: "AWS::Lambda::Function" DependsOn: - SecurityHubEnablerRole Properties: Handler: "securityhub_enabler.lambda_handler" Role: !Sub "arn:aws:iam::${AWS::AccountId}:role/${SecurityHubEnablerRole}" Code: S3Bucket: !Ref S3SourceBucket S3Key: !Ref S3SourceKey Runtime: "python3.10" MemorySize: 256 Timeout: 900 # ################################################################### ReservedConcurrentExecutions: 2 # ################################################################### Environment: Variables: ou_filter: !Ref OUFilter region_filter: !Ref RegionFilter assume_role: !Sub ${RoleToAssume} ct_admin_account: !Sub ${AWS::AccountId} sh_admin_account: !Sub ${SecurityAccountId} topic: !Ref SecurityHubEnablerTopic aws_standard: !Ref AWSStandard cis_standard: !Ref CIS120Standard cis_140_standard: !Ref CIS140Standard nist_standard: !Ref NISTStandard pci_standard: !Ref PCIStandard log_level: "ERROR" SecurityHubEnablerTopic: Type: AWS::SNS::Topic Properties: DisplayName: SecurityHub_Enabler TopicName: SecurityHubEnablerTopic Metadata: cfn_nag: rules_to_suppress: - id: W47 reason: "Not sensitive data, doesn't need encryption with kms" SecurityHubEnablerTopicLambdaPermission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt SecurityHubEnablerLambda.Arn Principal: sns.amazonaws.com SourceArn: !Ref SecurityHubEnablerTopic SecurityHubEnablerSubscription: Type: AWS::SNS::Subscription Properties: Endpoint: !GetAtt SecurityHubEnablerLambda.Arn Protocol: lambda TopicArn: !Ref SecurityHubEnablerTopic ScheduledRule: Type: AWS::Events::Rule Properties: Description: "SecurityHubScheduledComplianceTrigger" ScheduleExpression: !If - ComplianceFrequencySingleDay - !Sub "rate(${ComplianceFrequency} day)" - !Sub "rate(${ComplianceFrequency} days)" State: "ENABLED" Targets: - Arn: !GetAtt SecurityHubEnablerLambda.Arn Id: "DailyInvite" LifeCycleRule: Type: AWS::Events::Rule Properties: Description: "SecurityHubLifeCycleTrigger" EventPattern: source: - "aws.controltower" detail-type: - "AWS Service Event via CloudTrail" detail: eventName: - "CreateManagedAccount" State: "ENABLED" Targets: - Arn: !GetAtt SecurityHubEnablerLambda.Arn Id: "DailyInvite" PermissionForSchedEventToInvokeLambda: Type: AWS::Lambda::Permission Properties: FunctionName: !GetAtt SecurityHubEnablerLambda.Arn Action: "lambda:InvokeFunction" Principal: "events.amazonaws.com" SourceArn: !GetAtt ScheduledRule.Arn PermissionForCTEventToInvokeLambda: Type: AWS::Lambda::Permission Properties: FunctionName: !GetAtt SecurityHubEnablerLambda.Arn Action: "lambda:InvokeFunction" Principal: "events.amazonaws.com" SourceArn: !GetAtt LifeCycleRule.Arn FirstRun: Type: Custom::SecurityHubEnablerLambdaFirstRun DependsOn: - SecurityHubEnablerTopic - SecurityHubEnablerRole - SecurityHubEnablerTopicLambdaPermission - SecurityHubEnablerSubscription Properties: ServiceToken: !GetAtt SecurityHubEnablerLambda.Arn