Why does CDK ALB construct create an ICMP egress disallow rule? - amazon-web-services

AWS CDK lib version: 2.62.2
When I let the CDK implicitly create the SecurityGroup as below:
this.alb = new ApplicationLoadBalancer(this, 'Alb', {
loadBalancerName: "Raido",
vpc: props.vpc,
internetFacing: true,
vpcSubnets: {
subnetType: SubnetType.PUBLIC,
onePerAz: true,
},
// securityGroup: props.securityGroup,
idleTimeout: Duration.seconds(60),
dropInvalidHeaderFields: false,
desyncMitigationMode: DesyncMitigationMode.DEFENSIVE,
});
It creates an ICMP egress rule blocking port 86, like this:
egress rule:
Type: Custom ICMP - IPv4
Protocol: 252
Port: 86
Destination: 255.255.255.255/32
Description: Disallow all traffic
When I create my own SecurityGroup manually, it doesn't have the egress rule.
Why does the CDK create this egress rule, and should I add a similar rule of my own?
I'm reluctant to just copy/paste the rule without knowing what it's for.

Apparently, this rule is added when allowAllOutbound option is false, which is the default for the ApplicationLoadBalancer construct.
My takeaway: no point in setting the bogus rule on your ALB. If you want to allowAllOutbound, then do that; otherwise set your egress rules appropriate to your situation.
Apparenlty the 255.255.255.255/32 is intended to match no traffic because:
No machine can ever actually have the 255.255.255.255 IP address

Related

CodeBuild in private subnets have connection issue to S3

