diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/README.md b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/README.md index 724b9b7c9..e07f9a0e1 100644 --- a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/README.md +++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/README.md @@ -32,7 +32,7 @@ Here is a minimal deployable pattern definition in Typescript: const certificate = acm.Certificate.fromCertificateArn( scope, 'existing-cert', - "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012" + "arn:aws:acm:us-east-1:123456789012:certificate/11112222-3333-1234-1234-123456789012" ); const props: AlbToLambdaProps = { lambdaFunctionProps: { diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/alb-lambda.test.ts b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/alb-lambda.test.ts index 1850a8697..6579ab95c 100644 --- a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/alb-lambda.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/alb-lambda.test.ts @@ -24,7 +24,7 @@ function GetFakeCertificate(scope: Construct, id: string): acm.ICertificate { return acm.Certificate.fromCertificateArn( scope, id, - "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-1234567890AB" + "arn:aws:acm:us-east-1:123456789012:certificate/11112222-3333-1234-1234-123456789012" ); } @@ -677,7 +677,7 @@ test('Test providing certificateArns is an error', () => { handler: 'index.handler' }, listenerProps: { - certificateArns: [' arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012'] + certificateArns: [' arn:aws:acm:us-east-1:123456789012:certificate/11112222-3333-1234-1234-123456789012'] }, targetProps: { targetGroupName: 'different-name' diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiExistingResources.expected.json b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiExistingResources.expected.json index 2fbd1115f..3b5b44795 100644 --- a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiExistingResources.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiExistingResources.expected.json @@ -652,7 +652,35 @@ ] } }, - "existingFunctionServiceRoleC8C38BE5": { + "lambdasg93781054": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "privateApiExistingResources/lambda-sg", + "SecurityGroupEgress": [ + { + "CidrIp": "255.255.255.255/32", + "Description": "Disallow all traffic", + "FromPort": 252, + "IpProtocol": "icmp", + "ToPort": 86 + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W29", + "reason": "CDK created rule that blocks all traffic." + } + ] + } + } + }, + "LambdaFunctionServiceRole0C4CDE0B": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -667,51 +695,92 @@ ], "Version": "2012-10-17" }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - }, + "Policies": [ { - "Fn::Join": [ - "", - [ - "arn:", + "PolicyDocument": { + "Statement": [ { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" - ] - ] + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRolePolicy" } ] } }, - "existingFunctionSecurityGroupFE7872B0": { - "Type": "AWS::EC2::SecurityGroup", + "LambdaFunctionServiceRoleDefaultPolicy126C8897": { + "Type": "AWS::IAM::Policy", "Properties": { - "GroupDescription": "Automatic security group for Lambda Function privateApiExistingResourcesexistingFunctionB97ADEC0", - "SecurityGroupEgress": [ + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:AssignPrivateIpAddresses", + "ec2:UnassignPrivateIpAddresses" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRoleDefaultPolicy126C8897", + "Roles": [ { - "CidrIp": "0.0.0.0/0", - "Description": "Allow all outbound traffic by default", - "IpProtocol": "-1" + "Ref": "LambdaFunctionServiceRole0C4CDE0B" } - ], - "VpcId": { - "Ref": "Vpc8378EB38" + ] + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W12", + "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC." + } + ] } } }, - "existingFunctionEE93E252": { + "LambdaFunctionBF21E41F": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { @@ -722,17 +791,25 @@ }, "Role": { "Fn::GetAtt": [ - "existingFunctionServiceRoleC8C38BE5", + "LambdaFunctionServiceRole0C4CDE0B", "Arn" ] }, + "Environment": { + "Variables": { + "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1" + } + }, "Handler": "index.handler", "Runtime": "nodejs12.x", + "TracingConfig": { + "Mode": "Active" + }, "VpcConfig": { "SecurityGroupIds": [ { "Fn::GetAtt": [ - "existingFunctionSecurityGroupFE7872B0", + "lambdasg93781054", "GroupId" ] } @@ -751,36 +828,69 @@ } }, "DependsOn": [ - "existingFunctionServiceRoleC8C38BE5" - ] + "LambdaFunctionServiceRoleDefaultPolicy126C8897", + "LambdaFunctionServiceRole0C4CDE0B" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions." + }, + { + "id": "W89", + "reason": "This is not a rule for the general case, just for specific use cases/industries" + }, + { + "id": "W92", + "reason": "Impossible for us to define the correct concurrency for clients" + } + ] + } + } }, - "existingFunctionInvokeServicePrincipalelasticloadbalancingamazonawscomCF0F2020": { + "LambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom842E1595": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "existingFunctionEE93E252", + "LambdaFunctionBF21E41F", "Arn" ] }, "Principal": "elasticloadbalancing.amazonaws.com" } }, - "newlbF396DAF2": { + "existingalbalb3A941601": { "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", "Properties": { "LoadBalancerAttributes": [ { "Key": "deletion_protection.enabled", "Value": "false" + }, + { + "Key": "access_logs.s3.enabled", + "Value": "true" + }, + { + "Key": "access_logs.s3.bucket", + "Value": { + "Ref": "existingalb0F60CC48" + } + }, + { + "Key": "access_logs.s3.prefix", + "Value": "" } ], "Scheme": "internal", "SecurityGroups": [ { "Fn::GetAtt": [ - "newlbSecurityGroup04195C74", + "existingalbalbSecurityGroupC8DD2920", "GroupId" ] } @@ -797,12 +907,16 @@ } ], "Type": "application" - } + }, + "DependsOn": [ + "existingalbPolicy6C7AF240", + "existingalb0F60CC48" + ] }, - "newlbSecurityGroup04195C74": { + "existingalbalbSecurityGroupC8DD2920": { "Type": "AWS::EC2::SecurityGroup", "Properties": { - "GroupDescription": "Automatically created Security Group for ELB privateApiExistingResourcesnewlbEFACFA2B", + "GroupDescription": "Automatically created Security Group for ELB privateApiExistingResourcesexistingalbalbC2B8FCB6", "SecurityGroupEgress": [ { "CidrIp": "255.255.255.255/32", @@ -824,6 +938,150 @@ "VpcId": { "Ref": "Vpc8378EB38" } + }, + "DependsOn": [ + "existingalbPolicy6C7AF240", + "existingalb0F60CC48" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W29", + "reason": "CDK created rule that blocks all traffic." + }, + { + "id": "W2", + "reason": "Rule does not apply for ELB." + }, + { + "id": "W9", + "reason": "Rule does not apply for ELB." + } + ] + } + } + }, + "existingalb0F60CC48": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This is a log bucket for an Application Load Balancer" + } + ] + } + } + }, + "existingalbPolicy6C7AF240": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "existingalb0F60CC48" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "existingalb0F60CC48", + "Arn" + ] + }, + "/*" + ] + ] + }, + { + "Fn::GetAtt": [ + "existingalb0F60CC48", + "Arn" + ] + } + ], + "Sid": "HttpsOnly" + }, + { + "Action": [ + "s3:PutObject", + "s3:Abort*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::127311923021:root" + ] + ] + } + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "existingalb0F60CC48", + "Arn" + ] + }, + "/AWSLogs/", + { + "Ref": "AWS::AccountId" + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } } }, "testonefirsttgtg32EF5CD9": { @@ -833,7 +1091,7 @@ { "Id": { "Fn::GetAtt": [ - "existingFunctionEE93E252", + "LambdaFunctionBF21E41F", "Arn" ] } @@ -842,7 +1100,7 @@ "TargetType": "lambda" }, "DependsOn": [ - "existingFunctionInvokeServicePrincipalelasticloadbalancingamazonawscomCF0F2020" + "LambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom842E1595" ] }, "testonelistener5EBC4D40": { @@ -857,14 +1115,24 @@ } ], "LoadBalancerArn": { - "Ref": "newlbF396DAF2" + "Ref": "existingalbalb3A941601" }, "Port": 80, "Protocol": "HTTP" }, "DependsOn": [ "testonefirsttgtg32EF5CD9" - ] + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W56", + "reason": "All integration tests must be HTTP because of certificate limitations." + } + ] + } + } } }, "Parameters": { diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiExistingResources.ts b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiExistingResources.ts index 7766ceb32..b53739aa8 100644 --- a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiExistingResources.ts +++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiExistingResources.ts @@ -17,6 +17,7 @@ import { AlbToLambda, AlbToLambdaProps } from "../lib"; import { generateIntegStackName } from '@aws-solutions-constructs/core'; import * as lambda from '@aws-cdk/aws-lambda'; import * as defaults from '@aws-solutions-constructs/core'; +import { SecurityGroup, CfnSecurityGroup } from "@aws-cdk/aws-ec2"; // Note: All integration tests for alb are for HTTP APIs, as certificates require // validation through DNS and email. This validation is impossible during our integration @@ -38,11 +39,16 @@ const myVpc = defaults.buildVpc(stack, { } }); -const lambdaFunction = new lambda.Function(stack, 'existingFunction', { - code: lambda.Code.fromAsset(`${__dirname}/lambda`), - runtime: lambda.Runtime.NODEJS_12_X, - handler: 'index.handler', - vpc: myVpc, +const testSg = new SecurityGroup(stack, 'lambda-sg', { vpc: myVpc, allowAllOutbound: false}); + +const lambdaFunction = defaults.buildLambdaFunction(stack, { + lambdaFunctionProps: { + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + vpc: myVpc, + securityGroups: [ testSg ] + } }); const loadBalancer = defaults.ObtainAlb(stack, 'existing-alb', myVpc, false, undefined, { @@ -60,7 +66,22 @@ const props: AlbToLambdaProps = { publicApi: false }; -new AlbToLambda(stack, 'test-one', props); +const albToLambda = new AlbToLambda(stack, 'test-one', props); + +defaults.addCfnSuppressRules(albToLambda.listener, [ + { id: 'W56', reason: 'All integration tests must be HTTP because of certificate limitations.' }, +]); + +const newSecurityGroup = albToLambda.loadBalancer.connections.securityGroups[0].node.defaultChild as CfnSecurityGroup; +defaults.addCfnSuppressRules(newSecurityGroup, [ + { id: 'W29', reason: 'CDK created rule that blocks all traffic.'}, + { id: 'W2', reason: 'Rule does not apply for ELB.'}, + { id: 'W9', reason: 'Rule does not apply for ELB.'} +]); + +defaults.addCfnSuppressRules(testSg, [ + { id: 'W29', reason: 'CDK created rule that blocks all traffic.'}, +]); // Synth app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiNewResources.expected.json b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiNewResources.expected.json index e343b8e98..82f2ad6a6 100644 --- a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiNewResources.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiNewResources.expected.json @@ -80,7 +80,25 @@ "DependsOn": [ "testonePolicyE30853FE", "testoneE6ACFBB6" - ] + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W29", + "reason": "CDK created rule that blocks all traffic." + }, + { + "id": "W2", + "reason": "Rule does not apply for ELB." + }, + { + "id": "W9", + "reason": "Rule does not apply for ELB." + } + ] + } + } }, "testoneE6ACFBB6": { "Type": "AWS::S3::Bucket", @@ -455,7 +473,17 @@ }, "DependsOn": [ "testonefirsttgtg32EF5CD9" - ] + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W56", + "reason": "All integration tests must be HTTP because of certificate limitations." + } + ] + } + } }, "Vpc8378EB38": { "Type": "AWS::EC2::VPC", diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiNewResources.ts b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiNewResources.ts index 1fa6e212f..88161e7a7 100644 --- a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiNewResources.ts +++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.privateApiNewResources.ts @@ -16,6 +16,8 @@ import { Aws, App, Stack } from "@aws-cdk/core"; import { AlbToLambda, AlbToLambdaProps } from "../lib"; import { generateIntegStackName } from '@aws-solutions-constructs/core'; import * as lambda from '@aws-cdk/aws-lambda'; +import * as defaults from '@aws-solutions-constructs/core'; +import { CfnSecurityGroup } from "@aws-cdk/aws-ec2"; // Note: All integration tests for alb are for HTTP APIs, as certificates require // validation through DNS and email. This validation is impossible during our integration @@ -39,7 +41,18 @@ const props: AlbToLambdaProps = { }, publicApi: false }; -new AlbToLambda(stack, 'test-one', props); +const albToLambda = new AlbToLambda(stack, 'test-one', props); + +defaults.addCfnSuppressRules(albToLambda.listener, [ + { id: 'W56', reason: 'All integration tests must be HTTP because of certificate limitations.' }, +]); + +const newSecurityGroup = albToLambda.loadBalancer.connections.securityGroups[0].node.defaultChild as CfnSecurityGroup; +defaults.addCfnSuppressRules(newSecurityGroup, [ + { id: 'W29', reason: 'CDK created rule that blocks all traffic.'}, + { id: 'W2', reason: 'Rule does not apply for ELB.'}, + { id: 'W9', reason: 'Rule does not apply for ELB.'} +]); // Synth app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publidApiExistingResources.expected.json b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiExistingResources.expected.json similarity index 66% rename from source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publidApiExistingResources.expected.json rename to source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiExistingResources.expected.json index 79fc34071..22e00434e 100644 --- a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publidApiExistingResources.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiExistingResources.expected.json @@ -11,7 +11,7 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc" + "Value": "publicApiExistingResources/Vpc" } ] } @@ -36,7 +36,7 @@ }, { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PublicSubnet1" + "Value": "publicApiExistingResources/Vpc/PublicSubnet1" } ] }, @@ -60,7 +60,7 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PublicSubnet1" + "Value": "publicApiExistingResources/Vpc/PublicSubnet1" } ] } @@ -98,7 +98,7 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PublicSubnet1" + "Value": "publicApiExistingResources/Vpc/PublicSubnet1" } ] } @@ -118,7 +118,7 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PublicSubnet1" + "Value": "publicApiExistingResources/Vpc/PublicSubnet1" } ] } @@ -143,7 +143,7 @@ }, { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PublicSubnet2" + "Value": "publicApiExistingResources/Vpc/PublicSubnet2" } ] }, @@ -167,7 +167,7 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PublicSubnet2" + "Value": "publicApiExistingResources/Vpc/PublicSubnet2" } ] } @@ -205,7 +205,7 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PublicSubnet2" + "Value": "publicApiExistingResources/Vpc/PublicSubnet2" } ] } @@ -225,7 +225,7 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PublicSubnet2" + "Value": "publicApiExistingResources/Vpc/PublicSubnet2" } ] } @@ -250,7 +250,7 @@ }, { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PublicSubnet3" + "Value": "publicApiExistingResources/Vpc/PublicSubnet3" } ] }, @@ -274,7 +274,7 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PublicSubnet3" + "Value": "publicApiExistingResources/Vpc/PublicSubnet3" } ] } @@ -312,7 +312,7 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PublicSubnet3" + "Value": "publicApiExistingResources/Vpc/PublicSubnet3" } ] } @@ -332,7 +332,7 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PublicSubnet3" + "Value": "publicApiExistingResources/Vpc/PublicSubnet3" } ] } @@ -357,7 +357,7 @@ }, { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PrivateSubnet1" + "Value": "publicApiExistingResources/Vpc/PrivateSubnet1" } ] } @@ -371,7 +371,7 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PrivateSubnet1" + "Value": "publicApiExistingResources/Vpc/PrivateSubnet1" } ] } @@ -419,7 +419,7 @@ }, { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PrivateSubnet2" + "Value": "publicApiExistingResources/Vpc/PrivateSubnet2" } ] } @@ -433,7 +433,7 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PrivateSubnet2" + "Value": "publicApiExistingResources/Vpc/PrivateSubnet2" } ] } @@ -481,7 +481,7 @@ }, { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PrivateSubnet3" + "Value": "publicApiExistingResources/Vpc/PrivateSubnet3" } ] } @@ -495,7 +495,7 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc/PrivateSubnet3" + "Value": "publicApiExistingResources/Vpc/PrivateSubnet3" } ] } @@ -529,7 +529,7 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc" + "Value": "publicApiExistingResources/Vpc" } ] } @@ -563,7 +563,7 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc" + "Value": "publicApiExistingResources/Vpc" } ] } @@ -647,12 +647,40 @@ "Tags": [ { "Key": "Name", - "Value": "publidApiExistingResources/Vpc" + "Value": "publicApiExistingResources/Vpc" } ] } }, - "existingFunctionServiceRoleC8C38BE5": { + "lambdasg93781054": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "publicApiExistingResources/lambda-sg", + "SecurityGroupEgress": [ + { + "CidrIp": "255.255.255.255/32", + "Description": "Disallow all traffic", + "FromPort": 252, + "IpProtocol": "icmp", + "ToPort": 86 + } + ], + "VpcId": { + "Ref": "Vpc8378EB38" + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W29", + "reason": "CDK created rule that blocks all traffic." + } + ] + } + } + }, + "LambdaFunctionServiceRole0C4CDE0B": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -667,51 +695,92 @@ ], "Version": "2012-10-17" }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - }, + "Policies": [ { - "Fn::Join": [ - "", - [ - "arn:", + "PolicyDocument": { + "Statement": [ { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" - ] - ] + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRolePolicy" } ] } }, - "existingFunctionSecurityGroupFE7872B0": { - "Type": "AWS::EC2::SecurityGroup", + "LambdaFunctionServiceRoleDefaultPolicy126C8897": { + "Type": "AWS::IAM::Policy", "Properties": { - "GroupDescription": "Automatic security group for Lambda Function publidApiExistingResourcesexistingFunction323CF2B0", - "SecurityGroupEgress": [ + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:AssignPrivateIpAddresses", + "ec2:UnassignPrivateIpAddresses" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRoleDefaultPolicy126C8897", + "Roles": [ { - "CidrIp": "0.0.0.0/0", - "Description": "Allow all outbound traffic by default", - "IpProtocol": "-1" + "Ref": "LambdaFunctionServiceRole0C4CDE0B" } - ], - "VpcId": { - "Ref": "Vpc8378EB38" + ] + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W12", + "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC." + } + ] } } }, - "existingFunctionEE93E252": { + "LambdaFunctionBF21E41F": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { @@ -722,17 +791,25 @@ }, "Role": { "Fn::GetAtt": [ - "existingFunctionServiceRoleC8C38BE5", + "LambdaFunctionServiceRole0C4CDE0B", "Arn" ] }, + "Environment": { + "Variables": { + "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1" + } + }, "Handler": "index.handler", "Runtime": "nodejs12.x", + "TracingConfig": { + "Mode": "Active" + }, "VpcConfig": { "SecurityGroupIds": [ { "Fn::GetAtt": [ - "existingFunctionSecurityGroupFE7872B0", + "lambdasg93781054", "GroupId" ] } @@ -751,63 +828,95 @@ } }, "DependsOn": [ - "existingFunctionServiceRoleC8C38BE5" - ] + "LambdaFunctionServiceRoleDefaultPolicy126C8897", + "LambdaFunctionServiceRole0C4CDE0B" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions." + }, + { + "id": "W89", + "reason": "This is not a rule for the general case, just for specific use cases/industries" + }, + { + "id": "W92", + "reason": "Impossible for us to define the correct concurrency for clients" + } + ] + } + } }, - "existingFunctionInvokeServicePrincipalelasticloadbalancingamazonawscomCF0F2020": { + "LambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom842E1595": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { "Fn::GetAtt": [ - "existingFunctionEE93E252", + "LambdaFunctionBF21E41F", "Arn" ] }, "Principal": "elasticloadbalancing.amazonaws.com" } }, - "newlbF396DAF2": { + "existingalbalb3A941601": { "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", "Properties": { "LoadBalancerAttributes": [ { "Key": "deletion_protection.enabled", "Value": "false" + }, + { + "Key": "access_logs.s3.enabled", + "Value": "true" + }, + { + "Key": "access_logs.s3.bucket", + "Value": { + "Ref": "existingalb0F60CC48" + } + }, + { + "Key": "access_logs.s3.prefix", + "Value": "" } ], - "Scheme": "internet-facing", + "Scheme": "internal", "SecurityGroups": [ { "Fn::GetAtt": [ - "newlbSecurityGroup04195C74", + "existingalbalbSecurityGroupC8DD2920", "GroupId" ] } ], "Subnets": [ { - "Ref": "VpcPublicSubnet1Subnet5C2D37C4" + "Ref": "VpcPrivateSubnet1Subnet536B997A" }, { - "Ref": "VpcPublicSubnet2Subnet691E08A3" + "Ref": "VpcPrivateSubnet2Subnet3788AAA1" }, { - "Ref": "VpcPublicSubnet3SubnetBE12F0B6" + "Ref": "VpcPrivateSubnet3SubnetF258B56E" } ], "Type": "application" }, "DependsOn": [ - "VpcPublicSubnet1DefaultRoute3DA9E72A", - "VpcPublicSubnet2DefaultRoute97F91067", - "VpcPublicSubnet3DefaultRoute4697774F" + "existingalbPolicy6C7AF240", + "existingalb0F60CC48" ] }, - "newlbSecurityGroup04195C74": { + "existingalbalbSecurityGroupC8DD2920": { "Type": "AWS::EC2::SecurityGroup", "Properties": { - "GroupDescription": "Automatically created Security Group for ELB publidApiExistingResourcesnewlbEF59711B", + "GroupDescription": "Automatically created Security Group for ELB publicApiExistingResourcesexistingalbalb2415E979", "SecurityGroupEgress": [ { "CidrIp": "255.255.255.255/32", @@ -829,6 +938,150 @@ "VpcId": { "Ref": "Vpc8378EB38" } + }, + "DependsOn": [ + "existingalbPolicy6C7AF240", + "existingalb0F60CC48" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W29", + "reason": "CDK created rule that blocks all traffic." + }, + { + "id": "W2", + "reason": "Rule does not apply for ELB." + }, + { + "id": "W9", + "reason": "Rule does not apply for ELB." + } + ] + } + } + }, + "existingalb0F60CC48": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + }, + "VersioningConfiguration": { + "Status": "Enabled" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This is a log bucket for an Application Load Balancer" + } + ] + } + } + }, + "existingalbPolicy6C7AF240": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "existingalb0F60CC48" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "existingalb0F60CC48", + "Arn" + ] + }, + "/*" + ] + ] + }, + { + "Fn::GetAtt": [ + "existingalb0F60CC48", + "Arn" + ] + } + ], + "Sid": "HttpsOnly" + }, + { + "Action": [ + "s3:PutObject", + "s3:Abort*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::127311923021:root" + ] + ] + } + }, + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "existingalb0F60CC48", + "Arn" + ] + }, + "/AWSLogs/", + { + "Ref": "AWS::AccountId" + }, + "/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } } }, "testonefirsttgtg32EF5CD9": { @@ -838,7 +1091,7 @@ { "Id": { "Fn::GetAtt": [ - "existingFunctionEE93E252", + "LambdaFunctionBF21E41F", "Arn" ] } @@ -847,7 +1100,7 @@ "TargetType": "lambda" }, "DependsOn": [ - "existingFunctionInvokeServicePrincipalelasticloadbalancingamazonawscomCF0F2020" + "LambdaFunctionInvokeServicePrincipalelasticloadbalancingamazonawscom842E1595" ] }, "testonelistener5EBC4D40": { @@ -862,14 +1115,24 @@ } ], "LoadBalancerArn": { - "Ref": "newlbF396DAF2" + "Ref": "existingalbalb3A941601" }, "Port": 80, "Protocol": "HTTP" }, "DependsOn": [ "testonefirsttgtg32EF5CD9" - ] + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W56", + "reason": "All integration tests must be HTTP because of certificate limitations." + } + ] + } + } } }, "Parameters": { diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publidApiExistingResources.ts b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiExistingResources.ts similarity index 60% rename from source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publidApiExistingResources.ts rename to source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiExistingResources.ts index 6f61213e5..26c69465c 100644 --- a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publidApiExistingResources.ts +++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiExistingResources.ts @@ -16,8 +16,8 @@ import { Aws, App, Stack } from "@aws-cdk/core"; import { AlbToLambda, AlbToLambdaProps } from "../lib"; import { generateIntegStackName } from '@aws-solutions-constructs/core'; import * as lambda from '@aws-cdk/aws-lambda'; -import * as elb from '@aws-cdk/aws-elasticloadbalancingv2'; import * as defaults from '@aws-solutions-constructs/core'; +import { SecurityGroup, CfnSecurityGroup } from "@aws-cdk/aws-ec2"; // Note: All integration tests for alb are for HTTP APIs, as certificates require // validation through DNS and email. This validation is impossible during our integration @@ -39,15 +39,20 @@ const myVpc = defaults.buildVpc(stack, { } }); -const lambdaFunction = new lambda.Function(stack, 'existingFunction', { - code: lambda.Code.fromAsset(`${__dirname}/lambda`), - runtime: lambda.Runtime.NODEJS_12_X, - handler: 'index.handler', - vpc: myVpc, +const testSg = new SecurityGroup(stack, 'lambda-sg', { vpc: myVpc, allowAllOutbound: false}); + +const lambdaFunction = defaults.buildLambdaFunction(stack, { + lambdaFunctionProps: { + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + vpc: myVpc, + securityGroups: [ testSg ] + } }); -const loadBalancer = new elb.ApplicationLoadBalancer(stack, 'new-lb', { - internetFacing: true, +const loadBalancer = defaults.ObtainAlb(stack, 'existing-alb', myVpc, false, undefined, { + internetFacing: false, vpc: myVpc }); @@ -60,7 +65,22 @@ const props: AlbToLambdaProps = { }, publicApi: true }; -new AlbToLambda(stack, 'test-one', props); +const albToLambda = new AlbToLambda(stack, 'test-one', props); + +defaults.addCfnSuppressRules(albToLambda.listener, [ + { id: 'W56', reason: 'All integration tests must be HTTP because of certificate limitations.' }, +]); + +const newSecurityGroup = albToLambda.loadBalancer.connections.securityGroups[0].node.defaultChild as CfnSecurityGroup; +defaults.addCfnSuppressRules(newSecurityGroup, [ + { id: 'W29', reason: 'CDK created rule that blocks all traffic.'}, + { id: 'W2', reason: 'Rule does not apply for ELB.'}, + { id: 'W9', reason: 'Rule does not apply for ELB.'} +]); + +defaults.addCfnSuppressRules(testSg, [ + { id: 'W29', reason: 'CDK created rule that blocks all traffic.'}, +]); // Synth app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiNewResources.expected.json b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiNewResources.expected.json index 8e3f1ba78..ea359039f 100644 --- a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiNewResources.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiNewResources.expected.json @@ -83,7 +83,25 @@ "DependsOn": [ "testonePolicyE30853FE", "testoneE6ACFBB6" - ] + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W29", + "reason": "CDK created rule that blocks all traffic." + }, + { + "id": "W2", + "reason": "Rule does not apply for ELB." + }, + { + "id": "W9", + "reason": "Rule does not apply for ELB." + } + ] + } + } }, "testoneE6ACFBB6": { "Type": "AWS::S3::Bucket", @@ -458,7 +476,17 @@ }, "DependsOn": [ "testonefirsttgtg32EF5CD9" - ] + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W56", + "reason": "All integration tests must be HTTP because of certificate limitations." + } + ] + } + } }, "Vpc8378EB38": { "Type": "AWS::EC2::VPC", diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiNewResources.ts b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiNewResources.ts index 5ffcb618f..bf28c6de7 100644 --- a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiNewResources.ts +++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.publicApiNewResources.ts @@ -16,6 +16,8 @@ import { Aws, App, Stack } from "@aws-cdk/core"; import { AlbToLambda, AlbToLambdaProps } from "../lib"; import { generateIntegStackName } from '@aws-solutions-constructs/core'; import * as lambda from '@aws-cdk/aws-lambda'; +import * as defaults from '@aws-solutions-constructs/core'; +import { CfnSecurityGroup } from "@aws-cdk/aws-ec2"; // Note: All integration tests for alb are for HTTP APIs, as certificates require // validation through DNS and email. This validation is impossible during our integration @@ -39,7 +41,18 @@ const props: AlbToLambdaProps = { }, publicApi: true }; -new AlbToLambda(stack, 'test-one', props); +const albToLambda = new AlbToLambda(stack, 'test-one', props); + +defaults.addCfnSuppressRules(albToLambda.listener, [ + { id: 'W56', reason: 'All integration tests must be HTTP because of certificate limitations.' }, +]); + +const newSecurityGroup = albToLambda.loadBalancer.connections.securityGroups[0].node.defaultChild as CfnSecurityGroup; +defaults.addCfnSuppressRules(newSecurityGroup, [ + { id: 'W29', reason: 'CDK created rule that blocks all traffic.'}, + { id: 'W2', reason: 'Rule does not apply for ELB.'}, + { id: 'W9', reason: 'Rule does not apply for ELB.'} +]); // Synth app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.twoTargets.expected.json b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.twoTargets.expected.json index 31171d7e2..c9a8d4fee 100644 --- a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.twoTargets.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.twoTargets.expected.json @@ -83,7 +83,25 @@ "DependsOn": [ "testonePolicyE30853FE", "testoneE6ACFBB6" - ] + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W29", + "reason": "CDK created rule that blocks all traffic." + }, + { + "id": "W2", + "reason": "Rule does not apply for ELB." + }, + { + "id": "W9", + "reason": "Rule does not apply for ELB." + } + ] + } + } }, "testoneE6ACFBB6": { "Type": "AWS::S3::Bucket", @@ -458,7 +476,17 @@ }, "DependsOn": [ "testonefirsttgtg32EF5CD9" - ] + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W56", + "reason": "All integration tests must be HTTP because of certificate limitations." + } + ] + } + } }, "testonelistenertesttwotargetsRuleE0E52701": { "Type": "AWS::ElasticLoadBalancingV2::ListenerRule", diff --git a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.twoTargets.ts b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.twoTargets.ts index c868f746a..7b9d9e5da 100644 --- a/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.twoTargets.ts +++ b/source/patterns/@aws-solutions-constructs/aws-alb-lambda/test/integ.twoTargets.ts @@ -17,6 +17,8 @@ import { AlbToLambda, AlbToLambdaProps } from "../lib"; import { generateIntegStackName } from '@aws-solutions-constructs/core'; import * as lambda from '@aws-cdk/aws-lambda'; import * as elb from '@aws-cdk/aws-elasticloadbalancingv2'; +import * as defaults from '@aws-solutions-constructs/core'; +import { CfnSecurityGroup } from "@aws-cdk/aws-ec2"; // Note: All integration tests for alb are for HTTP APIs, as certificates require // validation through DNS and email. This validation is impossible during our integration @@ -58,5 +60,16 @@ const secondProps: AlbToLambdaProps = { }; new AlbToLambda(stack, 'test-two', secondProps); +defaults.addCfnSuppressRules(firstConstruct.listener, [ + { id: 'W56', reason: 'All integration tests must be HTTP because of certificate limitations.' }, +]); + +const newSecurityGroup = firstConstruct.loadBalancer.connections.securityGroups[0].node.defaultChild as CfnSecurityGroup; +defaults.addCfnSuppressRules(newSecurityGroup, [ + { id: 'W29', reason: 'CDK created rule that blocks all traffic.'}, + { id: 'W2', reason: 'Rule does not apply for ELB.'}, + { id: 'W9', reason: 'Rule does not apply for ELB.'} +]); + // Synth app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts index 3eee31add..7641bf572 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts @@ -211,7 +211,7 @@ test('test cloudfront disable cloudfront logging', () => { test('test cloudfront with custom domain names', () => { const stack = new cdk.Stack(); - const certificate = acm.Certificate.fromCertificateArn(stack, 'Cert', 'arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012'); + const certificate = acm.Certificate.fromCertificateArn(stack, 'Cert', 'arn:aws:acm:us-east-1:123456789012:certificate/11112222-3333-1234-1234-123456789012'); const props: CloudFrontToS3Props = { cloudFrontDistributionProps: { diff --git a/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts b/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts index 654ebd617..e5010b7a2 100644 --- a/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts +++ b/source/patterns/@aws-solutions-constructs/core/test/cloudfront-distribution-s3-helper.test.ts @@ -484,7 +484,7 @@ test('test override cloudfront replace custom lambda@edge', () => { test('test cloudfront override cloudfront custom domain names ', () => { const stack = new Stack(); const [sourceBucket] = buildS3Bucket(stack, {}); - const certificate = acm.Certificate.fromCertificateArn(stack, 'Cert', 'arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012'); + const certificate = acm.Certificate.fromCertificateArn(stack, 'Cert', 'arn:aws:acm:us-east-1:123456789012:certificate/11112222-3333-1234-1234-123456789012'); const myprops = { domainNames: ['mydomains'],