Skip to content

Commit

Permalink
add validation for ipv6 in subnet
Browse files Browse the repository at this point in the history
  • Loading branch information
shikha372 committed Jun 13, 2024
1 parent e7cd0e9 commit 1d0e247
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 52 deletions.
26 changes: 2 additions & 24 deletions packages/@aws-cdk/aws-vpcv2-alpha/lib/ipam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ export class Ipam {
public readonly ipamID: string;
constructor(scope: Construct, id: string) {
this._ipam = new CfnIPAM(scope, id);
this.publicScope = new IpamPublicScope(scope, 'DefaultPublic');
this.privateScope = new IpamPrivateScope(scope, 'DefaultPrivate');
this.publicScope = new IpamPublicScope(scope, this._ipam.attrPublicDefaultScopeId);
this.privateScope = new IpamPrivateScope(scope, this._ipam.attrPrivateDefaultScopeId);
this.ipamID = this._ipam.attrIpamId;
}
}
Expand Down Expand Up @@ -180,28 +180,6 @@ export class IpamPrivateScope {
// provisionedCidrs: [{ cidr: '10.0.0.0/24' }],
// });

// export class IpamPool extends Resource {

// static publicScope: any;

// publicScope = {
// addPool: (options: PoolOptions) => {
// const scope = new IpamScope(this, 'PublicScope', {
// scopeType: ScopeType.PUBLIC,
// });
// return new CfnIPAMPool(this, '', {
// addressFamily: options.addressFamily.toString(),
// provisionedCidrs: options.provisionedCidrs,
// ipamScopeId: scope.ipamScopeId;
// });
// },
// }
// constructor(stack: Construct, id: string) {
// super(stack, id);
// }

// }

export class IpamIpv6 implements IIpAddresses {

constructor(private readonly props: IpamOptions) {
Expand Down
43 changes: 30 additions & 13 deletions packages/@aws-cdk/aws-vpcv2-alpha/lib/subnet-v2.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/* eslint-disable @typescript-eslint/member-ordering */
// /* eslint-disable @typescript-eslint/member-ordering */
import { Resource, Names } from 'aws-cdk-lib';
import { CfnRouteTable, CfnSubnet, CfnSubnetRouteTableAssociation, INetworkAcl, IRouteTable, ISubnet, IVpc, NetworkAcl, SubnetNetworkAclAssociation } from 'aws-cdk-lib/aws-ec2';
import { CfnRouteTable, CfnSubnet, CfnSubnetRouteTableAssociation, INetworkAcl, IRouteTable, ISubnet, NetworkAcl, SubnetNetworkAclAssociation } from 'aws-cdk-lib/aws-ec2';
import { Construct, DependencyGroup, IDependable } from 'constructs';
import { IVpcV2 } from './vpc-v2-base';

export interface ICidr {
readonly cidr: string;
Expand All @@ -23,16 +24,16 @@ export class Ipv4Cidr implements ICidr {
export class Ipv6Cidr implements ICidr {

public readonly cidr: string;
constructor(props: cidrBlockProps ) {
this.cidr = props.cidrBlock;
constructor(props: string ) {
this.cidr = props;
}
}

export interface SubnetPropsV2 {
/**
* VPC Prop
*/
vpc: IVpc;
vpc: IVpcV2;

/**
* custom CIDR range
Expand Down Expand Up @@ -104,18 +105,21 @@ export class SubnetV2 extends Resource implements ISubnet {
constructor(scope: Construct, id: string, props: SubnetPropsV2) {
super(scope, id);

let ipv4cidr: string = '';
//let ipv6cidr: string = '';
let ipv4CidrBlock: string | undefined;
let ipv6CidrBlock: string| undefined;

if (props.cidrBlock instanceof Ipv4Cidr) {
ipv4cidr = props.cidrBlock.cidr;
ipv4CidrBlock = props.cidrBlock.cidr;
} else if (props.cidrBlock instanceof Ipv6Cidr) {
if (validateSupportIpv6(props.vpc)) {
ipv6CidrBlock = props.cidrBlock.cidr;
}

}
// } else if (props.cidrBlock instanceof Ipv6Cidr) {
// ipv6cidr = props.cidrBlock.cidr;
// }
const subnet = new CfnSubnet(this, 'Subnet', {
vpcId: props.vpc.vpcId,
cidrBlock: ipv4cidr,
//ipv6CidrBlock: ipv6cidr,
cidrBlock: ipv4CidrBlock,
ipv6CidrBlock: ipv6CidrBlock,
availabilityZone: props.availabilityZone,
});

Expand Down Expand Up @@ -150,6 +154,7 @@ export class SubnetV2 extends Resource implements ISubnet {
pushIsolatedSubnet(props.vpc, this);
} //isolated by default
}

/**
* Associate a Network ACL with this subnet
*
Expand All @@ -172,6 +177,18 @@ export class SubnetV2 extends Resource implements ISubnet {

}

function pushIsolatedSubnet(vpc: IVpc, subnet: SubnetV2) {
function pushIsolatedSubnet(vpc: IVpcV2, subnet: SubnetV2) {
vpc.isolatedSubnets.push(subnet);
}

/**
* currently checking for amazon provided Ipv6 only which we plan to release
*/

function validateSupportIpv6(vpc: IVpcV2) {
if (vpc.cidrBlock.some((secondaryAddress) => secondaryAddress.amazonProvidedIpv6CidrBlock === true)) {
return true;
} else {
throw new Error('To use IPv6, the VPC must enable IPv6 support.');
}
}
150 changes: 150 additions & 0 deletions packages/@aws-cdk/aws-vpcv2-alpha/lib/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { Construct } from 'constructs';
import { ISubnet, Subnet, SubnetType } from 'aws-cdk-lib/aws-ec2';

/**
* Turn an arbitrary string into one that can be used as a CloudFormation identifier by stripping special characters
*
* (At the moment, no efforts are taken to prevent collisions, but we can add that later when it becomes necessary).
*/
export function slugify(x: string): string {
return x.replace(/[^a-zA-Z0-9]/g, '');
}

/**
* The default names for every subnet type
*/
export function defaultSubnetName(type: SubnetType) {
switch (type) {
case SubnetType.PUBLIC: return 'Public';
case SubnetType.PRIVATE_WITH_NAT:
case SubnetType.PRIVATE_WITH_EGRESS:
case SubnetType.PRIVATE:
return 'Private';
case SubnetType.PRIVATE_ISOLATED:
case SubnetType.ISOLATED:
return 'Isolated';
}
}

/**
* Return a subnet name from its construct ID
*
* All subnet names look like NAME <> "Subnet" <> INDEX
*/
export function subnetGroupNameFromConstructId(subnet: ISubnet) {
return subnet.node.id.replace(/Subnet\d+$/, '');
}

/**
* Make the subnet construct ID from a name and number
*/
export function subnetId(name: string, i: number) {
return `${name}Subnet${i + 1}`;
}

export class ImportSubnetGroup {
private readonly subnetIds: string[];
private readonly names: string[];
private readonly routeTableIds: string[];
private readonly ipv4CidrBlocks: string[];
private readonly groups: number;

constructor(
subnetIds: string[] | undefined,
names: string[] | undefined,
routeTableIds: string[] | undefined,
ipv4CidrBlocks: string[] | undefined,
type: SubnetType,
private readonly availabilityZones: string[],
idField: string,
nameField: string,
routeTableIdField: string,
ipv4CidrBlockField: string) {

this.subnetIds = subnetIds || [];
this.routeTableIds = routeTableIds || [];
this.ipv4CidrBlocks = ipv4CidrBlocks || [];
this.groups = this.subnetIds.length / this.availabilityZones.length;

if (Math.floor(this.groups) !== this.groups) {
// eslint-disable-next-line max-len
throw new Error(`Number of ${idField} (${this.subnetIds.length}) must be a multiple of availability zones (${this.availabilityZones.length}).`);
}
if (this.routeTableIds.length !== this.subnetIds.length && routeTableIds != null) {
// We don't err if no routeTableIds were provided to maintain backwards-compatibility. See https://github.com/aws/aws-cdk/pull/3171
/* eslint-disable max-len */
throw new Error(`Number of ${routeTableIdField} (${this.routeTableIds.length}) must be equal to the amount of ${idField} (${this.subnetIds.length}).`);
}
if (this.ipv4CidrBlocks.length !== this.subnetIds.length && ipv4CidrBlocks != null) {
// We don't err if no ipv4CidrBlocks were provided to maintain backwards-compatibility.
/* eslint-disable max-len */
throw new Error(`Number of ${ipv4CidrBlockField} (${this.ipv4CidrBlocks.length}) must be equal to the amount of ${idField} (${this.subnetIds.length}).`);
}

this.names = this.normalizeNames(names, defaultSubnetName(type), nameField);
}

public import(scope: Construct): ISubnet[] {
return range(this.subnetIds.length).map(i => {
const k = Math.floor(i / this.availabilityZones.length);
return Subnet.fromSubnetAttributes(scope, subnetId(this.names[k], i), {
availabilityZone: this.pickAZ(i),
subnetId: this.subnetIds[i],
routeTableId: this.routeTableIds[i],
ipv4CidrBlock: this.ipv4CidrBlocks[i],
});
});
}

/**
* Return a list with a name for every subnet
*/
private normalizeNames(names: string[] | undefined, defaultName: string, fieldName: string) {
// If not given, return default
if (names === undefined || names.length === 0) {
return [defaultName];
}

// If given, must match given subnets
if (names.length !== this.groups) {
throw new Error(`${fieldName} must have an entry for every corresponding subnet group, got: ${JSON.stringify(names)}`);
}

return names;
}

/**
* Return the i'th AZ
*/
private pickAZ(i: number) {
return this.availabilityZones[i % this.availabilityZones.length];
}
}

/**
* Generate the list of numbers of [0..n)
*/
export function range(n: number): number[] {
const ret: number[] = [];
for (let i = 0; i < n; i++) {
ret.push(i);
}
return ret;
}

/**
* Return the union of table IDs from all selected subnets
*/
export function allRouteTableIds(subnets: ISubnet[]): string[] {
const ret = new Set<string>();
for (const subnet of subnets) {
if (subnet.routeTable && subnet.routeTable.routeTableId) {
ret.add(subnet.routeTable.routeTableId);
}
}
return Array.from(ret);
}

export function flatten<A>(xs: A[][]): A[] {
return Array.prototype.concat.apply([], xs);
}
17 changes: 14 additions & 3 deletions packages/@aws-cdk/aws-vpcv2-alpha/lib/vpc-v2-base.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { Resource, Annotations } from 'aws-cdk-lib';
import { IVpc, ISubnet, SubnetSelection, SelectedSubnets, EnableVpnGatewayOptions, VpnGateway, VpnConnectionType, CfnVPCGatewayAttachment, CfnVPNGatewayRoutePropagation, VpnConnectionOptions, VpnConnection, ClientVpnEndpointOptions, ClientVpnEndpoint, InterfaceVpcEndpointOptions, InterfaceVpcEndpoint, GatewayVpcEndpointOptions, GatewayVpcEndpoint, FlowLogOptions, FlowLog, FlowLogResourceType, SubnetType, SubnetFilter } from 'aws-cdk-lib/aws-ec2';
import { allRouteTableIds, flatten, subnetGroupNameFromConstructId } from 'aws-cdk-lib/aws-ec2/lib/util';
import { IVpc, ISubnet, SubnetSelection, SelectedSubnets, EnableVpnGatewayOptions, VpnGateway, VpnConnectionType, CfnVPCGatewayAttachment, CfnVPNGatewayRoutePropagation, VpnConnectionOptions, VpnConnection, ClientVpnEndpointOptions, ClientVpnEndpoint, InterfaceVpcEndpointOptions, InterfaceVpcEndpoint, GatewayVpcEndpointOptions, GatewayVpcEndpoint, FlowLogOptions, FlowLog, FlowLogResourceType, SubnetType, SubnetFilter, CfnVPCCidrBlock } from 'aws-cdk-lib/aws-ec2';
// eslint-disable-next-line no-duplicate-imports
import { allRouteTableIds, flatten, subnetGroupNameFromConstructId } from '../lib/util';
import { IDependable, Dependable, IConstruct } from 'constructs';

export abstract class VpcV2Base extends Resource implements IVpc {
/**
* Placeholder to see what extra props we might need,
* will be added to original IVPC
*/
export interface IVpcV2 extends IVpc {
readonly cidrBlock: CfnVPCCidrBlock[];
}

export abstract class VpcV2Base extends Resource implements IVpcV2 {

/**
* Identifier for this VPC
Expand Down Expand Up @@ -50,6 +59,8 @@ export abstract class VpcV2Base extends Resource implements IVpc {
*/
public abstract readonly internetConnectivityEstablished: IDependable;

public abstract readonly cidrBlock: CfnVPCCidrBlock[];

/**
* If this is set to true, don't error out on trying to select subnets
*/
Expand Down
32 changes: 23 additions & 9 deletions packages/@aws-cdk/aws-vpcv2-alpha/lib/vpc-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export interface VpcV2Options {
readonly ipv6Pool?: string;

/**
* required with cidr block for BYOL IP
* use amazon provided IP range
*/
readonly amazonProvided?: boolean;
}
Expand Down Expand Up @@ -144,7 +144,6 @@ export interface VpcV2Props {
readonly secondaryAddressBlocks?: IIpAddresses[];
readonly enableDnsHostnames?: boolean;
readonly enableDnsSupport?: boolean;
readonly useIpv6?: boolean;
}

/**
Expand Down Expand Up @@ -199,6 +198,17 @@ export class VpcV2 extends VpcV2Base {

private readonly _internetConnectivityEstablished = new DependencyGroup();

/**
* reference to all secondary blocks attached
*/
public readonly cidrBlock= new Array<CfnVPCCidrBlock>;

/**
* For validation to define IPv6 subnets
* @default false
*/
public readonly useIpv6: boolean = false;

constructor(scope: Construct, id: string, props: VpcV2Props = {}) {
super(scope, id);

Expand Down Expand Up @@ -226,17 +236,21 @@ export class VpcV2 extends VpcV2Base {
if (props.secondaryAddressBlocks) {

const secondaryAddressBlocks: IIpAddresses[] = props.secondaryAddressBlocks;

let ipCount = 0;
for (const secondaryAddressBlock of secondaryAddressBlocks) {
//TODO: Add unique has for each secondary ip address
ipCount+=1;
const vpcoptions: VpcV2Options = secondaryAddressBlock.allocateVpcCidr();

if (vpcOptions.amazonProvided === true) {
this.useIpv6 = true;
}
// validate CIDR ranges per RFC 1918
// if (vpcOptions.ipv4CidrBlock!) {
// validateIpv4address(vpcoptions.ipv4CidrBlock);
// }
//Create secondary blocks for Ipv4 and Ipv6
//TODO: Add unique has for each secondary ip address
new CfnVPCCidrBlock(this, `Secondary${vpcoptions.ipv4CidrBlock}`, {
this.cidrBlock = [...this.cidrBlock, new CfnVPCCidrBlock(this, `SecondaryIp${ipCount}`, {
vpcId: this.vpcId,
cidrBlock: vpcoptions.ipv4CidrBlock,
ipv4IpamPoolId: vpcoptions.ipv4IpamPoolId,
Expand All @@ -247,7 +261,7 @@ export class VpcV2 extends VpcV2Base {
ipv6NetmaskLength: vpcoptions.ipv6NetmaskLength,
ipv6IpamPoolId: vpcoptions.ipv6IpamPoolId,
amazonProvidedIpv6CidrBlock: vpcoptions.amazonProvided,
});
})];
}
}

Expand Down Expand Up @@ -295,14 +309,14 @@ class ipv6CidrAllocation implements IIpAddresses {

export class AmazonProvided implements IIpAddresses {

private readonly amazonProvided: boolean;
//private readonly amazonProvided: boolean;
constructor() {
this.amazonProvided = true;
//this.amazonProvided = true;
};

allocateVpcCidr(): VpcV2Options {
return {
amazonProvided: this.amazonProvided,
amazonProvided: true,
};
}

Expand Down
Loading

0 comments on commit 1d0e247

Please sign in to comment.