@cdklabs/aws-data-solutions-framework
Version:
L3 CDK Constructs used to build data solutions with AWS
174 lines • 27 kB
JavaScript
"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=