AWSTemplateFormatVersion: '2010-09-09' Description: This AWS CloudFormation Template configures an Trivy service that is used for the Container DevSecOps Demo. Parameters: ResourceName: Type: String DevSecOpsResources: Type: String Resources: ### Trivy ECR Repository TrivyRepository: Type: AWS::ECR::Repository Properties: RepositoryName: !Join [ '-', [ !Ref ResourceName, 'trivy' ] ] TrivyCodeBuildRole: Type: AWS::IAM::Role Properties: RoleName: TrivyCodeBuildRole AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - codebuild.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: ServicePolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: '*' - Effect: Allow Action: - ecr:* Resource: '*' - Effect: Allow Action: - s3:PutObject - s3:GetObject - s3:ListObject Resource: '*' TrivyCodeBuild: Type: AWS::CodeBuild::Project Properties: Artifacts: Type: NO_ARTIFACTS Environment: ComputeType: BUILD_GENERAL1_SMALL Image: aws/codebuild/standard:5.0-22.06.30 PrivilegedMode: True Type: LINUX_CONTAINER ImagePullCredentialsType: CODEBUILD EnvironmentVariables: - Name: IMAGE Value: !Sub - ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${Repo} - { Repo: !Ref TrivyRepository} Name: !Join [ '-', [ !Ref ResourceName, 'Trivy', 'build' ] ] ServiceRole: !GetAtt TrivyCodeBuildRole.Arn Source: Type: S3 Location: !Join [ "/", [ !Ref DevSecOpsResources , devsecops, trivy-build.zip ] ] BuildSpec: | version: 0.2 phases: install: runtime-versions: python: 3.7 pre_build: commands: - curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - - apt-get update && apt-get install -y python-dev - curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py - python get-pip.py - pip install awscli - $(aws ecr get-login --no-include-email) build: commands: - docker build $CODEBUILD_SRC_DIR -t $IMAGE - docker push $IMAGE CodeBuildStarter: Type: Custom::CodeBuildStarter Properties: ServiceToken: !GetAtt CodeBuildStarterLambda.Arn ProjectName: !Ref TrivyCodeBuild CodeBuildStarterLambdaRole: Type: AWS::IAM::Role Properties: RoleName: TrivyCodeBuildStarterLambdaRole AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: Policy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - codebuild:* Resource: '*' CodeBuildStarterLambda: Type: AWS::Lambda::Function Properties: FunctionName: !Join [ '-', [ !Ref ResourceName, 'codebuild', 'starter' ] ] Role: !GetAtt CodeBuildStarterLambdaRole.Arn Runtime: python3.7 Handler: index.handler Code: ZipFile: | import json import boto3 from botocore.vendored import requests codebuild_client = boto3.client('codebuild') def handler(event, context): try: if event['RequestType'] == 'Create': response = codebuild_client.start_build( projectName=event['ResourceProperties']['ProjectName'] ) send(event, context, "SUCCESS") elif event['RequestType'] == 'Update': response = codebuild_client.start_build( projectName=event['ResourceProperties']['ProjectName'] ) send(event, context, "SUCCESS") elif event['RequestType'] == 'Delete': send(event, context, "SUCCESS") else: send(event, context, "FAILED") except: send(event, context, "FAILED") def send(event, context, responseStatus): responseUrl = event['ResponseURL'] print(responseUrl) responseBody = {} responseBody['Status'] = responseStatus responseBody['Reason'] = 'See the details in CloudWatch Log Stream: ' + context.log_stream_name responseBody['PhysicalResourceId'] = context.log_stream_name responseBody['StackId'] = event['StackId'] responseBody['RequestId'] = event['RequestId'] responseBody['LogicalResourceId'] = event['LogicalResourceId'] json_responseBody = json.dumps(responseBody, indent=4, sort_keys=True, default=str) print("Response body:\n" + json_responseBody) headers = { 'content-type' : '', 'content-length' : str(len(json_responseBody)) } try: response = requests.put(responseUrl, data=json_responseBody, headers=headers) print("Status code: " + response.reason) except Exception as e: print("send(..) failed executing requests.put(..): " + str(e)) EcsSlr: Type: AWS::IAM::ServiceLinkedRole Properties: AWSServiceName: ecs.amazonaws.com Description: Role to enable Amazon ECS to manage your cluster. Cluster: Type: AWS::ECS::Cluster DependsOn: EcsSlr TrivyTaskRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: !Join - "" - - ecs-tasks. - !Ref AWS::URLSuffix Version: 2012-10-17 TrivyTaskRoleDefaultPolicy: Type: AWS::IAM::Policy Properties: PolicyDocument: Statement: - Action: - ecr:GetAuthorizationToken - ecr:BatchCheckLayerAvailability - ecr:GetDownloadUrlForLayer - ecr:GetRepositoryPolicy - ecr:DescribeRepositories - ecr:ListImages - ecr:DescribeImages - ecr:BatchGetImage - ecr:InitiateLayerUpload - ecr:UploadLayerPart - ecr:CompleteLayerUpload - ecr:PutImage Effect: Allow Resource: "*" Version: 2012-10-17 PolicyName: TrivyTaskRoleDefaultPolicy Roles: - !Ref TrivyTaskRole TaskLoggingLogGroup: Type: AWS::Logs::LogGroup Properties: # LogGroupName: !Join [ '', [ '/aws/ecs/task',!Ref ResourceName, '-Trivy-engine' ] ] RetentionInDays: 365 DeletionPolicy: Retain TrivyTaskExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: Fn::Join: - "" - - ecs-tasks. - !Ref AWS::URLSuffix Version: 2012-10-17 TrivyTaskExecutionRoleDefaultPolicy: Type: AWS::IAM::Policy Properties: PolicyDocument: Statement: - Action: - ecr:BatchCheckLayerAvailability - ecr:GetDownloadUrlForLayer - ecr:BatchGetImage Effect: Allow Resource: "*" - Action: ecr:GetAuthorizationToken Effect: Allow Resource: "*" - Action: - logs:CreateLogStream - logs:PutLogEvents Effect: Allow Resource: !GetAtt - TaskLoggingLogGroup - Arn Version: 2012-10-17 PolicyName: TrivyTaskExecutionRoleDefaultPolicy Roles: - !Ref TrivyTaskExecutionRole TrivyTask: Type: AWS::ECS::TaskDefinition Properties: ContainerDefinitions: - Essential: True Image: !Sub - ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${Repo} - { Repo: !Ref TrivyRepository} LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref TaskLoggingLogGroup awslogs-stream-prefix: Trivy awslogs-region: Ref: AWS::Region Name: TrivyContainer PortMappings: - ContainerPort: 8080 Protocol: tcp Cpu: "1024" ExecutionRoleArn: !GetAtt - TrivyTaskExecutionRole - Arn Family: TrivyFargateTask Memory: "6144" NetworkMode: awsvpc RequiresCompatibilities: - FARGATE TaskRoleArn: !GetAtt - TrivyTaskRole - Arn DependsOn: - CodeBuildStarter LBSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Security Group for ELB SecurityGroupEgress: [] SecurityGroupIngress: - CidrIp: 0.0.0.0/0 Description: Allow from anyone on port 80 FromPort: 80 IpProtocol: tcp ToPort: 80 VpcId: !ImportValue 'TrivyVpcStack:VpcID' LB: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: LoadBalancerAttributes: [] Scheme: internet-facing SecurityGroups: - !GetAtt - LBSecurityGroup - GroupId Subnets: !Split - "," - !ImportValue 'TrivyVpcStack:PublicSubnets' Type: application TrivyFargateServiceSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: TrivyFargateService/SecurityGroup SecurityGroupEgress: - CidrIp: 0.0.0.0/0 Description: Allow all outbound traffic by default IpProtocol: "-1" SecurityGroupIngress: [] VpcId: !ImportValue 'TrivyVpcStack:VpcID' LBSecurityGrouptoTrivyFargateServiceSecurityGroup: Type: AWS::EC2::SecurityGroupEgress Properties: GroupId: !GetAtt - LBSecurityGroup - GroupId IpProtocol: tcp Description: Load balancer to target DestinationSecurityGroupId: !GetAtt - TrivyFargateServiceSecurityGroup - GroupId FromPort: 8080 ToPort: 8080 LBPublicListenerECSGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: HealthCheckIntervalSeconds: 60 HealthCheckPath: / HealthCheckPort: "8080" HealthCheckTimeoutSeconds: 5 Matcher: HttpCode: 200,404 Port: 80 Protocol: HTTP TargetGroupAttributes: [] Targets: [] TargetType: ip VpcId: !ImportValue 'TrivyVpcStack:VpcID' LBPublicListener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - TargetGroupArn: Ref: LBPublicListenerECSGroup Type: forward LoadBalancerArn: !Ref LB Port: 80 Protocol: HTTP Certificates: [] TrivyFargateService: Type: AWS::ECS::Service Properties: TaskDefinition: !Ref TrivyTask Cluster: !Ref Cluster DeploymentConfiguration: MaximumPercent: 200 MinimumHealthyPercent: 50 DesiredCount: 1 LaunchType: FARGATE LoadBalancers: - ContainerName: TrivyContainer ContainerPort: 8080 TargetGroupArn: !Ref LBPublicListenerECSGroup NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: DISABLED SecurityGroups: - !GetAtt - TrivyFargateServiceSecurityGroup - GroupId Subnets: !Split - "," - !ImportValue 'TrivyVpcStack:PrivateSubnets' DependsOn: - LBPublicListener TrivyFargateServiceSecurityGroupfromTrivyFargateCdkStackLBSecurityGroup: Type: AWS::EC2::SecurityGroupIngress Properties: IpProtocol: tcp Description: Load balancer to target FromPort: 8080 GroupId: !GetAtt - TrivyFargateServiceSecurityGroup - GroupId SourceSecurityGroupId: !GetAtt - LBSecurityGroup - GroupId ToPort: 8080 Outputs: TrivyLB: Value: !GetAtt - LB - DNSName Export: Name: TrivyFargateStack:TrivyLB