From 04101819850d1e733e20bff52d3b2ce3c39d199a Mon Sep 17 00:00:00 2001 From: Parker Scanlon <69879391+scanlonp@users.noreply.github.com> Date: Mon, 4 Dec 2023 12:52:31 -0800 Subject: [PATCH] test one ipv6 address --- .../{ => test}/integ.vpc-dual-stack.ts | 6 +- packages/aws-cdk-lib/aws-ec2/lib/index.ts | 1 + .../aws-cdk-lib/aws-ec2/lib/vpc-dual-stack.ts | 101 +++++++++++++++++- 3 files changed, 103 insertions(+), 5 deletions(-) rename packages/@aws-cdk-testing/framework-integ/test/aws-ec2/{ => test}/integ.vpc-dual-stack.ts (58%) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/integ.vpc-dual-stack.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.vpc-dual-stack.ts similarity index 58% rename from packages/@aws-cdk-testing/framework-integ/test/aws-ec2/integ.vpc-dual-stack.ts rename to packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.vpc-dual-stack.ts index 6147d1c9d2ba8..0472659fceff2 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/integ.vpc-dual-stack.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.vpc-dual-stack.ts @@ -3,9 +3,11 @@ import * as ec2 from 'aws-cdk-lib/aws-ec2'; import { IntegTest } from '@aws-cdk/integ-tests-alpha'; const app = new App(); -const stack = new Stack(app); +const stack = new Stack(app, 'OneIPv6Block'); -new ec2.VpcDualStack(stack, 'TestVpcDualStack'); +//new ec2.VpcDualStack(stack, 'TestVpcDualStack'); + +new ec2.VpcDualStackOneIPv6Block(stack, 'TestVpcDualStackOneIPv6Block'); new IntegTest(app, 'DualStackTesting', { testCases: [stack], diff --git a/packages/aws-cdk-lib/aws-ec2/lib/index.ts b/packages/aws-cdk-lib/aws-ec2/lib/index.ts index 276856f8470ad..91b09ec846dec 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/index.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/index.ts @@ -17,6 +17,7 @@ export * from './subnet'; export * from './peer'; export * from './volume'; export * from './vpc'; +export * from './vpc-dual-stack'; export * from './vpc-lookup'; export * from './vpn'; export * from './vpc-endpoint'; diff --git a/packages/aws-cdk-lib/aws-ec2/lib/vpc-dual-stack.ts b/packages/aws-cdk-lib/aws-ec2/lib/vpc-dual-stack.ts index 40e668c302cf6..6f365ac2efca5 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/vpc-dual-stack.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/vpc-dual-stack.ts @@ -1,19 +1,24 @@ +import { Construct } from 'constructs'; +import { Fn, Tags } from '../../core'; import { CfnEgressOnlyInternetGateway, + CfnInternetGateway, CfnRoute, CfnSubnet, CfnVPC, CfnVPCCidrBlock, + CfnVPCGatewayAttachment, IIpAddresses, + FlowLogDestination, NatProvider, + PrivateSubnet, + PublicSubnet, RouterType, Subnet, + SubnetType, Vpc, } from '../lib'; -import { Fn, Tags } from '../../core'; -import { Construct } from 'constructs'; - export enum SubnetProtocol { DUAL_STACK = 'DualStack', IPV4 = 'IPv4', @@ -55,6 +60,96 @@ export interface NetworkLayerProps { readonly vpcName?: string; } +export class VpcDualStackOneIPv6Block extends Construct { + public readonly Vpc: Vpc; + + constructor(scope: Construct, id: string) { + super(scope, id); + + // TODO for Production: Set the cidr property of this construct to be a range that doesn't overlap with existing networks + this.Vpc = new Vpc(this, 'Vpc', { + subnetConfiguration: [ + { + name: 'Public', + subnetType: SubnetType.PUBLIC, + }, + { + name: 'Private', + subnetType: SubnetType.PRIVATE_WITH_NAT, + }, + ], + flowLogs: { + cloudwatchLogs: { + destination: FlowLogDestination.toCloudWatchLogs(), + }, + }, + }); + + const ipv6Cidr = new CfnVPCCidrBlock(this, 'ipv6cidr', { + vpcId: this.Vpc.vpcId, + amazonProvidedIpv6CidrBlock: true, + }); + + const vpc6cidr = Fn.select(0, this.Vpc.vpcIpv6CidrBlocks); + const subnet6cidrs = Fn.cidr(vpc6cidr, 256, (128 - 64).toString()); + + const allSubnets = [...this.Vpc.publicSubnets, ...this.Vpc.privateSubnets, ...this.Vpc.isolatedSubnets]; + + // associate an IPv6 block to each subnets + allSubnets.forEach((subnet, i) => { + const cidr6 = Fn.select(i, subnet6cidrs); + + const cfnSubnet = subnet.node.defaultChild as CfnSubnet; + cfnSubnet.ipv6CidrBlock = cidr6; + cfnSubnet.assignIpv6AddressOnCreation = true; + subnet.node.addDependency(ipv6Cidr); + }); + + // for public subnets, ensure there is one IPv6 Internet Gateway + if (this.Vpc.publicSubnets) { + let igwId = this.Vpc.internetGatewayId; + if (!igwId) { + const igw = new CfnInternetGateway(this, 'IGW'); + igwId = igw.ref; + + new CfnVPCGatewayAttachment(this, 'VPCGW', { + internetGatewayId: igw.ref, + vpcId: this.Vpc.vpcId, + }); + } + + // and that each subnet has a routing table to the Internet Gateway + this.Vpc.publicSubnets.forEach((subnet) => { + const s = subnet as PublicSubnet; + s.addRoute('DefaultRoute6', { + routerType: RouterType.GATEWAY, + routerId: igwId!, + destinationIpv6CidrBlock: '::/0', + enablesInternetConnectivity: true, + }); + }); + } + + // for private subnet, ensure there is an IPv6 egress gateway + if (this.Vpc.privateSubnets) { + const eigw = new CfnEgressOnlyInternetGateway(this, 'EIGW6', { + vpcId: this.Vpc.vpcId, + }); + + // and attach a routing table to the egress gateway + this.Vpc.privateSubnets.forEach((subnet) => { + const s = subnet as PrivateSubnet; + s.addRoute('DefaultRoute6', { + routerType: RouterType.EGRESS_ONLY_INTERNET_GATEWAY, + routerId: eigw.ref, + destinationIpv6CidrBlock: '::/0', + enablesInternetConnectivity: true, + }); + }); + } + } +} + export class VpcDualStack extends Construct { public readonly vpc: Vpc;