raindancers-network
Version:
Extensions to the ec2.Vpc Constructs
592 lines • 88.7 kB
JavaScript
"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CloudWanTGW = void 0;
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
const path = require("path");
const cdk = require("aws-cdk-lib");
const aws_cdk_lib_1 = require("aws-cdk-lib");
const constructs = require("constructs");
const CloudWanTGWProps = require("./cloudwanTGWProps");
const ipam_1 = require("../ipam/ipam");
/**
* Create a TransitGateway That is attached to Cloudwan
*/
class CloudWanTGW extends constructs.Construct {
/**
*
* @param scope scope in which the resource is c
* @param id
* @param props TGWOnCloudWanProps
*/
constructor(scope, id, props) {
super(scope, id);
this.tgcidr = props.tgCidr;
//this.tgDXattachmentId = 'abcef';
// look up the corewan
const lookupIdLambda = new aws_cdk_lib_1.aws_lambda.Function(this, 'lookupIdLambda-tgOnCloudwan', {
runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9,
handler: 'getcloudwanID.on_event',
code: aws_cdk_lib_1.aws_lambda.Code.fromAsset(path.join(__dirname, '../../lambda/cloudwan')),
});
lookupIdLambda.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({
effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW,
resources: ['*'],
actions: ['networkmanager:ListCoreNetworks'],
}));
// get the parameters from the core.
const coreNetwork = new cdk.CustomResource(this, 'idfinderCR', {
serviceToken: new aws_cdk_lib_1.custom_resources.Provider(this, 'NetworkManagerProvider', {
onEventHandler: lookupIdLambda,
}).serviceToken,
properties: {
CoreNetworkName: this.node.tryGetContext('coreNetworkName'),
ForceUpdate: new Date().toISOString(),
},
});
// create a transit gateway that will fit in as a shim between cloud WAN and the egress VPC. this is to
// over come the issues with Cloudwan not supporting Appliance Mode, and asymetric routes.
const egressTG = new aws_cdk_lib_1.aws_ec2.CfnTransitGateway(this, 'EgressTg', /* all optional props */ {
amazonSideAsn: props.amazonSideAsn,
transitGatewayCidrBlocks: props.tgCidr,
autoAcceptSharedAttachments: 'enable',
defaultRouteTableAssociation: 'enable',
defaultRouteTablePropagation: 'enable',
description: props.description,
dnsSupport: 'enable',
multicastSupport: 'enable',
});
// a transit gateway must have a policy table to be attached to Cloudwan..
new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'TWpolicyTable', {
onCreate: {
service: 'EC2',
action: 'createTransitGatewayPolicyTable',
parameters: {
TransitGatewayId: egressTG.attrId,
},
physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.fromResponse('TransitGatewayPolicyTable.TransitGatewayPolicyTableId'),
},
onDelete: {
service: 'EC2',
action: 'deleteTransitGatewayPolicyTable',
parameters: {
TransitGatewayPolicyTableId: new aws_cdk_lib_1.custom_resources.PhysicalResourceIdReference(),
},
},
policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE,
}),
});
//get the TG's default routing table, as the attribute is not workign
const getDefaultRoutingTableId = new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'defaultroutingtableId', {
onCreate: {
service: 'EC2',
action: 'describeTransitGateways',
parameters: {
TransitGatewayIds: [egressTG.attrId],
},
physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.fromResponse('TransitGateways.0.Options.AssociationDefaultRouteTableId'),
},
policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE,
}),
});
const routingtableId = getDefaultRoutingTableId.getResponseField('TransitGateways.0.Options.AssociationDefaultRouteTableId');
this.transitGateway = egressTG;
// const egressTGRoutingTable = new ec2.CfnTransitGatewayRouteTable(this, 'TGRouteTable', {
// transitGatewayId: egressTG.attrId
// })
//register the transitgateway with the global network.
new aws_cdk_lib_1.aws_networkmanager.CfnTransitGatewayRegistration(this, 'TransitGatewayRegistration', {
globalNetworkId: coreNetwork.getAttString('GlobalNetworkId'),
transitGatewayArn: `arn:aws:ec2:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:transit-gateway/${egressTG.attrId}`,
});
// peer the tg to cloudwan.
// Checked that this builds and deletes
const transitGatewaypeering = new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'AttachTGtoCloudWan', {
onCreate: {
service: 'NetworkManager',
action: 'createTransitGatewayPeering',
parameters: {
CoreNetworkId: coreNetwork.getAttString('CoreNetworkId'),
TransitGatewayArn: `arn:aws:ec2:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:transit-gateway/${egressTG.attrId}`,
},
physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.fromResponse('TransitGatewayPeering.Peering.PeeringId'),
region: 'us-west-2',
},
onDelete: {
service: 'NetworkManager',
action: 'deletePeering',
parameters: {
PeeringId: new aws_cdk_lib_1.custom_resources.PhysicalResourceIdReference(),
},
region: 'us-west-2',
},
logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.SEVEN_YEARS,
policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromStatements([
new aws_cdk_lib_1.aws_iam.PolicyStatement({
actions: ['*'],
resources: ['*'],
}),
]),
});
// Attach TransitGatewayRouteTabletoCloudwan
// lambdas for custom resource
const onEvent = new aws_cdk_lib_1.aws_lambda.Function(this, 'oneventlambda', {
code: aws_cdk_lib_1.aws_lambda.Code.fromAsset(path.join(__dirname, '../../lambda/cloudwan'), {
bundling: {
image: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9.bundlingImage,
command: [
'bash', '-c',
'pip install -r requirements.txt -t /asset-output && cp -au . /asset-output',
],
},
}),
runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9,
handler: 'peerroutingtable.on_event',
});
onEvent.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({
effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW,
resources: ['*'],
actions: [
'networkmanager:deleteAttachment',
],
}));
const isPeeringDone = new aws_cdk_lib_1.aws_lambda.Function(this, 'ispeeringDone', {
code: aws_cdk_lib_1.aws_lambda.Code.fromAsset(path.join(__dirname, '../../lambda/cloudwan'), {
bundling: {
image: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9.bundlingImage,
command: [
'bash', '-c',
'pip install -r requirements.txt -t /asset-output && cp -au . /asset-output',
],
},
}),
runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9,
handler: 'peerroutingtable.is_complete',
});
isPeeringDone.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({
effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW,
resources: ['*'],
actions: [
'networkmanager:CreateTransitGatewayRouteTableAttachment',
'networkmanager:deleteAttachment',
'*',
],
}));
const AttachTGRouteTableToCloudwanProvider = new aws_cdk_lib_1.custom_resources.Provider(this, 'isReadyProvider', {
onEventHandler: onEvent,
isCompleteHandler: isPeeringDone,
logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.SEVEN_YEARS,
queryInterval: cdk.Duration.seconds(30),
});
// we will place the attachmentId in a SSM parameter as its difficult to lookup with the SDK
const attachmentId = new aws_cdk_lib_1.aws_ssm.StringParameter(this, 'attachmentID', {
stringValue: 'Null',
});
attachmentId.grantRead(isPeeringDone);
attachmentId.grantWrite(isPeeringDone);
attachmentId.grantRead(onEvent);
attachmentId.grantWrite(onEvent);
const cloudwanTgAttachment = new cdk.CustomResource(this, 'AttachTGRouteTable', {
serviceToken: AttachTGRouteTableToCloudwanProvider.serviceToken,
properties: {
transitGatewayRouteTableArn: `arn:aws:ec2:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:transit-gateway-route-table/${routingtableId}`,
PeeringId: transitGatewaypeering.getResponseField('TransitGatewayPeering.Peering.PeeringId'),
AttachmentSegment: props.attachmentSegment,
CoreNetworkId: coreNetwork.getAttString('CoreNetworkId'),
EdgeLocation: cdk.Aws.REGION,
attachmentIdSSMName: attachmentId.parameterName,
},
});
this.cloudwanTgAttachmentId = cloudwanTgAttachment.getAttString('AttachmentId');
}
/**
* @param dxgatewayId Id of a DX gateway that
*/
createDirectConnectGatewayAssociation(dxgatewayId) {
// associate the TransitGateway with the DXGateway
// https://docs.aws.amazon.com/cli/latest/reference/directconnect/create-direct-connect-gateway-association.html
var cidrList = [];
// create-direct-connect-gateway-association requires the prefix lists to be presented as a array of objects
// in the format {'cidr':cidr}
//
// this is a bit unfriendly, so we we create them here.
if (this.tgcidr) {
this.tgcidr?.forEach((cidr) => {
cidrList.push({ cidr: cidr });
});
}
const dxGWAssn = new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'CreateDXGatewayAssn', {
onCreate: {
service: 'DirectConnect',
action: 'createDirectConnectGatewayAssociation',
parameters: {
directConnectGatewayId: dxgatewayId,
addAllowedPrefixesToDirectConnectGateway: cidrList,
gatewayId: this.transitGateway.attrId,
},
physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.fromResponse('directConnectGatewayAssociation.associationId'),
},
onDelete: {
service: 'DirectConnect',
action: 'DeleteDirectConnectGatewayAssociation',
parameters: {
directConnectGatewayID: new aws_cdk_lib_1.custom_resources.PhysicalResourceIdReference(),
},
},
logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.ONE_WEEK,
policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE,
}),
});
const getAttachmentId = new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'Getattachment', {
onCreate: {
service: 'EC2',
action: 'describeTransitGatewayAttachments',
parameters: {
Filters: [
{
Name: 'resource-type',
Values: [
'direct-connect-gateway',
],
},
{
Name: 'transit-gateway-id',
Values: [
this.transitGateway.attrId,
],
},
],
},
physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.of('getAttachmentId'),
},
logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.ONE_WEEK,
policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE,
}),
});
getAttachmentId.node.addDependency(dxGWAssn);
this.tgDXattachmentId = getAttachmentId.getResponseField('TransitGatewayAttachments.0.TransitGatewayAttachmentId');
//this.tgDXattachmentId = dxGWAssn.getResponseField('directConnectGatewayAssociation.associationId');
return getAttachmentId.getResponseField('TransitGatewayAttachments.0.TransitGatewayAttachmentId');
}
/**
* provision a DX Gateway and attach it to the transit gateway
* @param dxgatewayname The name of the dxgateway
* @param dxgatewayASN An ASN for the Dxgateway
* @returns Direct Connect gatewayId
*/
addDXGateway(dxgatewayname, dxgatewayASN) {
const dxgateway = new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'CreateDXGateway', {
onCreate: {
service: 'DirectConnect',
action: 'createDirectConnectGateway',
parameters: {
directConnectGatewayName: dxgatewayname,
amazonSideAsn: dxgatewayASN,
},
physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.fromResponse('directConnectGateway.directConnectGatewayId'),
},
onDelete: {
service: 'DirectConnect',
action: 'deleteDirectConnectGateway',
parameters: {
directConnectGatewayID: new aws_cdk_lib_1.custom_resources.PhysicalResourceIdReference(),
},
},
logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.ONE_WEEK,
policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE,
}),
});
const gatewayId = dxgateway.getResponseField('directConnectGateway.directConnectGatewayId');
// associate the TransitGateway with the DXGateway
// https://docs.aws.amazon.com/cli/latest/reference/directconnect/create-direct-connect-gateway-association.html
var cidrList = [];
// create-direct-connect-gateway-association requires the prefix lists to be presented as a array of objects
// in the format {'cidr':cidr}
//
// this is a bit unfriendly, so we we create them here.
if (this.tgcidr) {
this.tgcidr?.forEach((cidr) => {
cidrList.push({ cidr: cidr });
});
}
new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'CreateDXGateway', {
onCreate: {
service: 'DirectConnect',
action: 'createDirectConnectGatewayAssociation',
parameters: {
directConnectGatewayId: dxgateway.getResponseField('directConnectGateway.directConnectGatewayId'),
addAllowedPrefixesToDirectConnectGateway: this.tgcidr,
gatewayId: this.transitGateway.attrId,
},
physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.fromResponse('directConnectGatewayAssociation.associationId'),
},
onDelete: {
service: 'associationId',
action: 'DeleteDirectConnectGatewayAssociation',
parameters: {
directConnectGatewayID: new aws_cdk_lib_1.custom_resources.PhysicalResourceIdReference(),
},
},
logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.ONE_WEEK,
policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE,
}),
});
return gatewayId;
}
/**
* Creates a Site To Site IPSec VPN between the Transit Gateway and Customer Gateway,
* using a defined set of VPn Properties.
* @param name A name to identify the vpn
* @param vpnprops the vpn properties
* @returns
*/
adds2sVPN(name, vpnprops) {
const vpnPresharedKey = new aws_cdk_lib_1.aws_secretsmanager.Secret(this, `${name}PresharedKey`, {
generateSecretString: {
excludePunctuation: true,
passwordLength: 61,
},
secretName: `${name}-PSK`,
description: `PresharedKey for ${name} s2S VPN`,
});
const createVpnCrLambda = new aws_cdk_lib_1.aws_lambda.SingletonFunction(this, `createVpnCrLambda${name}`, {
uuid: 'AABBCCDDEEFF000001',
code: aws_cdk_lib_1.aws_lambda.Code.fromAsset(path.join(__dirname, '../../lambda/cloudwan'), {
bundling: {
image: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9.bundlingImage,
command: [
'bash', '-c',
'pip install -r requirements.txt -t /asset-output && cp -au . /asset-output',
],
},
}),
runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9,
handler: 'addS2Svpn.on_event',
});
createVpnCrLambda.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({
effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW,
resources: ['*'],
actions: [
'ec2:CreateVpnConnection',
'ec2:DeleteVpnConnection',
'ec2:DeleteTags',
'ec2:CreateTags',
],
}));
vpnPresharedKey.grantRead(createVpnCrLambda);
const createVPNProvider = new aws_cdk_lib_1.custom_resources.Provider(this, `createVPNProvider${name}`, {
onEventHandler: createVpnCrLambda,
logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.ONE_WEEK,
});
// create a flow log for the vpn
var cloudWatchLogOptions;
if (vpnprops.vpnspec.enableLogging !== undefined) {
if (vpnprops.vpnspec.enableLogging) {
var vpnlog = new aws_cdk_lib_1.aws_logs.LogGroup(this, `${name}vpnLogGroup`, {
retention: aws_cdk_lib_1.aws_logs.RetentionDays.THREE_MONTHS,
});
vpnlog.grantWrite(new aws_cdk_lib_1.aws_iam.ServicePrincipal('delivery.logs.amazonaws.com'));
// const cloudWatchLogOptions = {
// LogEnabled: true,
// LogGroupArn: vpnlog.logGroupArn,
// LogOutputFormat: 'json'
// }
}
}
// validate that if the Outside addrss's are Private that Acceleration is not enabled.
if (vpnprops.vpnspec.outsideIpAddressType === CloudWanTGWProps.OutsideIpAddressType.PRIVATE && vpnprops.vpnspec.enableAcceleration === true) {
throw new Error('if using Private addresss for S2S Vpn, Vpn Acceleration can not be enabled.');
}
// validate that if the Outside address's are Private that a DX gateway Assn is provided.
if (vpnprops.vpnspec.outsideIpAddressType === CloudWanTGWProps.OutsideIpAddressType.PRIVATE && !this.tgDXattachmentId) {
throw new Error('If the Outside address for a S2S VPN is Private, a dxAssociationId must be provided');
}
// validate Timeout
if (vpnprops.vpnspec.phase1LifetimeSeconds !== undefined) {
if (!(vpnprops.vpnspec.phase1LifetimeSeconds >= 900 && vpnprops.vpnspec.phase1LifetimeSeconds <= 28000)) {
throw new Error('Phase1 Life time must be between 900 and 28000');
}
}
// validate p2 Timeout
if (vpnprops.vpnspec.phase2LifeTimeSeconds !== undefined) {
if (!(vpnprops.vpnspec.phase2LifeTimeSeconds >= 900 && vpnprops.vpnspec.phase2LifeTimeSeconds <= 3600)) {
throw new Error('Phase2 Life time must be between 900 and 3600');
}
if (vpnprops.vpnspec.phase1LifetimeSeconds !== undefined) {
if (!(vpnprops.vpnspec.phase1LifetimeSeconds > vpnprops.vpnspec.phase2LifeTimeSeconds)) {
throw new Error('Phase1 Life time must be greater than phase2 lifetime ');
}
}
}
// validate rekeyFuzzPercentage
if (vpnprops.vpnspec.rekeyFuzzPercentage !== undefined) {
if (vpnprops.vpnspec.rekeyFuzzPercentage > 100 && vpnprops.vpnspec.rekeyFuzzPercentage < 0) {
throw new Error('rekey Fuzz Percentage must be between 0 and 100');
}
}
// validate rekeyMarginTimeSeconds
// Constraints: A value between 60 and half of Phase2LifetimeSeconds
var p2lifetime;
if (vpnprops.vpnspec.phase2LifeTimeSeconds === undefined) {
p2lifetime = 540;
}
else {
p2lifetime = vpnprops.vpnspec.phase2LifeTimeSeconds / 2;
}
if (vpnprops.vpnspec.rekeyMarginTimeSeconds !== undefined) {
if (!(vpnprops.vpnspec.rekeyMarginTimeSeconds >= 60 && vpnprops.vpnspec.rekeyMarginTimeSeconds <= p2lifetime)) {
throw new Error('rekeyMarginTimeSeconds must be a value between 60 and half of Phase2LifetimeSeconds ');
}
}
//valdiate replayWindowsSize:
// Constraints: A value between 64 and 2048.
if (vpnprops.vpnspec.replayWindowSize !== undefined) {
if (vpnprops.vpnspec.replayWindowSize <= 64 && vpnprops.vpnspec.replayWindowSize >= 2048) {
throw new Error('replayWindowSize must be a value between 64 and 2048');
}
}
// validate or create Ipam address's
if (vpnprops.tunnelInsideCidr === undefined && vpnprops.tunnelIpamPool === undefined) {
throw new Error('At least one of tunnelInsideCidr or tunnelIpamPool must be defined');
}
else if (vpnprops.tunnelInsideCidr !== undefined && vpnprops.tunnelIpamPool !== undefined) {
throw new Error('Only one of tunnelInsideCidr or tunnelIpamPool can be defined');
}
var assignedCidrs = [];
// validate if supplied tunnels are valid
if (vpnprops.tunnelInsideCidr !== undefined) {
vpnprops.tunnelInsideCidr.forEach((cidr) => {
// check to see that cidr starts with 169.254 and ends with /30
if (!(cidr.endsWith('/30') && cidr.startsWith('169.254'))) {
throw new Error('Invalid tunnel Cidr');
}
//check to see if cidr is in reserved range
const reservedTunnelIP = [
'169.254.0.0/30',
'169.254.1.0/30',
'169.254.2.0/30',
'169.254.3.0/30',
'169.254.4.0/30',
'169.254.5.0/30',
'169.254.169.252/30',
];
if (cidr in reservedTunnelIP) {
throw new Error(`the Cidr is a reserved range and can not be used, refer: \n
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-vpnconnection-vpntunneloptionsspecification.html"
`);
}
assignedCidrs.push(cidr);
});
}
else if (vpnprops.tunnelIpamPool !== undefined) {
const tunnelAllocation = new ipam_1.GetTunnelAddressPair(this, `${name}tunneladdress`, {
ipamPoolId: vpnprops.tunnelIpamPool.attrIpamPoolId,
name: name,
});
assignedCidrs = tunnelAllocation.assignedCidrPair;
}
const tunnels = [];
assignedCidrs.forEach((cidr) => {
tunnels.push({
// key and tunnel addressing
PreSharedKeyArn: vpnPresharedKey.secretFullArn,
TunnelInsideCidr: cidr,
// Dead peer detection and action
DPDTimeoutAction: vpnprops.vpnspec.dpdTimeoutAction,
DPDTimeoutSeconds: vpnprops.vpnspec.dpdTimeoutSeconds,
// Allowable IKE versions
IKEVersions: makeObject(vpnprops.vpnspec.ikeVersions),
LogOptions: {
CloudWatchLogOptions: cloudWatchLogOptions,
},
// Phase one Configuration
Phase1DHGroupNumbers: makeObject(vpnprops.vpnspec.phase1DHGroupNumbers),
Phase1EncryptionAlgorithms: makeObject(vpnprops.vpnspec.phase1EncryptionAlgorithms),
Phase1IntegrityAlgorithms: makeObject(vpnprops.vpnspec.phase1IntegrityAlgorithms),
Phase1LifetimeSeconds: vpnprops.vpnspec.phase1LifetimeSeconds,
// Phase Two configuration
Phase2DHGroupNumbers: makeObject(vpnprops.vpnspec.phase2DHGroupNumbers),
Phase2EncryptionAlgorithms: makeObject(vpnprops.vpnspec.phase2EncryptionAlgorithms),
Phase2IntegrityAlgorithms: makeObject(vpnprops.vpnspec.phase2IntegrityAlgorithms),
Phase2LifetimeSeconds: vpnprops.vpnspec.phase2LifeTimeSeconds,
// rekeying and windowsizes
RekeyFuzzPercentage: vpnprops.vpnspec.rekeyFuzzPercentage,
RekeyMarginTimeSeconds: vpnprops.vpnspec.rekeyMarginTimeSeconds,
ReplayWindowSize: vpnprops.vpnspec.replayWindowSize,
StartupAction: vpnprops.vpnspec.startupAction,
});
});
// the converison to a base64 string is so that the types of the string can be passed to the lambda without them being converted to a string.
// json supports ints and boleans, but natively the propertys in a lambda dont.
// where the the properties are not plain strings, they need to be packed in base64, and then decoded lambda side.
const props = {
CustomerGatewayId: vpnprops.customerGateway.attrCustomerGatewayId,
Type: 'ipsec.1',
TransitGatewayId: this.transitGateway.attrId,
Name: name,
Options: {
EnableAcceleration: vpnprops.vpnspec.enableAcceleration,
LocalIpv4NetworkCidr: vpnprops.vpnspec.localIpv4NetworkCidr,
RemoteIpv4NetworkCidr: vpnprops.vpnspec.remoteIpv4NetworkCidr,
OutsideIpAddressType: vpnprops.vpnspec.outsideIpAddressType,
StaticRoutesOnly: vpnprops.vpnspec.staticRoutesOnly,
TunnelInsideIpVersion: vpnprops.vpnspec.tunnelInsideIpVersion,
TunnelOptions: tunnels,
TransportTransitGatewayAttachmentId: this.tgDXattachmentId,
},
};
const vpn = new cdk.CustomResource(this, `CreateP2PVPN${name}`, {
serviceToken: createVPNProvider.serviceToken,
properties: {
props64: cdk.Fn.base64(cdk.Stack.of(this).toJsonString(props)),
},
});
if (vpnprops.sampleconfig !== undefined) {
const sampleConfigLambda = new aws_cdk_lib_1.aws_lambda.SingletonFunction(this, `${name}sampleconfig`, {
code: aws_cdk_lib_1.aws_lambda.Code.fromAsset(path.join(__dirname, '../../lambda/cloudwan')),
uuid: 'FFFFAAFFEEDDDDE000',
handler: 'samplevpn.on_event',
runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9,
});
sampleConfigLambda.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({
effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW,
resources: ['*'],
actions: ['ec2:getVpnConnectionDeviceSampleConfiguration'],
}));
vpnprops.sampleconfig.bucket.grantReadWrite(sampleConfigLambda);
// get the parameters from the core.
new cdk.CustomResource(this, `createconfigplaceinbucket${name}`, {
serviceToken: new aws_cdk_lib_1.custom_resources.Provider(this, `NetworkManagerProvider${name}`, {
onEventHandler: sampleConfigLambda,
}).serviceToken,
properties: {
Name: `${name}`,
BucketName: vpnprops.sampleconfig.bucket.bucketName,
VpnConnectionId: vpn.getAtt('VpnConnectionId'),
VpnConnectionDeviceTypeId: vpnprops.sampleconfig.deviceType,
InternetKeyExchangeVersion: vpnprops.sampleconfig.ikeVersion,
},
});
}
}
}
exports.CloudWanTGW = CloudWanTGW;
_a = JSII_RTTI_SYMBOL_1;
CloudWanTGW[_a] = { fqn: "raindancers-network.cloudwan.CloudWanTGW", version: "1.29.3" };
function makeObject(x) {
let object = [];
if (x) {
x.forEach((item) => {
object.push({ Value: item });
});
return object;
}
return null;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xvdWR3YW5UR1cuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xvdWR3YW4vY2xvdWR3YW5UR1cudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSw2QkFBNkI7QUFDN0IsbUNBQW1DO0FBQ25DLDZDQVVxQjtBQUNyQix5Q0FBeUM7QUFHekMsdURBQXVEO0FBQ3ZELHVDQUFvRDtBQUdwRDs7R0FFRztBQUNILE1BQWEsV0FBWSxTQUFRLFVBQVUsQ0FBQyxTQUFTO0lBbUJuRDs7Ozs7U0FLRTtJQUNGLFlBQVksS0FBMkIsRUFBRSxFQUFVLEVBQUUsS0FBMEM7UUFDN0YsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQUVqQixJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUM7UUFDM0Isa0NBQWtDO1FBRWxDLHNCQUFzQjtRQUN0QixNQUFNLGNBQWMsR0FBRyxJQUFJLHdCQUFVLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSw2QkFBNkIsRUFBRTtZQUNsRixPQUFPLEVBQUUsd0JBQVUsQ0FBQyxPQUFPLENBQUMsVUFBVTtZQUN0QyxPQUFPLEVBQUUsd0JBQXdCO1lBQ2pDLElBQUksRUFBRSx3QkFBVSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsdUJBQXVCLENBQUMsQ0FBQztTQUMvRSxDQUFDLENBQUM7UUFFSCxjQUFjLENBQUMsZUFBZSxDQUM1QixJQUFJLHFCQUFHLENBQUMsZUFBZSxDQUFDO1lBQ3RCLE1BQU0sRUFBRSxxQkFBRyxDQUFDLE1BQU0sQ0FBQyxLQUFLO1lBQ3hCLFNBQVMsRUFBRSxDQUFDLEdBQUcsQ0FBQztZQUNoQixPQUFPLEVBQUUsQ0FBQyxpQ0FBaUMsQ0FBQztTQUM3QyxDQUFDLENBQ0gsQ0FBQztRQUVGLG9DQUFvQztRQUNwQyxNQUFNLFdBQVcsR0FBRyxJQUFJLEdBQUcsQ0FBQyxjQUFjLENBQUMs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