├── img.png ├── .npmignore ├── aws-client-vpn.png ├── .gitignore ├── .idea ├── .gitignore ├── vcs.xml ├── modules.xml └── awscdk-client-vpn-endpoint.iml ├── jest.config.js ├── test └── awscdk-client-vpn-endpoint.test.ts ├── tsconfig.json ├── package.json ├── cdk.json ├── bin └── awscdk-client-vpn-endpoint.ts ├── lib ├── client-vp-endpoint-props.ts └── awscdk-client-vpn-endpoint-stack.ts ├── README.md ├── config └── client-vpn-config.ts └── cdk.context.json /img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shashimal/awscdk-client-vpn-endpoint/HEAD/img.png -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /aws-client-vpn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shashimal/awscdk-client-vpn-endpoint/HEAD/aws-client-vpn.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | !jest.config.js 3 | *.d.ts 4 | node_modules 5 | 6 | # CDK asset staging directory 7 | .cdk.staging 8 | cdk.out 9 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/awscdk-client-vpn-endpoint.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/awscdk-client-vpn-endpoint.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as AwscdkClientVpnEndpoint from '../lib/awscdk-client-vpn-endpoint-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new AwscdkClientVpnEndpoint.AwscdkClientVpnEndpointStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2018" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "typeRoots": [ 23 | "./node_modules/@types" 24 | ] 25 | }, 26 | "exclude": [ 27 | "node_modules", 28 | "cdk.out" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awscdk-client-vpn-endpoint", 3 | "version": "0.1.0", 4 | "bin": { 5 | "awscdk-client-vpn-endpoint": "bin/awscdk-client-vpn-endpoint.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@aws-cdk/assert": "1.117.0", 15 | "@types/jest": "^26.0.10", 16 | "@types/node": "10.17.27", 17 | "aws-cdk": "1.117.0", 18 | "jest": "^26.4.2", 19 | "ts-jest": "^26.2.0", 20 | "ts-node": "^9.0.0", 21 | "typescript": "~3.9.7" 22 | }, 23 | "dependencies": { 24 | "@aws-cdk/aws-ec2": "^1.117.0", 25 | "@aws-cdk/aws-logs": "^1.117.0", 26 | "@aws-cdk/core": "1.117.0", 27 | "source-map-support": "^0.5.16" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/awscdk-client-vpn-endpoint.ts", 3 | "context": { 4 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 5 | "@aws-cdk/core:enableStackNameDuplicates": "true", 6 | "aws-cdk:enableDiffNoFail": "true", 7 | "@aws-cdk/core:stackRelativeExports": "true", 8 | "@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true, 9 | "@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true, 10 | "@aws-cdk/aws-kms:defaultKeyPolicies": true, 11 | "@aws-cdk/aws-s3:grantWriteWithoutAcl": true, 12 | "@aws-cdk/aws-ecs-patterns:removeDefaultDesiredCount": true, 13 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 14 | "@aws-cdk/aws-efs:defaultEncryptionAtRest": true, 15 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 16 | "appName": "ClientVpnEndpoint" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /bin/awscdk-client-vpn-endpoint.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from '@aws-cdk/core'; 4 | import { AwscdkClientVpnEndpointStack } from '../lib/awscdk-client-vpn-endpoint-stack'; 5 | 6 | const app = new cdk.App(); 7 | new AwscdkClientVpnEndpointStack(app, 'AwscdkClientVpnEndpointStack', { 8 | /* If you don't specify 'env', this stack will be environment-agnostic. 9 | * Account/Region-dependent features and context lookups will not work, 10 | * but a single synthesized template can be deployed anywhere. */ 11 | 12 | /* Uncomment the next line to specialize this stack for the AWS Account 13 | * and Region that are implied by the current CLI configuration. */ 14 | // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, 15 | 16 | /* Uncomment the next line if you know exactly what Account and Region you 17 | * want to deploy the stack to. */ 18 | env: { account: '123456', region: 'us-east-1' }, 19 | 20 | /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ 21 | }); 22 | -------------------------------------------------------------------------------- /lib/client-vp-endpoint-props.ts: -------------------------------------------------------------------------------- 1 | export interface IClientVpnEndpointProps { 2 | name: string; 3 | description?: string; 4 | clientCidr: string; 5 | serverCertificateArn: string; 6 | associatedVpcId: string; 7 | associatedSubnets: string[]; 8 | splitTunnel? : boolean; 9 | securityGroups? : ISecurityGroupRule[]; 10 | authorizationRules? : IAuthorizationRule[]; 11 | routes? : IRoutes[]; 12 | vpcPeering?: boolean; 13 | clientCertificateArn?: string; 14 | activeDirectoryId?: string; 15 | vpcPeeringConnections?: IVpcPeeringConnection[]; 16 | enabledLogs: boolean 17 | } 18 | 19 | export interface ISecurityGroupRule { 20 | name: string; 21 | ingressRules: ISecurityGroupIngressRule[]; 22 | } 23 | 24 | export interface IAuthorizationRule { 25 | name: string; 26 | destinationCIDR: string; 27 | adGroupSid?: string; 28 | } 29 | 30 | export interface IRoutes { 31 | name: string; 32 | destinationCIDR: string; 33 | targetSubnetId: string; 34 | } 35 | 36 | export interface IVpcPeeringConnection { 37 | name: string; 38 | crossAccount?: boolean; 39 | vpcId: string; 40 | peerVpcId: string; 41 | peerOwnerId: string; 42 | peerRegion: string; 43 | peerRoleArn?: string; 44 | tags?: ITag[]; 45 | } 46 | 47 | export interface ISecurityGroupIngressRule { 48 | type: string; 49 | port: number; 50 | source: string; 51 | description?: string; 52 | } 53 | 54 | export interface ITag { 55 | key: string; 56 | value: string; 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Client VPN Endpoint (AWS CDK) 2 | 3 | Purpose of this project is to automate the "Client VPN Endpoint" creation by using AWS CDK 4 | 5 | AWS Client VPN is a managed client-based VPN service that enables you to securely access your AWS resources and resources in your on-premises network. With Client VPN, you can access your resources from any location using an OpenVPN-based VPN client. 6 | [more](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/what-is.html) 7 | 8 | # Architecture 9 | ![AWS Client VPN endpoint](aws-client-vpn.png?raw=true) 10 | 11 | ## Configuration 12 | IClientVpnEndpointProps has to be implemented with required parameters. 13 | You can find the config file in **config/client-vpn-config**. 14 | Provide your implementation based on your requirement. For example, 15 | ``` 16 | /** 17 | * Name of the client VPN endpoint 18 | * 19 | */ 20 | name: "ClientVpnEndpoint", 21 | 22 | /** 23 | * VPN endpoint is going to associate with this VPC 24 | * 25 | */ 26 | associatedVpcId: "vpc-0f3827efe79059937", 27 | 28 | /** 29 | * Associated subnets for the VPN endpoint 30 | * 31 | */ 32 | associatedSubnets: ["subnet-078c62a9f0a24b41d", "subnet-0e890c9543fdd23ee"], 33 | 34 | /** 35 | * Client CIDR 36 | * 37 | */ 38 | clientCidr: "20.0.0.0/16", 39 | 40 | /** 41 | * Server certificate 42 | * 43 | */ 44 | serverCertificateArn: "arn:aws:acm:us-east-1:000000:certificate/6540019e-faf8-4237-9f6f-cd3af6433f31", 45 | 46 | /** 47 | * Client certificate 48 | * 49 | */ 50 | clientCertificateArn: "arn:aws:acm:us-east-1:000000:certificate/00bfb760-905e-4bcb-acb0-ae6618b0be9a", 51 | ``` 52 | 53 | ## Useful commands 54 | 55 | * `npm run build` compile typescript to js 56 | * `npm run watch` watch for changes and compile 57 | * `npm run test` perform the jest unit tests 58 | * `cdk deploy` deploy this stack to your default AWS account/region 59 | * `cdk diff` compare deployed stack with current state 60 | * `cdk synth` emits the synthesized CloudFormation template 61 | -------------------------------------------------------------------------------- /config/client-vpn-config.ts: -------------------------------------------------------------------------------- 1 | import {IClientVpnEndpointProps} from "../lib/client-vp-endpoint-props"; 2 | 3 | export const ClientVpnConfig: IClientVpnEndpointProps = { 4 | /** 5 | * Name of the client VPN endpoint 6 | * 7 | */ 8 | name: "ClientVpnEndpoint", 9 | 10 | /** 11 | * Description of the client VPN endpoint 12 | * 13 | */ 14 | description: "VPN for accessing private resources", 15 | 16 | /** 17 | * VPN endpoint is going to associate with this VPC 18 | * 19 | */ 20 | associatedVpcId: "vpc-0f3827efe79059937", 21 | 22 | 23 | /** 24 | * Associated subnets for the VPN endpoint 25 | * 26 | */ 27 | associatedSubnets: ["subnet-078c62a9f0a24b41d", "subnet-0e890c9543fdd23ee"], 28 | 29 | /** 30 | * Client CIDR 31 | * 32 | */ 33 | clientCidr: "20.0.0.0/16", 34 | 35 | /** 36 | * Server certificate 37 | * 38 | */ 39 | serverCertificateArn: "arn:aws:acm:us-east-1:1234560000:certificate/6540019e-faf8-4237-9f6f-cd3af6433f31", 40 | 41 | /** 42 | * Client certificate 43 | * 44 | */ 45 | clientCertificateArn: "arn:aws:acm:us-east-1:1234560000:certificate/00bfb760-905e-4bcb-acb0-ae6618b0be9a", 46 | 47 | 48 | /** 49 | * CloudWatch log group 50 | * 51 | */ 52 | enabledLogs: true, 53 | 54 | /** 55 | * Enable or disable split tunnel for the VPN 56 | * 57 | */ 58 | splitTunnel: true, 59 | 60 | /** 61 | * Security groups for the VPN 62 | * 63 | */ 64 | securityGroups: [ 65 | { 66 | name: "ClientVPN-SecurityGroup", 67 | ingressRules: [ 68 | { 69 | type: "SSH", 70 | port: 22, 71 | source: "0.0.0.0/0", 72 | description: "SSH Access" 73 | } 74 | ] 75 | } 76 | ], 77 | 78 | /** 79 | * Authorization rules for the destination networks 80 | * 81 | */ 82 | authorizationRules: [ 83 | { 84 | "name": "DEV-PROD-Users", 85 | "destinationCIDR": "10.17.0.0/16", 86 | "adGroupSid": "" 87 | } 88 | ], 89 | 90 | /** 91 | * Authorization route table configurations for the destination networks 92 | * 93 | */ 94 | routes: [ 95 | { 96 | "name": "DEV-PROD-Route-1", 97 | "destinationCIDR": "10.17.0.0/16", 98 | "targetSubnetId": "subnet-0e890c9543fdd23ee", 99 | }, 100 | { 101 | "name": "DEV-PROD-Route-2", 102 | "destinationCIDR": "10.17.0.0/16", 103 | "targetSubnetId": "subnet-078c62a9f0a24b41d", 104 | } 105 | ], 106 | 107 | /** 108 | * VPN VPC has peered with other VPCs 109 | * 110 | */ 111 | vpcPeering: true, 112 | 113 | /** 114 | * Peered VPC connections with this VPN endpoint 115 | * crossAccount: Peering between two accounts 116 | * vpcId: Requester VPC 117 | * peerVpcId: Accepter VPC 118 | * peerOwnerId: Accepter account id 119 | * peerRoleArn: Cross account role ARN 120 | */ 121 | vpcPeeringConnections: 122 | [ 123 | { 124 | name: "DEV-PRD", 125 | crossAccount: true, 126 | vpcId: "vpc-0f3827efe79059937", 127 | peerVpcId: "vpc-037f694b5d0f94674", 128 | peerOwnerId: "549560551218", 129 | peerRegion: "us-east-1", 130 | peerRoleArn: "arn:aws:iam::20000000:role/CrossAccountPeeringRole", 131 | tags: [ 132 | { 133 | key: "Name", 134 | value: "DEV-PRD" 135 | } 136 | ] 137 | } 138 | ] 139 | } -------------------------------------------------------------------------------- /cdk.context.json: -------------------------------------------------------------------------------- 1 | { 2 | "vpc-provider:account=793209430381:filter.vpc-id=vpc-b9dbb0c4:region=us-east-1:returnAsymmetricSubnets=true": { 3 | "vpcId": "vpc-b9dbb0c4", 4 | "vpcCidrBlock": "172.31.0.0/16", 5 | "availabilityZones": [], 6 | "subnetGroups": [ 7 | { 8 | "name": "Public", 9 | "type": "Public", 10 | "subnets": [ 11 | { 12 | "subnetId": "subnet-213bd56d", 13 | "cidr": "172.31.16.0/20", 14 | "availabilityZone": "us-east-1a", 15 | "routeTableId": "rtb-01e01170" 16 | }, 17 | { 18 | "subnetId": "subnet-f9441fa6", 19 | "cidr": "172.31.32.0/20", 20 | "availabilityZone": "us-east-1b", 21 | "routeTableId": "rtb-01e01170" 22 | }, 23 | { 24 | "subnetId": "subnet-b77826d1", 25 | "cidr": "172.31.0.0/20", 26 | "availabilityZone": "us-east-1c", 27 | "routeTableId": "rtb-01e01170" 28 | }, 29 | { 30 | "subnetId": "subnet-b482a795", 31 | "cidr": "172.31.80.0/20", 32 | "availabilityZone": "us-east-1d", 33 | "routeTableId": "rtb-01e01170" 34 | }, 35 | { 36 | "subnetId": "subnet-0792f036", 37 | "cidr": "172.31.48.0/20", 38 | "availabilityZone": "us-east-1e", 39 | "routeTableId": "rtb-01e01170" 40 | }, 41 | { 42 | "subnetId": "subnet-5e233350", 43 | "cidr": "172.31.64.0/20", 44 | "availabilityZone": "us-east-1f", 45 | "routeTableId": "rtb-01e01170" 46 | } 47 | ] 48 | } 49 | ] 50 | }, 51 | "vpc-provider:account=793209430381:filter.vpc-id=vpc-0f3827efe79059937:region=us-east-1:returnAsymmetricSubnets=true": { 52 | "vpcId": "vpc-0f3827efe79059937", 53 | "vpcCidrBlock": "10.16.0.0/16", 54 | "availabilityZones": [], 55 | "subnetGroups": [ 56 | { 57 | "name": "Private", 58 | "type": "Private", 59 | "subnets": [ 60 | { 61 | "subnetId": "subnet-0e890c9543fdd23ee", 62 | "cidr": "10.16.16.0/20", 63 | "availabilityZone": "us-east-1a", 64 | "routeTableId": "rtb-069bb4c7e6d4f07ab" 65 | }, 66 | { 67 | "subnetId": "subnet-01cde2dc1a9cbe0cd", 68 | "cidr": "10.16.48.0/20", 69 | "availabilityZone": "us-east-1a", 70 | "routeTableId": "rtb-069bb4c7e6d4f07ab" 71 | }, 72 | { 73 | "subnetId": "subnet-03cdceb59dea2b4dd", 74 | "cidr": "10.16.0.0/20", 75 | "availabilityZone": "us-east-1a", 76 | "routeTableId": "rtb-069bb4c7e6d4f07ab" 77 | }, 78 | { 79 | "subnetId": "subnet-0355fa95b106cd4c0", 80 | "cidr": "10.16.32.0/20", 81 | "availabilityZone": "us-east-1a", 82 | "routeTableId": "rtb-069bb4c7e6d4f07ab" 83 | }, 84 | { 85 | "subnetId": "subnet-078c62a9f0a24b41d", 86 | "cidr": "10.16.80.0/20", 87 | "availabilityZone": "us-east-1b", 88 | "routeTableId": "rtb-069bb4c7e6d4f07ab" 89 | }, 90 | { 91 | "subnetId": "subnet-0e6af15792830dbc8", 92 | "cidr": "10.16.112.0/20", 93 | "availabilityZone": "us-east-1b", 94 | "routeTableId": "rtb-069bb4c7e6d4f07ab" 95 | }, 96 | { 97 | "subnetId": "subnet-0946a328d6436c69d", 98 | "cidr": "10.16.64.0/20", 99 | "availabilityZone": "us-east-1b", 100 | "routeTableId": "rtb-069bb4c7e6d4f07ab" 101 | }, 102 | { 103 | "subnetId": "subnet-0ddd53000bcb46498", 104 | "cidr": "10.16.96.0/20", 105 | "availabilityZone": "us-east-1b", 106 | "routeTableId": "rtb-069bb4c7e6d4f07ab" 107 | }, 108 | { 109 | "subnetId": "subnet-0723ac45e54153386", 110 | "cidr": "10.16.160.0/20", 111 | "availabilityZone": "us-east-1c", 112 | "routeTableId": "rtb-069bb4c7e6d4f07ab" 113 | }, 114 | { 115 | "subnetId": "subnet-0cf4b8d011b142d38", 116 | "cidr": "10.16.128.0/20", 117 | "availabilityZone": "us-east-1c", 118 | "routeTableId": "rtb-069bb4c7e6d4f07ab" 119 | }, 120 | { 121 | "subnetId": "subnet-0be1177e4e36f6fe9", 122 | "cidr": "10.16.176.0/20", 123 | "availabilityZone": "us-east-1c", 124 | "routeTableId": "rtb-069bb4c7e6d4f07ab" 125 | }, 126 | { 127 | "subnetId": "subnet-0ae868910b0b80640", 128 | "cidr": "10.16.144.0/20", 129 | "availabilityZone": "us-east-1c", 130 | "routeTableId": "rtb-069bb4c7e6d4f07ab" 131 | } 132 | ] 133 | } 134 | ] 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /lib/awscdk-client-vpn-endpoint-stack.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from '@aws-cdk/core'; 2 | import * as ec2 from '@aws-cdk/aws-ec2'; 3 | import {ClientVpnConfig} from "../config/client-vpn-config"; 4 | import {ILogGroup, LogGroup, RetentionDays} from "@aws-cdk/aws-logs"; 5 | import {ISecurityGroupRule} from "./client-vp-endpoint-props"; 6 | 7 | export class AwscdkClientVpnEndpointStack extends cdk.Stack { 8 | 9 | private readonly appName: string; 10 | private readonly vpc: ec2.IVpc; 11 | private vpnEndpoint: ec2.ClientVpnEndpoint; 12 | 13 | constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { 14 | super(scope, id, props); 15 | 16 | this.appName = this.node.tryGetContext("appName"); 17 | 18 | this.vpc = ec2.Vpc.fromLookup(this, `${this.appName}-${ClientVpnConfig.associatedVpcId}`, { 19 | vpcId: ClientVpnConfig.associatedVpcId 20 | }); 21 | 22 | //Create VPC peering connections if the peering is enabled 23 | if (ClientVpnConfig.vpcPeering && ClientVpnConfig.vpcPeeringConnections) { 24 | this.createVpcPeeringConnections(); 25 | } 26 | 27 | //Create the client VPN endpoint 28 | this.vpnEndpoint = this.createClientVpnEndpoint(); 29 | 30 | //Configure the VPN endpoint 31 | this.configureClientVpnEndpoint(); 32 | } 33 | 34 | /** 35 | * Create client VPN endpoint 36 | * 37 | */ 38 | private createClientVpnEndpoint = (): ec2.ClientVpnEndpoint => { 39 | return new ec2.ClientVpnEndpoint(this, `${this.appName}`, { 40 | description: ClientVpnConfig.description, 41 | vpc: this.vpc, 42 | cidr: ClientVpnConfig.clientCidr, 43 | serverCertificateArn: ClientVpnConfig.serverCertificateArn, 44 | splitTunnel: ClientVpnConfig.splitTunnel, 45 | vpcSubnets: { 46 | subnets: this.getAssociatedSubnets() 47 | }, 48 | ...(ClientVpnConfig.enabledLogs && {logGroup: this.createLogGroup()}), 49 | ...(ClientVpnConfig.securityGroups && {securityGroups: this.getSecurityGroups()}), 50 | ...(ClientVpnConfig.clientCertificateArn != null && { clientCertificateArn: ClientVpnConfig.clientCertificateArn }), 51 | ...(ClientVpnConfig.activeDirectoryId != null && { userBasedAuthentication: ec2.ClientVpnUserBasedAuthentication.activeDirectory(ClientVpnConfig.activeDirectoryId) }) 52 | }); 53 | } 54 | 55 | /** 56 | * 57 | Configure the client vpn endpoint 58 | */ 59 | private configureClientVpnEndpoint = () => { 60 | this.addAuthorizationRules(); 61 | this.addDestinationRoutes(); 62 | } 63 | 64 | /** 65 | * 66 | Add authorization rules 67 | */ 68 | private addAuthorizationRules = () => { 69 | if (ClientVpnConfig.authorizationRules && ClientVpnConfig.authorizationRules.length > 0) { 70 | for (let rule of ClientVpnConfig.authorizationRules) { 71 | rule.destinationCIDR != null && 72 | this.vpnEndpoint.addAuthorizationRule(rule.name, { 73 | cidr: rule.destinationCIDR, 74 | ...(rule.adGroupSid && { groupId: rule.adGroupSid }), 75 | }); 76 | } 77 | } 78 | } 79 | 80 | /** 81 | * Add destination routes for the VPN 82 | * 83 | */ 84 | private addDestinationRoutes = () => { 85 | if (ClientVpnConfig.routes && ClientVpnConfig.routes.length > 0) { 86 | for (let route of ClientVpnConfig.routes) { 87 | route.destinationCIDR && this.vpnEndpoint.addRoute(route.name, { 88 | cidr: route.destinationCIDR, 89 | target: ec2.ClientVpnRouteTarget.subnet(ec2.Subnet.fromSubnetId(this, route.name, route.targetSubnetId)), 90 | description: `${route.name}-${route.targetSubnetId}` 91 | }); 92 | } 93 | } 94 | } 95 | 96 | /** 97 | * Create log group for the vpn logs 98 | * 99 | */ 100 | private createLogGroup = (): ILogGroup => { 101 | return new LogGroup(this, `${this.appName}-LogGroup`, { 102 | logGroupName: `${this.appName}-Logs`, 103 | retention: RetentionDays.INFINITE 104 | }); 105 | } 106 | 107 | /** 108 | * Client security groups for the client vpn endpoint 109 | * 110 | */ 111 | private getSecurityGroups = (): ec2.ISecurityGroup[] => { 112 | let securityGroups: ec2.ISecurityGroup[] = []; 113 | if (ClientVpnConfig.securityGroups) { 114 | for (let securityGroupRule of ClientVpnConfig.securityGroups) { 115 | securityGroups.push(this.createSecurityGroup(securityGroupRule)); 116 | } 117 | } 118 | return securityGroups; 119 | } 120 | 121 | /** 122 | * Create security groups for the client vpn endpoint 123 | * 124 | */ 125 | private createSecurityGroup = (rule: ISecurityGroupRule): ec2.ISecurityGroup => { 126 | const securityGroup = new ec2.SecurityGroup(this, `${this.appName}-${rule.name}`, { 127 | vpc: this.vpc, 128 | allowAllOutbound: true, 129 | }) 130 | 131 | for (let ingressRule of rule.ingressRules) { 132 | securityGroup.addIngressRule(ec2.Peer.ipv4(ingressRule.source), ec2.Port.tcp(ingressRule.port), ingressRule.description); 133 | } 134 | return securityGroup; 135 | } 136 | 137 | /** 138 | * 139 | Create VPC peering connections 140 | */ 141 | private createVpcPeeringConnections = () => { 142 | if (ClientVpnConfig.vpcPeeringConnections) { 143 | const connections = ClientVpnConfig.vpcPeeringConnections; 144 | for (let connection of connections) { 145 | const peeringConnection = new ec2.CfnVPCPeeringConnection(this, `${this.appName}-${connection.name}`, { 146 | vpcId: connection.vpcId, 147 | peerVpcId: connection.peerVpcId, 148 | peerRegion: connection.peerRegion, 149 | ...(connection.crossAccount && {peerOwnerId: connection.peerOwnerId}), 150 | ...(connection.crossAccount && {peerRoleArn: connection.peerRoleArn}), 151 | tags: connection.tags 152 | }); 153 | } 154 | } 155 | } 156 | 157 | /** 158 | * 159 | * Get associated subnets of the VPN 160 | */ 161 | private getAssociatedSubnets = (): ec2.ISubnet[] => { 162 | const subnets: ec2.ISubnet[] = []; 163 | if (ClientVpnConfig.associatedSubnets) { 164 | for (let subnetId of ClientVpnConfig.associatedSubnets) { 165 | subnets.push(ec2.Subnet.fromSubnetId(this, `${this.appName}-${subnetId}`, subnetId)); 166 | } 167 | } 168 | return subnets; 169 | } 170 | } --------------------------------------------------------------------------------