|
| 1 | +# Copyright 2024 Chris Farris <chrisf@primeharbor.com> |
| 2 | +# |
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +# you may not use this file except in compliance with the License. |
| 5 | +# You may obtain a copy of the License at |
| 6 | +# |
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +# |
| 9 | +# Unless required by applicable law or agreed to in writing, software |
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +# See the License for the specific language governing permissions and |
| 13 | +# limitations under the License. |
| 14 | + |
| 15 | +# If you don't have a lot of guardduty alerts, your detection flow may be broken and you wouldn't know it. |
| 16 | +# This CFT creates a public S3 bucket every four hours, so you can alert on no GuardDuty messages in the last four hours. |
| 17 | + |
| 18 | + |
| 19 | + |
| 20 | +AWSTemplateFormatVersion: '2010-09-09' |
| 21 | +Description: Deploy a Lambda to create a public S3 bucket every four hours to ensure GuardDuty detections are in place |
| 22 | + |
| 23 | +Resources: |
| 24 | + # IAM Role for Lambda |
| 25 | + LambdaExecutionRole: |
| 26 | + Type: 'AWS::IAM::Role' |
| 27 | + Properties: |
| 28 | + AssumeRolePolicyDocument: |
| 29 | + Version: '2012-10-17' |
| 30 | + Statement: |
| 31 | + - Effect: 'Allow' |
| 32 | + Principal: |
| 33 | + Service: 'lambda.amazonaws.com' |
| 34 | + Action: 'sts:AssumeRole' |
| 35 | + Policies: |
| 36 | + - PolicyName: 'S3AccessPolicy' |
| 37 | + PolicyDocument: |
| 38 | + Version: '2012-10-17' |
| 39 | + Statement: |
| 40 | + - Effect: 'Allow' |
| 41 | + Action: |
| 42 | + - 's3:ListAllMyBuckets' |
| 43 | + - 's3:CreateBucket' |
| 44 | + - 's3:DeleteBucket' |
| 45 | + - 's3:PutBucketPolicy' |
| 46 | + - 's3:PutBucketPublicAccessBlock' |
| 47 | + Resource: '*' |
| 48 | + ManagedPolicyArns: |
| 49 | + - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole |
| 50 | + |
| 51 | + # Lambda Function |
| 52 | + PublicS3BucketLambda: |
| 53 | + Type: 'AWS::Lambda::Function' |
| 54 | + Properties: |
| 55 | + Handler: 'index.lambda_handler' |
| 56 | + Role: !GetAtt LambdaExecutionRole.Arn |
| 57 | + Runtime: 'python3.12' |
| 58 | + Timeout: 30 |
| 59 | + Code: |
| 60 | + ZipFile: | |
| 61 | + import boto3 |
| 62 | + import time |
| 63 | + import json |
| 64 | +
|
| 65 | + def lambda_handler(event, context): |
| 66 | + s3 = boto3.client('s3') |
| 67 | + epoch_time = int(time.time()) |
| 68 | + bucket_name = f'public-test-{epoch_time}' |
| 69 | +
|
| 70 | + # Delete existing buckets starting with 'public-test' |
| 71 | + response = s3.list_buckets() |
| 72 | + for bucket in response['Buckets']: |
| 73 | + if bucket['Name'].startswith('public-test'): |
| 74 | + s3.delete_bucket(Bucket=bucket['Name']) |
| 75 | +
|
| 76 | + # Create a new S3 bucket |
| 77 | + s3.create_bucket(Bucket=bucket_name) |
| 78 | +
|
| 79 | + # Disable Block Public Access |
| 80 | + public_access_block_config = { |
| 81 | + 'BlockPublicAcls': False, |
| 82 | + 'IgnorePublicAcls': False, |
| 83 | + 'BlockPublicPolicy': False, |
| 84 | + 'RestrictPublicBuckets': False |
| 85 | + } |
| 86 | + s3.put_public_access_block( |
| 87 | + Bucket=bucket_name, |
| 88 | + PublicAccessBlockConfiguration=public_access_block_config |
| 89 | + ) |
| 90 | +
|
| 91 | + # Make the bucket public |
| 92 | + bucket_policy = { |
| 93 | + "Version": "2012-10-17", |
| 94 | + "Statement": [ |
| 95 | + { |
| 96 | + "Effect": "Allow", |
| 97 | + "Principal": "*", |
| 98 | + "Action": "s3:GetObject", |
| 99 | + "Resource": f"arn:aws:s3:::{bucket_name}/*" |
| 100 | + } |
| 101 | + ] |
| 102 | + } |
| 103 | +
|
| 104 | + s3.put_bucket_policy(Bucket=bucket_name, Policy=json.dumps(bucket_policy)) |
| 105 | +
|
| 106 | + return {'statusCode': 200, 'body': f'Bucket {bucket_name} created and made public.'} |
| 107 | +
|
| 108 | + # EventBridge Rule to trigger Lambda every 4 hours |
| 109 | + LambdaScheduleRule: |
| 110 | + Type: 'AWS::Events::Rule' |
| 111 | + Properties: |
| 112 | + ScheduleExpression: 'rate(4 hours)' |
| 113 | + Targets: |
| 114 | + - Arn: !GetAtt PublicS3BucketLambda.Arn |
| 115 | + Id: 'PublicS3BucketLambdaTarget' |
| 116 | + |
| 117 | + # Permission for EventBridge to invoke Lambda |
| 118 | + LambdaInvokePermission: |
| 119 | + Type: 'AWS::Lambda::Permission' |
| 120 | + Properties: |
| 121 | + FunctionName: !Ref PublicS3BucketLambda |
| 122 | + Action: 'lambda:InvokeFunction' |
| 123 | + Principal: 'events.amazonaws.com' |
| 124 | + SourceArn: !GetAtt LambdaScheduleRule.Arn |
| 125 | + |
| 126 | +Outputs: |
| 127 | + LambdaFunctionName: |
| 128 | + Description: 'Name of the Lambda function created' |
| 129 | + Value: !Ref PublicS3BucketLambda |
| 130 | + LambdaFunctionArn: |
| 131 | + Description: 'ARN of the Lambda function created' |
| 132 | + Value: !GetAtt PublicS3BucketLambda.Arn |
0 commit comments