# EssInitSourceAccount.yaml - Initialize IAM ESS Lab Source Account # # This CloudFormation template is used for the External Security Services # lab. It sets up the "source account" that will send AWS CloudTrail # logs to the S3 bucket created by the "logging account." The security # services used by this lab are: # # Amazon Macie # Amazon GuardDuty # Amazon Inspector # AWS CloudTrail # # In particular it creates: # # (1) VPC and related components # # (2) An Amazon Linux EC2 instance with Apache. --- AWSTemplateFormatVersion: '2010-09-09' Description: Create ESS Workshop Stack # Mappings # # Constants: Use this mapping table for miscellaneous constant values # that should be centralized. # # NamePrefix - a prefix to apply to the Name tag of taggable resources # ProjectTag - a value to apply to the Project tag of taggable resource # # RegionMap: Define a mapping for the AWS Inspector rule packages. # Note that we only support us-west-2 and us-east-1 since the ESS portion # of the workshop will # only run in regions where Amazon Macie is supported # which is currently limited to us-east-1 and us-west-2. Mappings: Constants: Values: NamePrefix: 'esslab' ProjectTag: 'esslab' RegionMap: us-west-2: CVE: 'arn:aws:inspector:us-west-2:758058086616:rulespackage/0-9hgA516p' CIS: 'arn:aws:inspector:us-west-2:758058086616:rulespackage/0-H5hpSawc' SBP: 'arn:aws:inspector:us-west-2:758058086616:rulespackage/0-JJOtZiqQ' RBA: 'arn:aws:inspector:us-west-2:758058086616:rulespackage/0-vg5GGHSD' us-east-1: CVE: 'arn:aws:inspector:us-east-1:316112463485:rulespackage/0-gEjTy7T7' CIS: 'arn:aws:inspector:us-east-1:316112463485:rulespackage/0-rExsr2X8' SBP: 'arn:aws:inspector:us-east-1:316112463485:rulespackage/0-R01qwB5Q' RBA: 'arn:aws:inspector:us-east-1:316112463485:rulespackage/0-gBONHN9h' Resources: # LambdaExecutionRole - The AWS Lambda execution role for custom # CloudFormation resources. This function will be used for the # Lambda functions that are backing custom CloudFormation resources. # As such, it must include permissions for all the API calls that # the Lambda function will make. This typically includes the APIs # needed to write to Amazon CloudWatch Logs. In this case, since # we are using a custom resource to look up AMIs, it needs to also # support the DescribeImages API. LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" Policies: - PolicyName: root PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: arn:aws:logs:*:*:* - Effect: Allow Action: - ec2:DescribeImages Resource: "*" - Effect: Allow Action: - 'macie2:*' - 'guardduty:*' Resource: "*" # RandomStrFunction - Generate a string of random characters # # This AWS Lambda function is used to generate a random string # of letters. We'll use the Python string module to do this. # You can change the composition of the string by changing the # methods that are used. RandomStrFunction: Type: AWS::Lambda::Function Properties: Description: 'Generate a random string of characters' Handler: index.handler MemorySize: 128 Role: !GetAtt LambdaExecutionRole.Arn Runtime: 'python3.9' Timeout: 30 Tags: - Key: Project Value: !FindInMap [Constants, Values, 'ProjectTag'] Code: ZipFile: | import json import boto3 import cfnresponse import string import random def handler(event, context): if event['RequestType'] == 'Delete': responseData = {} cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData) return StringLength=int(event['ResourceProperties']['StringLength']) if StringLength <= 0: responseData = {} cfnresponse.send(event, context, cfnresponse.FAILED) else: responseData = {} chars=string.ascii_letters # change this to use other kinds of characters responseData['RandomString'] = ''.join(random.choice(chars) for _ in range(StringLength)) cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData) return # RandomCharString - generate a random string for the S3 bucket suffixes, passwords # # Parameters # # ServiceToken - a pointer to the AWS Lambda function # StringLength - the length of the random string to generate RandomCharString: Type: Custom::RandomCharString Properties: ServiceToken: !GetAtt RandomStrFunction.Arn StringLength: '12' # LoggingBucket LoggingBucket: Type: AWS::S3::Bucket DeletionPolicy: Retain Properties: LifecycleConfiguration: Rules: - ExpirationInDays: 1 Status: Enabled - AbortIncompleteMultipartUpload: DaysAfterInitiation: 1 Status: Enabled Tags: - Key: Name Value: !Join - '-' - - Fn::FindInMap: [Constants, Values, 'NamePrefix'] - 'loggingbucket' - Fn::GetAtt: [ RandomCharString, RandomString ] - Key: Project Value: !FindInMap [Constants, Values, 'ProjectTag'] # LoggingBucketPolicy - The policy for the logging bucket # # Note that CloudTrail must be able to add objects to the S3 # bucket for logs from both the source and logging accounts. LoggingBucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: Ref: LoggingBucket PolicyDocument: Statement: - Sid: AWSCloudTrailAclCheckESSLab Effect: Allow Principal: Service: cloudtrail.amazonaws.com Action: s3:GetBucketAcl Resource: !GetAtt LoggingBucket.Arn - Sid: AWSCloudTrailWriteESSLab Effect: Allow Principal: Service: cloudtrail.amazonaws.com Action: s3:PutObject Resource: - !Sub ${LoggingBucket.Arn}/AWSLogs/${AWS::AccountId}/* Condition: StringEquals: s3:x-amz-acl: bucket-owner-full-control # LoggingTrail - Create the CloudTrail # # NOTE: The DependsOn clause is needed because the bucket policy must be in # place before the trail can be created. LoggingTrail: Type: AWS::CloudTrail::Trail DependsOn: LoggingBucketPolicy Properties: IncludeGlobalServiceEvents: True IsLogging: True IsMultiRegionTrail: False S3BucketName: !Ref LoggingBucket Tags: - Key: Name Value: !Join - '-' - - Fn::FindInMap: [Constants, Values, 'NamePrefix'] - 'trail' - Fn::GetAtt: [ RandomCharString, RandomString ] - Key: Project Value: !FindInMap [Constants, Values, 'ProjectTag'] # SecAdministratorMaciePolicy - Managed Policy for Amazon Macie for Security Administrator role SecAdministratorMaciePolicy: Type: AWS::IAM::ManagedPolicy Properties: Description: 'Policy for Security Administrator role' Path: '/' PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: macie2:* Resource: '*' # SecOperatorMaciePolicy - Managed Policy for Amazon Macie Security Operator role # # NOTE: This policy *initially* is identical to SecAdministratorMaciePolicy. # You will update this policy in the console as part of the lab exercise. SecOperatorMaciePolicy: Type: AWS::IAM::ManagedPolicy Properties: Description: 'Policy for Security Operator role' Path: '/' PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: macie2:* Resource: '*' # SecAdministratorRole - Security Administrator Role # # This role would be used by Security Administrators. You will be able # to test this by doing a "Switch Role" from the console. # # NOTE: There is an AWS managed policy that provides full access to Amazon # Macie. The reason one is being created here is that it allows you to learn # how to create and edit your own managed policies. # # NOTE: Amazon SNS is included because it provides for a cleaner console # experience for Amazon Inspector. SecAdministratorRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: AWS: - !Ref AWS::AccountId Action: - "sts:AssumeRole" ManagedPolicyArns: - arn:aws:iam::aws:policy/AWSCloudTrailFullAccess - arn:aws:iam::aws:policy/AmazonGuardDutyFullAccess - arn:aws:iam::aws:policy/AmazonInspectorFullAccess - arn:aws:iam::aws:policy/AmazonSNSFullAccess - !Ref SecAdministratorMaciePolicy MaxSessionDuration: 43200 # SecOperatorRole - Security Operator Role # # This role would be used by Security Operator. You will be able # to test this by doing a "Switch Role" from the console. # # NOTE: Amazon SNS is included because it provides for a cleaner console # experience for Amazon Inspector. # # NOTE: This role *initially* is almost identical to SecAdministratorMacieRole. # You will update the definition in the console as part of the lab exercise. SecOperatorRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: AWS: - !Ref AWS::AccountId Action: - "sts:AssumeRole" ManagedPolicyArns: - arn:aws:iam::aws:policy/AWSCloudTrailFullAccess - arn:aws:iam::aws:policy/AmazonGuardDutyFullAccess - arn:aws:iam::aws:policy/AmazonInspectorFullAccess - arn:aws:iam::aws:policy/AmazonSNSReadOnlyAccess - !Ref SecOperatorMaciePolicy MaxSessionDuration: 43200 LampInspectorResourceGroup: Type: AWS::Inspector::ResourceGroup Properties: ResourceGroupTags : - Key: Name Value: !Join - '' - - Fn::FindInMap: [Constants, Values, 'NamePrefix'] - '-server' - Key: Project Value: !FindInMap [Constants, Values, 'ProjectTag'] LampInspectorAssessmentTarget: Type: AWS::Inspector::AssessmentTarget Properties: ResourceGroupArn: !GetAtt LampInspectorResourceGroup.Arn LampInspectorAssessmentTemplate: Type: AWS::Inspector::AssessmentTemplate Properties: AssessmentTargetArn: !GetAtt LampInspectorAssessmentTarget.Arn DurationInSeconds: 900 RulesPackageArns: - !FindInMap [RegionMap, !Ref 'AWS::Region', CVE] UserAttributesForFindings: - Key: Name Value: !Join - '-' - - Fn::FindInMap: [Constants, Values, 'NamePrefix'] - 'server' - Fn::GetAtt: [ RandomCharString, RandomString ] - Key: Project Value: !FindInMap [Constants, Values, 'ProjectTag'] Outputs: SecAdministratorRoleURL: Value: !Sub https://signin.aws.amazon.com/switchrole?account=${AWS::AccountId}&roleName=${SecAdministratorRole}&displayName=SecAdministrator Description: Switch to Security Administrator role SecOperatorRoleURL: Value: !Sub https://signin.aws.amazon.com/switchrole?account=${AWS::AccountId}&roleName=${SecOperatorRole}&displayName=SecOperator Description: Switch to Security Operator role LoggingBucketName: Value: !Ref LoggingBucket Description: Logging bucket name