I added CodeBuild in my VPC's private subnets, because I want to give it access to RDS cluster in the private subnets.
However, I got below error in the CodeBuild:
CLIENT_ERROR: RequestError: send request failed caused by: Get "https://abcdatabase-schema-abcdatabaseschemap-jatjfe01aqps.s3.amazonaws.com/abcDatabase-Sc/Artifact_S/LrJBqSR.zip": dial tcp 52.217.128.121:443: i/o timeout for primary source and source version arn:aws:s3:::abcdatabase-schema-abcdatabaseschemap-jatjfe01aqps/abcDatabase-Sc/Artifact_S/LrJBqSR.zip
It is a TCP 443 timeout.
It looks like the CodeBuild is trying to download the pipeline artifact from S3 bucket but have a connection timeout, which means that there is a network connection issue between my CodeBuild and S3. However, I added S3 VPC endpoint in my VPC, that is suppose to provide the network connection. https://docs.aws.amazon.com/codebuild/latest/userguide/use-vpc-endpoints-with-codebuild.html.
According to CodeBuild unable to fetch an S3 object despite having Administrator Access, as long as I have s3 VPC endpoint set up then I don't need NAT.
You can see the code below to see how I added the S3 VPC endpoint.
Code
VPC stack
private readonly coreVpc: EC2.Vpc;
constructor(scope: CDK.App, id: string, props?: VpcStackStackProps) {
super(scope, id, props);
const vpcName: string = "CoreVpc";
// Create VPC
this.coreVpc = new EC2.Vpc(this, "CoreVpc", {
vpcName: vpcName,
cidr: "10.0.0.0/16",
enableDnsHostnames: true,
enableDnsSupport: true,
maxAzs: 3, // 3 availability zones
// Each zone will have one public subnet and one private subnet.
subnetConfiguration: [
{
cidrMask: 19,
name: "PublicSubnet",
subnetType: EC2.SubnetType.PUBLIC,
},
{
cidrMask: 19,
name: "PrivateSubnet",
subnetType: EC2.SubnetType.PRIVATE_ISOLATED,
},
],
});
// Create security group for the VPC
const vpcEndpointSecurityGroup = new EC2.SecurityGroup(
this,
`${vpcName}-VPCEndpointSecurityGroup`,
{
securityGroupName: `${vpcName}-VPCEndpointSecurityGroup`,
vpc: this.coreVpc,
description: "Security group for granting AWS services access to the CoreVpc",
allowAllOutbound: false,
}
);
vpcEndpointSecurityGroup.addIngressRule(
EC2.Peer.ipv4(this.coreVpc.vpcCidrBlock),
EC2.Port.tcp(443),
"Allow HTTPS ingress traffic"
);
vpcEndpointSecurityGroup.addEgressRule(
EC2.Peer.ipv4(this.coreVpc.vpcCidrBlock),
EC2.Port.tcp(443),
"Allow HTTPS egress traffic"
);
const privateSubnets = this.coreVpc.selectSubnets(
{
subnetType: EC2.SubnetType.PRIVATE_ISOLATED
}
);
// Grant AWS CodeBuild service access to the VPC's private subnets.
new EC2.InterfaceVpcEndpoint(
this, 'CodeBuildInterfaceVpcEndpoint', {
service: EC2.InterfaceVpcEndpointAwsService.CODEBUILD,
vpc: this.coreVpc,
privateDnsEnabled: true,
securityGroups: [vpcEndpointSecurityGroup],
subnets: privateSubnets
}
);
// Grant VPC access to S3 service.
new EC2.GatewayVpcEndpoint(
this, 'S3InterfaceVpcEndpoint', {
service: EC2.GatewayVpcEndpointAwsService.S3,
vpc: this.coreVpc,
}
);
}
}
CodeBuild stack
export class CodeBuildStack extends CDK.Stack {
constructor(scope: Construct, id: string, props: CodeBuildStackProps) {
super(scope, id, props);
const buildspecFile = FS.readFileSync("./config/buildspec.yml", "utf-8");
const buildspecFileYaml = YAML.parse(buildspecFile, {
prettyErrors: true,
});
// Grant write permissions to the DeploymentRole to the artifact S3 bucket.
const deploymentRoleArn: string = `arn:aws:iam::${props.env?.account}:role/${props.pipelineName}-DeploymentRole`;
const deploymentRole = IAM.Role.fromRoleArn(
this,
`CodeBuild${props.pipelineStageInfo.stageName}DeploymentRoleConstructID`,
deploymentRoleArn,
{
mutable: false,
// Causes CDK to update the resource policy where required, instead of the Role
addGrantsToResources: true,
}
);
const coreVpc: EC2.IVpc = EC2.Vpc.fromLookup(
this,
`${props.pipelineStageInfo.stageName}VpcLookupId`,
{
vpcName: "CoreVpc",
}
);
const securityGroupForVpc: EC2.ISecurityGroup =
EC2.SecurityGroup.fromLookupByName(
this,
"SecurityGroupLookupForVpcEndpoint",
"CoreVpc-VPCEndpointSecurityGroup",
coreVpc
);
new CodeBuild.Project(
this,
`${props.pipelineName}-${props.pipelineStageInfo.stageName}-ColdBuild`,
{
projectName: `${props.pipelineName}-${props.pipelineStageInfo.stageName}-ColdBuild`,
environment: {
buildImage: CodeBuild.LinuxBuildImage.STANDARD_5_0,
},
buildSpec: CodeBuild.BuildSpec.fromObjectToYaml(buildspecFileYaml),
vpc: coreVpc,
securityGroups: [securityGroupForVpc],
role: deploymentRole,
}
);
}
}
The issue in my code is this part. The security group add ingress rule saying that allow TCP egress traffic from my VPC's CIDR block. But CodeBuild is not running inside my VPC's CIDR Block.
vpcEndpointSecurityGroup.addEgressRule(
EC2.Peer.ipv4(this.coreVpc.vpcCidrBlock),
EC2.Port.tcp(443),
"Allow TCP egress traffic"
);
It works by changing to :
vpcEndpointSecurityGroup.addEgressRule(
EC2.Peer.anyIpv4(),
EC2.Port.allTcp(),
"Allow TCP egress traffic"
);
There are other ways to make it work:
Alternative 1:
Changed based on #gshpychka's comments also works.
Alternative 2:
Make allowAllOutbound true. In this way you don't need to specify any egress rule any more.
If this is set to true, there will only be a single egress rule which allows all outbound traffic. If this is set to false, no outbound traffic will be allowed by default and all egress traffic must be explicitly authorized.
const vpcEndpointSecurityGroup = new EC2.SecurityGroup(
this,
`${vpcName}-VPCEndpointSecurityGroup`,
{
securityGroupName: `${vpcName}-VPCEndpointSecurityGroup`,
vpc: this.coreVpc,
description: "Security group for granting AWS services access to the CoreVpc",
allowAllOutbound: true,
}
);

Adding an ApplicationListenerRule to an existing ApplicationListener gives error 'A listener already exists on this port for this load balancer'

