Last active
April 25, 2021 21:14
-
-
Save kepstein/91b34f8921294b1babc84fe5d0bc29ea to your computer and use it in GitHub Desktop.
CloudFormation to deploy Prowler as a CodeBuild project
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
AWSTemplateFormatVersion: 2010-09-09 | |
Description: Creates a CodeBuild project to audit an AWS account with Prowler and stores the html report in a S3 bucket. This will run onece at the beginning and on a schedule afterwards. Partial contribution from https://github.com/stevecjones | |
Parameters: | |
ServiceName: | |
Description: 'Specifies the service name used within component naming' | |
Type: String | |
Default: 'prowler' | |
LogsRetentionInDays: | |
Description: 'Specifies the number of days you want to retain CodeBuild run log events in the specified log group. Junit reports are kept for 30 days, HTML reports in S3 are not deleted' | |
Type: Number | |
Default: 3 | |
AllowedValues: [1, 3, 5, 7, 14, 30, 60, 90, 180, 365] | |
ProwlerOptions: | |
Description: 'Options to pass to Prowler command, make sure at least -M junit-xml is used for CodeBuild reports. Use -r for the region to send API queries, -f to filter only one region, -M output formats, -c for comma separated checks, for all checks do not use -c or -g, for more options see -h. For a complete assessment use "-M text,junit-xml,html,csv,json", for SecurityHub integration use "-r region -f region -M text,junit-xml,html,csv,json,json-asff -S -q"' | |
Type: String | |
# Prowler command below runs a set of checks, configure it base on your needs, no options will run all regions all checks. | |
# option -M junit-xml is requirede in order to get the report in CodeBuild. | |
Default: -r us-east-1 -M junit-xml,html,csv -g cislevel1 | |
ProwlerScheduler: | |
Description: The time when Prowler will run in cron format. Default is daily at 22:00h or 10PM 'cron(0 22 * * ? *)', for every 5 hours also works 'rate(5 hours)'. More info here https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html. | |
Type: String | |
Default: 'cron(0 22 * * ? *)' | |
Resources: | |
CodeBuildStartBuild: | |
Type: 'Custom::CodeBuildStartBuild' | |
Properties: | |
Build: !Ref ProwlerCodeBuild | |
ServiceToken: | |
'Fn::GetAtt': | |
- CodeBuildStartBuildLambda | |
- Arn | |
CodeBuildStartBuildLambdaRole: | |
Type: 'AWS::IAM::Role' | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: lambda.amazonaws.com | |
Action: | |
- 'sts:AssumeRole' | |
Path: / | |
ManagedPolicyArns: | |
- 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' | |
Policies: | |
- PolicyName: StartBuildInline | |
PolicyDocument: | |
Statement: | |
- Effect: Allow | |
Action: | |
- 'codebuild:StartBuild' | |
Resource: '*' | |
CodeBuildStartBuildLambda: | |
Type: 'AWS::Lambda::Function' | |
Properties: | |
Handler: index.lambda_handler | |
MemorySize: 128 | |
Role: !Sub ${CodeBuildStartBuildLambdaRole.Arn} | |
Timeout: 120 | |
Runtime: python3.6 | |
Code: | |
ZipFile: | | |
import boto3 | |
import cfnresponse | |
from botocore.exceptions import ClientError | |
def lambda_handler(event,context): | |
props = event['ResourceProperties'] | |
codebuil_client = boto3.client('codebuild') | |
if (event['RequestType'] == 'Create' or event['RequestType'] == 'Update'): | |
try: | |
response = codebuil_client.start_build(projectName=props['Build']) | |
print(response) | |
print("Respond: SUCCESS") | |
cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) | |
except Exception as ex: | |
print(ex.response['Error']['Message']) | |
cfnresponse.send(event, context, cfnresponse.FAILED, ex.response) | |
ArtifactBucket: | |
Type: AWS::S3::Bucket | |
Properties: | |
Tags: | |
- Key: Name | |
Value: !Join ['-', [!Ref 'ServiceName', !Ref 'AWS::AccountId', 'S3', 'Prowler', !Ref 'AWS::StackName']] | |
BucketName: !Sub '${ServiceName}-reports-${AWS::Region}-prowler-${AWS::AccountId}' | |
AccessControl: LogDeliveryWrite | |
VersioningConfiguration: | |
Status: Enabled | |
BucketEncryption: | |
ServerSideEncryptionConfiguration: | |
- ServerSideEncryptionByDefault: | |
SSEAlgorithm: AES256 | |
PublicAccessBlockConfiguration: | |
BlockPublicAcls: true | |
BlockPublicPolicy: true | |
IgnorePublicAcls: true | |
RestrictPublicBuckets: true | |
ArtifactBucketPolicy: | |
Type: AWS::S3::BucketPolicy | |
Properties: | |
Bucket: !Ref 'ArtifactBucket' | |
PolicyDocument: | |
Id: Content | |
Version: '2012-10-17' | |
Statement: | |
- Action: '*' | |
Condition: | |
Bool: | |
aws:SecureTransport: 'false' | |
Effect: Deny | |
Principal: '*' | |
Resource: | |
- !Join ['', ['arn:aws:s3:::', !Ref 'ArtifactBucket', '/*']] | |
Sid: S3ForceSSL | |
- Action: 's3:PutObject' | |
Condition: | |
'Null': | |
s3:x-amz-server-side-encryption: 'true' | |
Effect: Deny | |
Principal: '*' | |
Resource: | |
- !Join ['', ['arn:aws:s3:::', !Ref 'ArtifactBucket', '/*']] | |
Sid: DenyUnEncryptedObjectUploads | |
CodeBuildServiceRole: | |
Type: AWS::IAM::Role | |
Metadata: | |
cfn_nag: | |
rules_to_suppress: | |
- id: W28 | |
reason: "Explicit name is required for this resource to avoid circular dependencies." | |
Properties: | |
RoleName: !Sub 'prowler-codebuild-role-${ServiceName}-${AWS::StackName}' | |
Path: '/service-role/' | |
ManagedPolicyArns: | |
- 'arn:aws:iam::aws:policy/job-function/SupportUser' | |
- 'arn:aws:iam::aws:policy/job-function/ViewOnlyAccess' | |
- 'arn:aws:iam::aws:policy/SecurityAudit' | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- | |
Action: 'sts:AssumeRole' | |
Effect: Allow | |
Principal: | |
Service: | |
- codebuild.amazonaws.com | |
Policies: | |
- PolicyName: LogGroup | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Action: | |
- logs:CreateLogGroup | |
- logs:CreateLogStream | |
- logs:PutLogEvents | |
Effect: Allow | |
Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*' | |
- PolicyName: S3 | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Action: | |
- s3:PutObject | |
- s3:GetObject | |
- s3:GetObjectVersion | |
- s3:GetBucketAcl | |
- s3:GetBucketLocation | |
Effect: Allow | |
Resource: !Sub 'arn:aws:s3:::${ArtifactBucket}/*' | |
- PolicyName: CodeBuild | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Action: | |
- codebuild:CreateReportGroup | |
- codebuild:CreateReport | |
- codebuild:UpdateReport | |
- codebuild:BatchPutTestCases | |
- codebuild:BatchPutCodeCoverages | |
Effect: Allow | |
Resource: !Sub 'arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:report-group/*' | |
- PolicyName: SecurityHubBatchImportFindings | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Action: | |
- securityhub:BatchImportFindings | |
Effect: Allow | |
Resource: !Sub 'arn:aws:securityhub:${AWS::Region}::product/prowler/prowler' | |
- PolicyName: AssumeRole | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Action: | |
- sts:AssumeRole | |
Effect: Allow | |
Resource: !Sub 'arn:aws:iam::${AWS::AccountId}:role/service-role/prowler-codebuild-role' | |
ProwlerCodeBuild: | |
Type: AWS::CodeBuild::Project | |
Properties: | |
Artifacts: | |
Type: NO_ARTIFACTS | |
Source: | |
Type: NO_SOURCE | |
BuildSpec: | | |
version: 0.2 | |
phases: | |
install: | |
runtime-versions: | |
python: 3.8 | |
commands: | |
- echo "Installing Prowler and dependencies..." | |
- pip3 install detect-secrets | |
- yum -y install jq | |
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" | |
- unzip awscliv2.zip | |
- ./aws/install | |
- git clone https://github.com/toniblyx/prowler | |
build: | |
commands: | |
- echo "Running Prowler as ./prowler $PROWLER_OPTIONS" | |
- cd prowler | |
- ./prowler $PROWLER_OPTIONS | |
post_build: | |
commands: | |
- echo "Uploading reports to S3..." | |
- aws s3 cp --sse AES256 output/ s3://$BUCKET_REPORT/ --recursive | |
- echo "Done!" | |
reports: | |
prowler: | |
files: | |
- '**/*' | |
base-directory: 'prowler/junit-reports' | |
file-format: JunitXml | |
Environment: | |
# AWS CodeBuild free tier includes 100 build minutes of BUILD_GENERAL1_SMALL per month. | |
# BUILD_GENERAL1_SMALL: Use up to 3 GB memory and 2 vCPUs for builds. $0.005/minute. | |
# BUILD_GENERAL1_MEDIUM: Use up to 7 GB memory and 4 vCPUs for builds. $0.01/minute. | |
# BUILD_GENERAL1_LARGE: Use up to 15 GB memory and 8 vCPUs for builds. $0.02/minute. | |
# BUILD_GENERAL1_2XLARGE: Use up to 144 GB memory and 72 vCPUs for builds. $0.20/minute. | |
ComputeType: "BUILD_GENERAL1_SMALL" | |
Image: "aws/codebuild/amazonlinux2-x86_64-standard:3.0" | |
Type: "LINUX_CONTAINER" | |
EnvironmentVariables: | |
- Name: BUCKET_REPORT | |
Value: !Ref 'ArtifactBucket' | |
Type: PLAINTEXT | |
- Name: PROWLER_OPTIONS | |
Value: !Ref 'ProwlerOptions' | |
Type: PLAINTEXT | |
Description: Run Prowler assessment | |
ServiceRole: !GetAtt CodeBuildServiceRole.Arn | |
TimeoutInMinutes: 300 | |
ProwlerCodeBuildReportGroup: | |
Type: AWS::CodeBuild::ReportGroup | |
Properties: | |
Name: !Sub 'prowler-report-group-${ServiceName}-${AWS::StackName}' | |
Type: TEST | |
ExportConfig: | |
ExportConfigType: NO_EXPORT | |
ProwlerLogGroup: | |
Type: 'AWS::Logs::LogGroup' | |
Properties: | |
LogGroupName: !Sub '/aws/codebuild/${ProwlerCodeBuild}' | |
RetentionInDays: !Ref LogsRetentionInDays | |
ProwlerSchedule: | |
Type: "AWS::Events::Rule" | |
Properties: | |
Description: > | |
A schedule for the Lambda function that triggers Prowler in CodeBuild.. | |
ScheduleExpression: !Ref ProwlerScheduler | |
State: ENABLED | |
Targets: | |
- Arn: !Sub ${ProwlerScheduleLambdaFunction.Arn} | |
Id: ProwlerSchedule | |
ProwlerSchedulePermission: | |
Type: "AWS::Lambda::Permission" | |
Properties: | |
Action: 'lambda:InvokeFunction' | |
FunctionName: !Sub ${ProwlerScheduleLambdaFunction.Arn} | |
Principal: 'events.amazonaws.com' | |
SourceArn: !Sub ${ProwlerSchedule.Arn} | |
ProwlerScheduleLambdaFunction: | |
Type: "AWS::Lambda::Function" | |
Properties: | |
Handler: index.lambda_handler | |
MemorySize: 128 | |
Role: !Sub ${CodeBuildStartBuildLambdaRole.Arn} | |
Timeout: 120 | |
Runtime: python3.6 | |
Environment: | |
Variables: | |
buildName: !Ref ProwlerCodeBuild | |
Code: | |
ZipFile: | | |
import boto3 | |
import os | |
def lambda_handler(event,context): | |
codebuild_client = boto3.client('codebuild') | |
print("Running Prowler scheduled!: " + os.environ['buildName']) | |
project_name = os.environ['buildName'] | |
response = codebuild_client.start_build(projectName=project_name) | |
print(response) | |
print("Respond: SUCCESS") | |
Outputs: | |
ArtifactBucketName: | |
Description: Artifact Bucket Name | |
Value: !Ref 'ArtifactBucket' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment