UNPKG

@cdklabs/aws-data-solutions-framework

Version:
174 lines 27 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.DataVpc = void 0; const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti"); // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 const aws_cdk_lib_1 = require("aws-cdk-lib"); const aws_ec2_1 = require("aws-cdk-lib/aws-ec2"); const aws_iam_1 = require("aws-cdk-lib/aws-iam"); const aws_kms_1 = require("aws-cdk-lib/aws-kms"); const aws_logs_1 = require("aws-cdk-lib/aws-logs"); const constructs_1 = require("constructs"); const context_1 = require("./context"); /** * Creates a VPC with best practices for securely deploying data solutions. * @see https://awslabs.github.io/data-solutions-framework-on-aws/docs/constructs/library/Utils/data-vpc * * @example * * const vpc = new dsf.utils.DataVpc(this, 'DataVpc', { * vpcCidr: '10.0.0.0/16', * }); * * vpc.tagVpc('Name', 'My VPC'); */ class DataVpc extends constructs_1.Construct { constructor(scope, id, props) { super(scope, id); const removalPolicy = context_1.Context.revertRemovalPolicy(scope, props.removalPolicy); const retention = props.flowLogRetention || aws_logs_1.RetentionDays.ONE_WEEK; this.flowLogKey = props.flowLogKey || new aws_kms_1.Key(this, 'FlowLogKey', { description: 'vpc-logs-key', enableKeyRotation: true, removalPolicy: removalPolicy, }); this.flowLogRole = props.flowLogRole || new aws_iam_1.Role(this, 'FlowLogRole', { assumedBy: new aws_iam_1.ServicePrincipal('vpc-flow-logs.amazonaws.com'), }); const vpcMask = parseInt(props.vpcCidr.split('/')[1]); const smallestVpcCidr = 28; if (vpcMask > smallestVpcCidr) { throw new Error(`The VPC netmask should be at least 28, netmask provided is ${vpcMask}`); } // Calculate subnet masks based on VPC's mask const publicSubnetMask = vpcMask + 4; const privateSubnetMask = publicSubnetMask + 2; // twice as large as public subnet // Calculate the number of NAT gateways based on the number of AZs in the region. // CDK has default behaviour, if region is not detect at synth time it will limit to two AZs // But if a region is detect we might have 4 or 6 AZs // We need to limit to 3 NATs as default since the MAX AZ is 3 let defaultNumberOfNat = aws_cdk_lib_1.Stack.of(this).availabilityZones.length > 3 ? 3 : aws_cdk_lib_1.Stack.of(this).availabilityZones.length; this.vpc = new aws_ec2_1.Vpc(this, 'Vpc', { ipAddresses: aws_ec2_1.IpAddresses.cidr(props.vpcCidr), maxAzs: 3, natGateways: props.natGateways ?? defaultNumberOfNat, subnetConfiguration: [ { cidrMask: publicSubnetMask, name: 'Public', subnetType: aws_ec2_1.SubnetType.PUBLIC, }, { cidrMask: privateSubnetMask, name: 'Private', subnetType: aws_ec2_1.SubnetType.PRIVATE_WITH_EGRESS, }, ], }); //Create VPC flow log for the VPC this.flowLogGroup = new aws_logs_1.LogGroup(scope, 'FLowLogGroup', { encryptionKey: this.flowLogKey, retention, removalPolicy: removalPolicy, }); //Allow vpc flowlog to access KMS key to encrypt logs this.flowLogKey.addToResourcePolicy(new aws_iam_1.PolicyStatement({ effect: aws_iam_1.Effect.ALLOW, principals: [new aws_iam_1.ServicePrincipal(`logs.${aws_cdk_lib_1.Stack.of(scope).region}.amazonaws.com`)], actions: [ 'kms:Encrypt*', 'kms:Decrypt*', 'kms:ReEncrypt*', 'kms:GenerateDataKey*', 'kms:Describe*', ], conditions: { ArnLike: { 'kms:EncryptionContext:aws:logs:arn': `arn:aws:logs:${aws_cdk_lib_1.Stack.of(scope).region}:${aws_cdk_lib_1.Stack.of(scope).account}:*`, }, }, resources: ['*'], })); this.vpc.addFlowLog('FlowLog', { destination: aws_ec2_1.FlowLogDestination.toCloudWatchLogs(this.flowLogGroup, this.flowLogRole), }); // Create a gateway endpoint for S3 this.s3VpcEndpoint = this.vpc.addGatewayEndpoint('S3VpcEndpoint', { service: aws_ec2_1.GatewayVpcEndpointAwsService.S3, }); // Create a Client VPN Endpoint if (props.clientVpnEndpointProps) { [this.vpnSecurityGroups, this.vpnLogGroup, this.clientVpnEndpoint] = this.setupClientVpn(scope, props, removalPolicy, retention); } } /** * Tag the VPC and the subnets * @param key the tag key * @param value the tag value */ tagVpc(key, value) { // Add tags to subnets for (let subnet of [...this.vpc.publicSubnets, ...this.vpc.privateSubnets]) { aws_cdk_lib_1.Tags.of(subnet).add(key, value); } // Add tags to vpc aws_cdk_lib_1.Tags.of(this.vpc).add(key, value); } /** * @internal * Configure Client VPN Endpoint * @param scope current scope * @param props DataVpcProps * @param removalPolicy RemovalPolicy * @param retention RetentionDays for Cloudwatch log group * @returns [ISecurityGroup[], ILogGroup, ClientVpnEndpoint] created ClientVpnEndpoint alongside with security group and log group. */ setupClientVpn(scope, props, removalPolicy, retention) { const vpnSamlProvider = new aws_iam_1.SamlProvider(scope, 'SamlProviderVpnEndpoint', { metadataDocument: aws_iam_1.SamlMetadataDocument.fromXml(props.clientVpnEndpointProps.samlMetadataDocument), }); const endpointProps = { ...props.clientVpnEndpointProps, vpnSubnets: this.vpc.selectSubnets({ onePerAz: true, subnetType: aws_ec2_1.SubnetType.PRIVATE_WITH_EGRESS }), userBasedAuthentication: aws_ec2_1.ClientVpnUserBasedAuthentication.federated(vpnSamlProvider), cidr: this.vpc.publicSubnets[0].ipv4CidrBlock, }; endpointProps.logging ?? (endpointProps.logging = true); endpointProps.transportProtocol ?? (endpointProps.transportProtocol = aws_ec2_1.TransportProtocol.TCP); endpointProps.splitTunnel ?? (endpointProps.splitTunnel = true); endpointProps.dnsServers ?? (endpointProps.dnsServers = [props.vpcCidr.replace(/^(\d+)\.(\d+)\.(\d+)\.\d+\/\d+$/, '$1.$2.$3.2')]); endpointProps.authorizeAllUsersToVpcCidr ?? (endpointProps.authorizeAllUsersToVpcCidr = true); endpointProps.port ?? (endpointProps.port = aws_ec2_1.VpnPort.HTTPS); endpointProps.selfServicePortal ?? (endpointProps.selfServicePortal = true); if (!endpointProps.securityGroups) { const vpnSecurityGroup = new aws_ec2_1.SecurityGroup(scope, 'vpnSecurityGroup', { vpc: this.vpc, allowAllOutbound: false, }); vpnSecurityGroup.addIngressRule(aws_ec2_1.Peer.ipv4(props.vpcCidr), (endpointProps.transportProtocol == aws_ec2_1.TransportProtocol.TCP) ? aws_ec2_1.Port.tcp(endpointProps.port) : aws_ec2_1.Port.udp(endpointProps.port)); vpnSecurityGroup.addEgressRule(aws_ec2_1.Peer.anyIpv4(), aws_ec2_1.Port.tcp(443)); vpnSecurityGroup.applyRemovalPolicy(removalPolicy); endpointProps.securityGroups = [vpnSecurityGroup]; } ; if (!endpointProps.logGroup) { const vpnLogGroup = new aws_logs_1.LogGroup(scope, 'vpnLogGroup', { encryptionKey: this.flowLogKey, retention, removalPolicy: removalPolicy, }); endpointProps.logGroup = vpnLogGroup; } const clientVpnEndpoint = this.vpc.addClientVpnEndpoint('Endpoint', endpointProps); clientVpnEndpoint.applyRemovalPolicy(removalPolicy); return [endpointProps.securityGroups, endpointProps.logGroup, clientVpnEndpoint]; } } exports.DataVpc = DataVpc; _a = JSII_RTTI_SYMBOL_1; DataVpc[_a] = { fqn: "@cdklabs/aws-data-solutions-framework.utils.DataVpc", version: "1.21.3" }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YS12cGMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvdXRpbHMvbGliL2RhdGEtdnBjLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEscUVBQXFFO0FBQ3JFLHNDQUFzQztBQUV0Qyw2Q0FBeUQ7QUFDekQsaURBSzZCO0FBQzdCLGlEQUFpSTtBQUNqSSxpREFBZ0Q7QUFDaEQsbURBQTBFO0FBQzFFLDJDQUF1QztBQUN2Qyx1Q0FBb0M7QUFHcEM7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFhLE9BQVEsU0FBUSxzQkFBUztJQXFDcEMsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUFtQjtRQUUzRCxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBRWpCLE1BQU0sYUFBYSxHQUFHLGlCQUFPLENBQUMsbUJBQW1CLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUU5RSxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsZ0JBQWdCLElBQUksd0JBQWEsQ0FBQyxRQUFRLENBQUM7UUFFbkUsSUFBSSxDQUFDLFVBQVUsR0FBRyxLQUFLLENBQUMsVUFBVSxJQUFJLElBQUksYUFBRyxDQUFDLElBQUksRUFBRSxZQUFZLEVBQUU7WUFDaEUsV0FBVyxFQUFFLGNBQWM7WUFDM0IsaUJBQWlCLEVBQUUsSUFBSTtZQUN2QixhQUFhLEVBQUUsYUFBYTtTQUM3QixDQUFDLENBQUM7UUFFSCxJQUFJLENBQUMsV0FBVyxHQUFHLEtBQUssQ0FBQyxXQUFXLElBQUksSUFBSSxjQUFJLENBQUMsSUFBSSxFQUFFLGFBQWEsRUFBRTtZQUNwRSxTQUFTLEVBQUUsSUFBSSwwQkFBZ0IsQ0FBQyw2QkFBNkIsQ0FBQztTQUMvRCxDQUFDLENBQUM7UUFFSCxNQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFD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IG9yIGl0cyBhZmZpbGlhdGVzLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgUmVtb3ZhbFBvbGljeSwgU3RhY2ssIFRhZ3MgfSBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQge1xuICBDbGllbnRWcG5FbmRwb2ludCwgQ2xpZW50VnBuRW5kcG9pbnRPcHRpb25zLCBDbGllbnRWcG5Vc2VyQmFzZWRBdXRoZW50aWNhdGlvbixcbiAgRmxvd0xvZ0Rlc3RpbmF0aW9uLCBHYXRld2F5VnBjRW5kcG9pbnRBd3NTZXJ2aWNlLCBJR2F0ZXdheVZwY0VuZHBvaW50LFxuICBJU2VjdXJpdHlHcm91cCwgSXBBZGRyZXNzZXMsIFBlZXIsIFBvcnQsIFNlY3VyaXR5R3JvdXAsIFN1Ym5ldFR5cGUsXG4gIFRyYW5zcG9ydFByb3RvY29sLCBWcGMsIFZwblBvcnQsXG59IGZyb20gJ2F3cy1jZGstbGliL2F3cy1lYzInO1xuaW1wb3J0IHsgRWZmZWN0LCBJUm9sZSwgUG9saWN5U3RhdGVtZW50LCBSb2xlLCBTYW1sTWV0YWRhdGFEb2N1bWVudCwgU2FtbFByb3ZpZGVyLCBTZXJ2aWNlUHJpbmNpcGFsIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWlhbSc7XG5pbXBvcnQgeyBJS2V5LCBLZXkgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3Mta21zJztcbmltcG9ydCB7IElMb2dHcm91cCwgTG9nR3JvdXAsIFJldGVudGlvbkRheXMgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtbG9ncyc7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcbmltcG9ydCB7IENvbnRleHQgfSBmcm9tICcuL2NvbnRleHQnO1xuaW1wb3J0IHsgRGF0YVZwY1Byb3BzIH0gZnJvbSAnLi9kYXRhLXZwYy1wcm9wcyc7XG5cbi8qKlxuICogQ3JlYXRlcyBhIFZQQyB3aXRoIGJlc3QgcHJhY3RpY2VzIGZvciBzZWN1cmVseSBkZXBsb3lpbmcgZGF0YSBzb2x1dGlvbnMuXG4gKiBAc2VlIGh0dHBzOi8vYXdzbGFicy5naXRodWIuaW8vZGF0YS1zb2x1dGlvbnMtZnJhbWV3b3JrLW9uLWF3cy9kb2NzL2NvbnN0cnVjdHMvbGlicmFyeS9VdGlscy9kYXRhLXZwY1xuICpcbiAqIEBleGFtcGxlXG4gKlxuICogY29uc3QgdnBjID0gbmV3IGRzZi51dGlscy5EYXRhVnBjKHRoaXMsICdEYXRhVnBjJywge1xuICogICB2cGNDaWRyOiAnMTAuMC4wLjAvMTYnLFxuICogfSk7XG4gKlxuICogdnBjLnRhZ1ZwYygnTmFtZScsICdNeSBWUEMnKTtcbiAqL1xuZXhwb3J0IGNsYXNzIERhdGFWcGMgZXh0ZW5kcyBDb25zdHJ1Y3Qge1xuXG4gIC8qKlxuICAgKiBUaGUgYW1hem9uIFZQQyBjcmVhdGVkXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgdnBjOiBWcGM7XG4gIC8qKlxuICAgKiBUaGUgS01TIEtleSB1c2VkIHRvIGVuY3J5cHQgVlBDIGZsb3cgbG9nc1xuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGZsb3dMb2dLZXk6IElLZXk7XG4gIC8qKlxuICAgKiBUaGUgSUFNIHJvbGUgdXNlZCB0byBwdWJsaXNoIFZQQyBGbG93IExvZ3NcbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBmbG93TG9nUm9sZTogSVJvbGU7XG4gIC8qKlxuICAgKiBUaGUgQ2xvdWRXYXRjaCBMb2cgR3JvdXAgY3JlYXRlZCBmb3IgdGhlIFZQQyBmbG93IGxvZ3NcbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBmbG93TG9nR3JvdXA6IElMb2dHcm91cDtcbiAgLyoqXG4gICAqIFRoZSBTMyBWUEMgZW5kcG9pbnQgZ2F0ZXdheVxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHMzVnBjRW5kcG9pbnQ6IElHYXRld2F5VnBjRW5kcG9pbnQ7XG4gIC8qKlxuICAgKiBUaGUgQ2xpZW50IFZQTiBFbmRwb2ludFxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGNsaWVudFZwbkVuZHBvaW50OiBDbGllbnRWcG5FbmRwb2ludCB8IHVuZGVmaW5lZDtcblxuICAvKipcbiAgICogVGhlIGxvZyBncm91cCBmb3IgQ2xpZW50IFZQTiBFbmRwb2ludFxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHZwbkxvZ0dyb3VwOiBJTG9nR3JvdXAgfCB1bmRlZmluZWQ7XG4gIC8qKlxuICAgKiBUaGUgc2VjdXJpdHkgZ3JvdXAgZm9yIENsaWVudCBWUE4gRW5kcG9pbnRcbiAgICovXG4gIHB1YmxpYyByZWFkb25seSB2cG5TZWN1cml0eUdyb3VwczogSVNlY3VyaXR5R3JvdXBbXSB8IHVuZGVmaW5lZDtcblxuXG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcsIHByb3BzOiBEYXRhVnBjUHJvcHMpIHtcblxuICAgIHN1cGVyKHNjb3BlLCBpZCk7XG5cbiAgICBjb25zdCByZW1vdmFsUG9saWN5ID0gQ29udGV4dC5yZXZlcnRSZW1vdmFsUG9saWN5KHNjb3BlLCBwcm9wcy5yZW1vdmFsUG9saWN5KTtcblxuICAgIGNvbnN0IHJldGVudGlvbiA9IHByb3BzLmZsb3dMb2dSZXRlbnRpb24gfHwgUmV0ZW50aW9uRGF5cy5PTkVfV0VFSztcblxuICAgIHRoaXMuZmxvd0xvZ0tleSA9IHByb3BzLmZsb3dMb2dLZXkgfHwgbmV3IEtleSh0aGlzLCAnRmxvd0xvZ0tleScsIHtcbiAgICAgIGRlc2NyaXB0aW9uOiAndnBjLWxvZ3Mta2V5JyxcbiAgICAgIGVuYWJsZUtleVJvdGF0aW9uOiB0cnVlLFxuICAgICAgcmVtb3ZhbFBvbGljeTogcmVtb3ZhbFBvbGljeSxcbiAgICB9KTtcblxuICAgIHRoaXMuZmxvd0xvZ1JvbGUgPSBwcm9wcy5mbG93TG9nUm9sZSB8fCBuZXcgUm9sZSh0aGlzLCAnRmxvd0xvZ1JvbGUnLCB7XG4gICAgICBhc3N1bWVkQnk6IG5ldyBTZXJ2aWNlUHJpbmNpcGFsKCd2cGMtZmxvdy1sb2dzLmFtYXpvbmF3cy5jb20nKSxcbiAgICB9KTtcblxuICAgIGNvbnN0IHZwY01hc2sgPSBwYXJzZUludChwcm9wcy52cGNDaWRyLnNwbGl0KCcvJylbMV0pO1xuICAgIGNvbnN0IHNtYWxsZXN0VnBjQ2lkcjogbnVtYmVyID0gMjg7XG4gICAgaWYgKHZwY01hc2sgPiBzbWFsbGVzdFZwY0NpZHIpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgVGhlIFZQQyBuZXRtYXNrIHNob3VsZCBiZSBhdCBsZWFzdCAyOCwgbmV0bWFzayBwcm92aWRlZCBpcyAke3ZwY01hc2t9YCk7XG4gICAgfVxuXG5cbiAgICAvLyBDYWxjdWxhdGUgc3VibmV0IG1hc2tzIGJhc2VkIG9uIFZQQydzIG1hc2tcbiAgICBjb25zdCBwdWJsaWNTdWJuZXRNYXNrID0gdnBjTWFzayArIDQ7XG4gICAgY29uc3QgcHJpdmF0ZVN1Ym5ldE1hc2sgPSBwdWJsaWNTdWJuZXRNYXNrICsgMjsgLy8gdHdpY2UgYXMgbGFyZ2UgYXMgcHVibGljIHN1Ym5ldFxuXG4gICAgLy8gQ2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgTkFUIGdhdGV3YXlzIGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgQVpzIGluIHRoZSByZWdpb24uXG4gICAgLy8gQ0RLIGhhcyBkZWZhdWx0IGJlaGF2aW91ciwgaWYgcmVnaW9uIGlzIG5vdCBkZXRlY3QgYXQgc3ludGggdGltZSBpdCB3aWxsIGxpbWl0IHRvIHR3byBBWnNcbiAgICAvLyBCdXQgaWYgYSByZWdpb24gaXMgZGV0ZWN0IHdlIG1pZ2h0IGhhdmUgNCBvciA2IEFac1xuICAgIC8vIFdlIG5lZWQgdG8gbGltaXQgdG8gMyBOQVRzIGFzIGRlZmF1bHQgc2luY2UgdGhlIE1BWCBBWiBpcyAzXG4gICAgbGV0IGRlZmF1bHROdW1iZXJPZk5hdCA9XG4gICAgICBTdGFjay5vZih0aGlzKS5hdmFpbGFiaWxpdHlab25lcy5sZW5ndGggPjMgP1xuICAgICAgICAzIDogU3RhY2sub2YodGhpcykuYXZhaWxhYmlsaXR5Wm9uZXMubGVuZ3RoO1xuXG4gICAgdGhpcy52cGMgPSBuZXcgVnBjKHRoaXMsICdWcGMnLCB7XG4gICAgICBpcEFkZHJlc3NlczogSXBBZGRyZXNzZXMuY2lkcihwcm9wcy52cGNDaWRyKSxcbiAgICAgIG1heEF6czogMyxcbiAgICAgIG5hdEdhdGV3YXlzOiBwcm9wcy5uYXRHYXRld2F5cyA/PyBkZWZhdWx0TnVtYmVyT2ZOYXQsXG4gICAgICBzdWJuZXRDb25maWd1cmF0aW9uOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBjaWRyTWFzazogcHVibGljU3VibmV0TWFzayxcbiAgICAgICAgICBuYW1lOiAnUHVibGljJyxcbiAgICAgICAgICBzdWJuZXRUeXBlOiBTdWJuZXRUeXBlLlBVQkxJQyxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGNpZHJNYXNrOiBwcml2YXRlU3VibmV0TWFzayxcbiAgICAgICAgICBuYW1lOiAnUHJpdmF0ZScsXG4gICAgICAgICAgc3VibmV0VHlwZTogU3VibmV0VHlwZS5QUklWQVRFX1dJVEhfRUdSRVNTLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICB9KTtcblxuICAgIC8vQ3JlYXRlIFZQQyBmbG93IGxvZyBmb3IgdGhlIFZQQ1xuICAgIHRoaXMuZmxvd0xvZ0dyb3VwID0gbmV3IExvZ0dyb3VwKHNjb3BlLCAnRkxvd0xvZ0dyb3VwJywge1xuICAgICAgZW5jcnlwdGlvbktleTogdGhpcy5mbG93TG9nS2V5LFxuICAgICAgcmV0ZW50aW9uLFxuICAgICAgcmVtb3ZhbFBvbGljeTogcmVtb3ZhbFBvbGljeSxcbiAgICB9KTtcblxuICAgIC8vQWxsb3cgdnBjIGZsb3dsb2cgdG8gYWNjZXNzIEtNUyBrZXkgdG8gZW5jcnlwdCBsb2dzXG4gICAgdGhpcy5mbG93TG9nS2V5LmFkZFRvUmVzb3VyY2VQb2xpY3koXG4gICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgZWZmZWN0OiBFZmZlY3QuQUxMT1csXG4gICAgICAgIHByaW5jaXBhbHM6IFtuZXcgU2VydmljZVByaW5jaXBhbChgbG9ncy4ke1N0YWNrLm9mKHNjb3BlKS5yZWdpb259LmFtYXpvbmF3cy5jb21gKV0sXG4gICAgICAgIGFjdGlvbnM6IFtcbiAgICAgICAgICAna21zOkVuY3J5cHQqJyxcbiAgICAgICAgICAna21zOkRlY3J5cHQqJyxcbiAgICAgICAgICAna21zOlJlRW5jcnlwdConLFxuICAgICAgICAgICdrbXM6R2VuZXJhdGVEYXRhS2V5KicsXG4gICAgICAgICAgJ2ttczpEZXNjcmliZSonLFxuICAgICAgICBdLFxuICAgICAgICBjb25kaXRpb25zOiB7XG4gICAgICAgICAgQXJuTGlrZToge1xuICAgICAgICAgICAgJ2ttczpFbmNyeXB0aW9uQ29udGV4dDphd3M6bG9nczphcm4nOiBgYXJuOmF3czpsb2dzOiR7U3RhY2sub2Yoc2NvcGUpLnJlZ2lvbn06JHtTdGFjay5vZihzY29wZSkuYWNjb3VudH06KmAsXG4gICAgICAgICAgfSxcbiAgICAgICAgfSxcbiAgICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICAgIH0pLFxuICAgICk7XG5cbiAgICB0aGlzLnZwYy5hZGRGbG93TG9nKCdGbG93TG9nJywge1xuICAgICAgZGVzdGluYXRpb246IEZsb3dMb2dEZXN0aW5hdGlvbi50b0Nsb3VkV2F0Y2hMb2dzKHRoaXMuZmxvd0xvZ0dyb3VwLCB0aGlzLmZsb3dMb2dSb2xlKSxcbiAgICB9KTtcblxuICAgIC8vIENyZWF0ZSBhIGdhdGV3YXkgZW5kcG9pbnQgZm9yIFMzXG4gICAgdGhpcy5zM1ZwY0VuZHBvaW50PSB0aGlzLnZwYy5hZGRHYXRld2F5RW5kcG9pbnQoJ1MzVnBjRW5kcG9pbnQnLCB7XG4gICAgICBzZXJ2aWNlOiBHYXRld2F5VnBjRW5kcG9pbnRBd3NTZXJ2aWNlLlMzLFxuICAgIH0pO1xuXG4gICAgLy8gQ3JlYXRlIGEgQ2xpZW50IFZQTiBFbmRwb2ludFxuICAgIGlmIChwcm9wcy5jbGllbnRWcG5FbmRwb2ludFByb3BzKSB7XG4gICAgICBbdGhpcy52cG5TZWN1cml0eUdyb3VwcywgdGhpcy52cG5Mb2dHcm91cCwgdGhpcy5jbGllbnRWcG5FbmRwb2ludF0gPSB0aGlzLnNldHVwQ2xpZW50VnBuKHNjb3BlLCBwcm9wcywgcmVtb3ZhbFBvbGljeSwgcmV0ZW50aW9uKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVGFnIHRoZSBWUEMgYW5kIHRoZSBzdWJuZXRzXG4gICAqIEBwYXJhbSBrZXkgdGhlIHRhZyBrZXlcbiAgICogQHBhcmFtIHZhbHVlIHRoZSB0YWcgdmFsdWVcbiAgICovXG4gIHB1YmxpYyB0YWdWcGMoa2V5OiBzdHJpbmcsIHZhbHVlOiBzdHJpbmcpIHtcbiAgICAvLyBBZGQgdGFncyB0byBzdWJuZXRzXG4gICAgZm9yIChsZXQgc3VibmV0IG9mIFsuLi50aGlzLnZwYy5wdWJsaWNTdWJuZXRzLCAuLi50aGlzLnZwYy5wcml2YXRlU3VibmV0c10pIHtcbiAgICAgIFRhZ3Mub2Yoc3VibmV0KS5hZGQoa2V5LCB2YWx1ZSk7XG4gICAgfVxuICAgIC8vIEFkZCB0YWdzIHRvIHZwY1xuICAgIFRhZ3Mub2YodGhpcy52cGMpLmFkZChrZXksIHZhbHVlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAaW50ZXJuYWxcbiAgICogQ29uZmlndXJlIENsaWVudCBWUE4gRW5kcG9pbnRcbiAgICogQHBhcmFtIHNjb3BlIGN1cnJlbnQgc2NvcGVcbiAgICogQHBhcmFtIHByb3BzIERhdGFWcGNQcm9wc1xuICAgKiBAcGFyYW0gcmVtb3ZhbFBvbGljeSBSZW1vdmFsUG9saWN5XG4gICAqIEBwYXJhbSByZXRlbnRpb24gUmV0ZW50aW9uRGF5cyBmb3IgQ2xvdWR3YXRjaCBsb2cgZ3JvdXBcbiAgICogQHJldHVybnMgW0lTZWN1cml0eUdyb3VwW10sIElMb2dHcm91cCwgQ2xpZW50VnBuRW5kcG9pbnRdIGNyZWF0ZWQgQ2xpZW50VnBuRW5kcG9pbnQgYWxvbmdzaWRlIHdpdGggc2VjdXJpdHkgZ3JvdXAgYW5kIGxvZyBncm91cC5cbiAgICovXG4gIHByaXZhdGUgc2V0dXBDbGllbnRWcG4oc2NvcGU6Q29uc3RydWN0LCBwcm9wczpEYXRhVnBjUHJvcHMsIHJlbW92YWxQb2xpY3k6UmVtb3ZhbFBvbGljeSwgcmV0ZW50aW9uOlJldGVudGlvbkRheXMpOlxuICBbSVNlY3VyaXR5R3JvdXBbXSwgSUxvZ0dyb3VwLCBDbGllbnRWcG5FbmRwb2ludF0ge1xuXG4gICAgY29uc3QgdnBuU2FtbFByb3ZpZGVyID0gbmV3IFNhbWxQcm92aWRlcihzY29wZSwgJ1NhbWxQcm92aWRlclZwbkVuZHBvaW50Jywge1xuICAgICAgbWV0YWRhdGFEb2N1bWVudDogU2FtbE1ldGFkYXRhRG9jdW1lbnQuZnJvbVhtbChwcm9wcy5jbGllbnRWcG5FbmRwb2ludFByb3BzIS5zYW1sTWV0YWRhdGFEb2N1bWVudCksXG4gICAgfSk7XG5cbiAgICBjb25zdCBlbmRwb2ludFByb3BzID0ge1xuICAgICAgLi4ucHJvcHMuY2xpZW50VnBuRW5kcG9pbnRQcm9wcyxcbiAgICAgIHZwblN1Ym5ldHM6IHRoaXMudnBjLnNlbGVjdFN1Ym5ldHMoeyBvbmVQZXJBejogdHJ1ZSwgc3VibmV0VHlwZTogU3VibmV0VHlwZS5QUklWQVRFX1dJVEhfRUdSRVNTIH0pLFxuICAgICAgdXNlckJhc2VkQXV0aGVudGljYXRpb246IENsaWVudFZwblVzZXJCYXNlZEF1dGhlbnRpY2F0aW9uLmZlZGVyYXRlZCh2cG5TYW1sUHJvdmlkZXIpLFxuICAgICAgY2lkcjogdGhpcy52cGMucHVibGljU3VibmV0c1swXS5pcHY0Q2lkckJsb2NrLFxuICAgIH07XG5cbiAgICBlbmRwb2ludFByb3BzLmxvZ2dpbmcgPz89IHRydWU7XG4gICAgZW5kcG9pbnRQcm9wcy50cmFuc3BvcnRQcm90b2NvbCA/Pz0gVHJhbnNwb3J0UHJvdG9jb2wuVENQO1xuICAgIGVuZHBvaW50UHJvcHMuc3BsaXRUdW5uZWwgPz89IHRydWU7XG4gICAgZW5kcG9pbnRQcm9wcy5kbnNTZXJ2ZXJzID8/PSBbcHJvcHMudnBjQ2lkci5yZXBsYWNlKC9eKFxcZCspXFwuKFxcZCspXFwuKFxcZCspXFwuXFxkK1xcL1xcZCskLywgJyQxLiQyLiQzLjInKV07XG4gICAgZW5kcG9pbnRQcm9wcy5hdXRob3JpemVBbGxVc2Vyc1RvVnBjQ2lkciA/Pz0gdHJ1ZTtcbiAgICBlbmRwb2ludFByb3BzLnBvcnQgPz89IFZwblBvcnQuSFRUUFM7XG4gICAgZW5kcG9pbnRQcm9wcy5zZWxmU2VydmljZVBvcnRhbCA/Pz0gdHJ1ZTtcblxuICAgIGlmICghZW5kcG9pbnRQcm9wcy5zZWN1cml0eUdyb3Vwcykge1xuICAgICAgY29uc3QgdnBuU2VjdXJpdHlHcm91cCA9IG5ldyBTZWN1cml0eUdyb3VwKHNjb3BlLCAndnBuU2VjdXJpdHlHcm91cCcsIHtcbiAgICAgICAgdnBjOiB0aGlzLnZwYyxcbiAgICAgICAgYWxsb3dBbGxPdXRib3VuZDogZmFsc2UsXG4gICAgICB9KTtcbiAgICAgIHZwblNlY3VyaXR5R3JvdXAuYWRkSW5ncmVzc1J1bGUoXG4gICAgICAgIFBlZXIuaXB2NChwcm9wcy52cGNDaWRyKSxcbiAgICAgICAgKGVuZHBvaW50UHJvcHMudHJhbnNwb3J0UHJvdG9jb2wgPT0gVHJhbnNwb3J0UHJvdG9jb2wuVENQKSA/XG4gICAgICAgICAgUG9ydC50Y3AoZW5kcG9pbnRQcm9wcy5wb3J0KSA6IFBvcnQudWRwKGVuZHBvaW50UHJvcHMucG9ydCksXG4gICAgICApO1xuICAgICAgdnBuU2VjdXJpdHlHcm91cC5hZGRFZ3Jlc3NSdWxlKFBlZXIuYW55SXB2NCgpLCBQb3J0LnRjcCg0NDMpKTtcbiAgICAgIHZwblNlY3VyaXR5R3JvdXAuYXBwbHlSZW1vdmFsUG9saWN5KHJlbW92YWxQb2xpY3kpO1xuICAgICAgZW5kcG9pbnRQcm9wcy5zZWN1cml0eUdyb3VwcyA9IFt2cG5TZWN1cml0eUdyb3VwXTtcbiAgICB9O1xuXG4gICAgaWYgKCFlbmRwb2ludFByb3BzLmxvZ0dyb3VwKSB7XG4gICAgICBjb25zdCB2cG5Mb2dHcm91cCA9IG5ldyBMb2dHcm91cChzY29wZSwgJ3ZwbkxvZ0dyb3VwJywge1xuICAgICAgICBlbmNyeXB0aW9uS2V5OiB0aGlzLmZsb3dMb2dLZXksXG4gICAgICAgIHJldGVudGlvbixcbiAgICAgICAgcmVtb3ZhbFBvbGljeTogcmVtb3ZhbFBvbGljeSxcbiAgICAgIH0pO1xuICAgICAgZW5kcG9pbnRQcm9wcy5sb2dHcm91cCA9IHZwbkxvZ0dyb3VwO1xuICAgIH1cblxuICAgIGNvbnN0IGNsaWVudFZwbkVuZHBvaW50ID0gdGhpcy52cGMuYWRkQ2xpZW50VnBuRW5kcG9pbnQoJ0VuZHBvaW50JywgZW5kcG9pbnRQcm9wcyBhcyBDbGllbnRWcG5FbmRwb2ludE9wdGlvbnMpO1xuXG4gICAgY2xpZW50VnBuRW5kcG9pbnQuYXBwbHlSZW1vdmFsUG9saWN5KHJlbW92YWxQb2xpY3kpO1xuXG4gICAgcmV0dXJuIFtlbmRwb2ludFByb3BzLnNlY3VyaXR5R3JvdXBzLCBlbmRwb2ludFByb3BzLmxvZ0dyb3VwLCBjbGllbnRWcG5FbmRwb2ludF07XG4gIH1cbn0iXX0=