I want to add a rule to an existing load balancer listener which is listening on PORT:80. I'm also creating a new target group to attach to the listener rule action.
in CDK i used fromLookup and grabbed the listener from ARN
const appListener = elbv2.ApplicationListener.fromLookup(this, `ALBListener-${props.stage}`, { listenerArn });
const applicationListenerRule = new elbv2.ApplicationListenerRule(this, `BlablaSyncRule-${props.stage}`, {
listener: appListener,
priority: 1000, //do not hardcode
conditions: [elbv2.ListenerCondition.pathPatterns(['/socket.io*'])],
action: elbv2.ListenerAction.forward([targetGroup])
});
when i do cdk synth i can see this included in the generated Cloudformation
ALBPublicListener9C613A95:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
Properties:
DefaultActions:
- TargetGroupArn:
Ref: ALBPublicListenerECSGroup7E4FFE32
Type: forward
LoadBalancerArn: >-
arn:aws:elasticloadbalancing:eu-central-1....
Port: 80
Protocol: HTTP
Metadata:
'aws:cdk:path': SocketFargateStack/ALB/PublicListener/Resource
When I try to deploy I get the error A listener already exists on this port for this load balancer so is it trying to create a new listener on PORT 80. If so why. Is there a way to add a rule to an existing listener using CDK

Using existing loadbalancer for adding targetgroup

I want to make the targetGroup by cdk and use the ALB already existed.
This error happens.
Error: .listeners can only be accessed if the class was constructed as an owned, not looked up, load balancer
at LookedUpApplicationLoadBalancer.get listeners [as listeners]
I can't access the listeners of ALB
What I want to do is use one load balancer for two ECS and two domain.
www.exampleA.com -> port 80 -> ALB -> fargate A
www.exampleB.com -> port 80 -> ALB -> fargate B
These are my code below.
const lb = elb.ApplicationLoadBalancer.fromLookup(this, 'ALB', {
loadBalancerArn: 'arn:aws:elasticloadbalancing:ap-northeast-1:678100228232:loadbalancer/app/app-load-balancer/1a97159fcaf4d6c0',
});
const listener = lb.listeners[0];
const targetGroup = listener.addTargets("ECS", {
protocol: elb.ApplicationProtocol.HTTP,
port: 80,
targets: [ecsAdminService]
});
targetGroup.configureHealthCheck({
path: "/",
port: "8080"
})
Or if it is impossible, I want to make targetgroup without ALB
(then I can attach targetgroup to ALB manually)
So, I tried this
const targetGroup = new elb.ApplicationTargetGroup(this,"ECS", {
protocol: elb.ApplicationProtocol.HTTP,
port: 80,
targets: [ecsAdminService],
vpc: cluster.vpc,
});
targetGroup.configureHealthCheck({
path: "/",
port: "8080"
})
However this error comes
"Invalid request provided: UpdateService error: The target group with targetGroupArn arn:aws:elasticloadbalancing:ap-northeast-1:678100228133:targetgroup/CdkTr-ECSD2-S1ROICFY9661/f1f3e3b280c2a008 does not have an associated load balancer
I can use existing ALB like this below
Actually, what I need is only securityGroup of ALB and listener.
const securityGroup = ec2.SecurityGroup.fromSecurityGroupId(this, "MyAlbSecGroup", "sg-0ea7a62badcc673a3")
const listenerArn = "arn:aws:elasticloadbalancing:ap-northeast-1:67810022242:listener/app/my-alb-id-listener/1a97159fcaf4d6c0/09a32815415beae6";
const existingListener = elb.ApplicationListener.fromApplicationListenerAttributes(this, "SharedListener", {
listenerArn,
securityGroup
});
const targetGroup = new elb.ApplicationTargetGroup(this,"ECS", {
port: 80,
targets: [ecsAdminService],
vpc: cluster.vpc,
});
existingListener.addTargetGroups("tg",{
priority:1,
conditions:[
elb.ListenerCondition.hostHeaders(['my.example.com'])
],
targetGroups:[targetGroup]
})

AWS-CDK ECS Fargate LoadBalancer listening on port 80 with target group mapping to container port

I've been trying to build a simple ECS Fargate infrastructure using the CDK to learn ECS and its components. The web server exposes port 8081, service SG ingress rule allows all TCP from ALB SG, ALB SG allows connection on PORT 80 - later changed to all TCP for testing. ALB has a listener on port 80 – later also on port 8081 – which forwards traffic to the Target Group on HTTP:8081, fargate tasks are automatically registered, health checks are passing.
Everything seems to be set up the right way, however, when going to [alb-dns].com I get nothing – not even a 504 – DNS just cannot be found. But when I go with [alb-dns].com:8081 it serves me the "hello world" from the webserver. This works regardless of whether my Target Group is on HTTP:8081 or HTTP:80.
I tried an old github/stackoverflow solution of passing a listener as port mapping to a container, but that doesn't work anymore – type mismatch.
What am I missing here?
Code:
this.cluster = new Cluster(this, 'exanubes-cluster', {
vpc: props.vpc,
clusterName: 'exanubes-cluster',
containerInsights: true,
enableFargateCapacityProviders: true,
})
const albSg = new SecurityGroup(this, 'SecurityGroupLoadBalancer', {
vpc: props.vpc,
allowAllOutbound: true
})
albSg.addIngressRule(Peer.anyIpv4(), Port.allTcp())
const alb = new ApplicationLoadBalancer(this, 'alb', {
vpc: props.vpc,
loadBalancerName: 'exanubes-ecs-application-LB',
internetFacing: true,
securityGroup: albSg,
http2Enabled: false,
deletionProtection: false
})
const listener = alb.addListener('http listener', {
port: 80,
open: true
})
const targetGroup = listener.addTargets('tcp-listener-target', {
targetGroupName: 'tcp-target-ecs-service',
protocol: ApplicationProtocol.HTTP,
protocolVersion: ApplicationProtocolVersion.HTTP1,
port: CONTAINER_PORT
})
const taskDefinition = new FargateTaskDefinition(this, 'fargate-task-definition');
taskDefinition.addContainer('web-server', {
image: EcrImage.fromEcrRepository(props.repository),
}).addPortMappings({
containerPort: CONTAINER_PORT
})
const securityGroup = new SecurityGroup(this, 'http-sg', {
vpc: props.vpc,
})
securityGroup.addIngressRule(Peer.securityGroupId(albSg.securityGroupId), Port.allTcp(), 'Allow inbound connections from ALB')
const fargateService = new FargateService(this, 'fargate-service', {
cluster: this.cluster,
assignPublicIp: true,
taskDefinition,
capacityProviderStrategies: [
{
capacityProvider: "FARGATE_SPOT",
weight: 0,
},
{
capacityProvider: "FARGATE",
weight: 1
}
],
securityGroups: [securityGroup],
})
targetGroup.addTarget(fargateService)
PS: I know of ApplicationLoadBalancedFargateService but I wanted to build it myself.
I think this example in the source code .../aws_cdk/aws_ecs/__init__.py should help
Example::
# cluster: ecs.Cluster
# task_definition: ecs.TaskDefinition
# vpc: ec2.Vpc
service = ecs.FargateService(self, "Service", cluster=cluster, task_definition=task_definition)
lb = elbv2.ApplicationLoadBalancer(self, "LB", vpc=vpc, internet_facing=True)
listener = lb.add_listener("Listener", port=80)
service.register_load_balancer_targets(
container_name="web",
container_port=80,
new_target_group_id="ECS",
listener=ecs.ListenerConfig.application_listener(listener,
protocol=elbv2.ApplicationProtocol.HTTPS
)
)
Edit: the above did not work for me, but this did
listener.add_targets('tcp-target-group',
protocol=ApplicationProtocol.HTTP,
target_group_name="my-target",
targets=[service.load_balancer_target(
container_name=container.container_name,
container_port=CONTAINER_PORT
)],
)

How to add source_security_group_id in Security Group?

Hi I am trying to create security group in AWS CDK. when I try to create Ingress rule, I want to specify source_security_group_id. I created security group as below.
mws_vpc_sg = ec2.SecurityGroup(self,"securitygroupname",
description="EC2 Services Security Group",
security_group_name="securitygroupname",
vpc=vpc
);
Then I want to add ingress rules as below.
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: "80"
ToPort: "80"
SourceSecurityGroupId: !Ref MerchWebServicesLoadBalancerSecurityGroup
Can someone help me to write same template using aws cdk?
According to the documentation, there's a method belonging to ec2.SecurityGroup called add_ingress_rule which takes a "Peer" and a "Connection" as arguments. These being another Security Group and a Port
So you can try something like this
my_sg = ec2.SecurityGroup(self, "securitygroupone",
description="EC2 Services Security Group",
security_group_name="securitygroupone",
vpc=vpc
)
my_sg_two = ec2.SecurityGroup(self, "securitygrouptwo",
description="EC2 Services Security Group with Ingress from securitygroupone",
security_group_name="securitygrouptwo",
vpc=vpc
)
my_sg_two.add_ingress_rule(my_sg, 8080)
https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.aws_ec2/SecurityGroup.html#aws_cdk.aws_ec2.SecurityGroup.add_ingress_rule