UNPKG

cdk-tailscale-bastion

Version:

Deploys a AWS Bastion Host preconfigured for Tailscale access

76 lines 20.1 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.TailscaleBastion = void 0; const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti"); const aws_cdk_lib_1 = require("aws-cdk-lib"); const aws_ec2_1 = require("aws-cdk-lib/aws-ec2"); const constructs_1 = require("constructs"); class TailscaleBastion extends constructs_1.Construct { constructor(scope, id, props) { super(scope, id); const { tailscaleCredentials, vpc, availabilityZone, instanceName, subnetSelection, securityGroup, instanceType, additionalInit, incomingRoutes, advertiseRoute, cpuType, cachedInContext, ami, } = props; const authKeyCommand = this.computeTsKeyCli(tailscaleCredentials); const machineImage = ami ?? aws_ec2_1.MachineImage.latestAmazonLinux2023({ cpuType: cpuType ?? aws_ec2_1.AmazonLinuxCpuType.X86_64, cachedInContext: cachedInContext ?? false, }); const bastion = new aws_ec2_1.BastionHostLinux(this, 'BastionHost', { vpc, availabilityZone, instanceName: instanceName ?? 'BastionHostTailscale', securityGroup, instanceType, machineImage, subnetSelection: subnetSelection ?? { subnetType: aws_ec2_1.SubnetType.PUBLIC }, init: aws_ec2_1.CloudFormationInit.fromElements( // Configure IP forwarding aws_ec2_1.InitCommand.shellCommand('echo \'net.ipv4.ip_forward = 1\' | sudo tee -a /etc/sysctl.conf'), aws_ec2_1.InitCommand.shellCommand('echo \'net.ipv6.conf.all.forwarding = 1\' | sudo tee -a /etc/sysctl.conf'), aws_ec2_1.InitCommand.shellCommand('sudo sysctl -p /etc/sysctl.conf'), // Configure Amazon Linux 2023 DNS https://github.com/tailscale/tailscale/issues/7816 aws_ec2_1.InitCommand.shellCommand('mkdir -p /etc/systemd/resolved.conf.d'), aws_ec2_1.InitCommand.shellCommand('ln -sf /dev/null /etc/systemd/resolved.conf.d/resolved-disable-stub-listener.conf'), aws_ec2_1.InitCommand.shellCommand('ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf'), // Install Tailscale aws_ec2_1.InitCommand.shellCommand('dnf config-manager --add-repo https://pkgs.tailscale.com/stable/amazon-linux/2023/tailscale.repo'), // Protect against a potential conflict with AWS activity aws_ec2_1.InitCommand.shellCommand('until dnf -y install tailscale ; do sleep 10s ; done'), aws_ec2_1.InitCommand.shellCommand('systemctl enable --now tailscaled'), aws_ec2_1.InitCommand.shellCommand(`echo TS_AUTHKEY=${authKeyCommand} >> /etc/environment`), aws_ec2_1.InitCommand.shellCommand(`source /etc/environment && tailscale up --authkey $TS_AUTHKEY --advertise-routes=${advertiseRoute ?? vpc.vpcCidrBlock} --accept-routes --accept-dns=false`), ...(additionalInit ?? [])), initOptions: {}, }); if (tailscaleCredentials.secretsManager) { tailscaleCredentials.secretsManager.secret.grantRead(bastion); } bastion.connections.allowFromAnyIpv4(aws_ec2_1.Port.udp(41641), 'Tailscale IPv4'); bastion.connections.allowFrom(aws_ec2_1.Peer.anyIpv6(), aws_ec2_1.Port.udp(41641), 'Tailscale IPv6'); const splitAddress = aws_cdk_lib_1.Fn.split('/', props.vpc.vpcCidrBlock, 2)[0]; const splitIp = aws_cdk_lib_1.Fn.split('.', splitAddress, 4); const dnsServer = `${splitIp[0]}.${splitIp[1]}.${splitIp[2]}.2`; new aws_cdk_lib_1.CfnOutput(this, 'Vpc-Dns-Nameserver', { value: dnsServer }); const stack = aws_cdk_lib_1.Stack.of(this); const domain = aws_cdk_lib_1.Token.isUnresolved(stack.region) ? 'compute.internal' : `${stack.region}.compute.internal`; new aws_cdk_lib_1.CfnOutput(this, 'Vpc-Dns-Domain', { value: domain }); for (const incomingRoute of incomingRoutes ?? []) { for (const subnet of vpc.privateSubnets) { new aws_ec2_1.CfnRoute(subnet, `TailscaleRoute-${incomingRoute}`, { routeTableId: subnet.routeTable.routeTableId, destinationCidrBlock: incomingRoute, instanceId: bastion.instanceId, }); } } this.bastion = bastion; } computeTsKeyCli(credentials) { if (credentials.unsafeString) { return credentials.unsafeString; } else if (credentials.secretsManager) { const sm = credentials.secretsManager; return `$(aws secretsmanager get-secret-value --region ${sm.secret.env.region} --secret-id ${sm.secret.secretArn} --query SecretString --output text | jq '."${sm.key}"')`; } else { throw new Error('No Tailscale credentials set'); } } } exports.TailscaleBastion = TailscaleBastion; _a = JSII_RTTI_SYMBOL_1; TailscaleBastion[_a] = { fqn: "cdk-tailscale-bastion.TailscaleBastion", version: "2.3.0" }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSw2Q0FBMEQ7QUFDMUQsaURBZ0I2QjtBQUU3QiwyQ0FBdUM7QUF5R3ZDLE1BQWEsZ0JBQWlCLFNBQVEsc0JBQVM7SUFFN0MsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUE0QjtRQUNwRSxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBRWpCLE1BQU0sRUFDSixvQkFBb0IsRUFDcEIsR0FBRyxFQUNILGdCQUFnQixFQUNoQixZQUFZLEVBQ1osZUFBZSxFQUNmLGFBQWEsRUFDYixZQUFZLEVBQ1osY0FBYyxFQUNkLGNBQWMsRUFDZCxjQUFjLEVBQ2QsT0FBTyxFQUNQLGVBQWUsRUFDZixHQUFHLEdBQ0osR0FBRyxLQUFLLENBQUM7UUFFVixNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLG9CQUFvQixDQUFDLENBQUM7UUFDbEUsTUFBTSxZQUFZLEdBQUcsR0FBRyxJQUFJLHNCQUFZLENBQUMscUJBQXFCLENBQUM7WUFDN0QsT0FBTyxFQUFFLE9BQU8sSUFBSSw0QkFBa0IsQ0FBQyxNQUFNO1lBQzdDLGVBQWUsRUFBRSxlQUFlLElBQUksS0FBSztTQUMxQyxDQUFDLENBQUM7UUFFSCxNQUFNLE9BQU8sR0FBRyxJQUFJLDBCQUFnQixDQUFDLElBQUksRUFBRSxhQUFhLEVBQUU7WUFDeEQsR0FBRztZQUNILGdCQUFnQjtZQUNoQixZQUFZLEVBQUUsWUFBWSxJQUFJLHNCQUFzQjtZQUNwRCxhQUFhO1lBQ2IsWUFBWTtZQUNaLFlBQVk7WUFDWixlQUFlLEVBQUUsZUFBZSxJQUFJLEVBQUUsVUFBVSxFQUFFLG9CQUFVLENBQUMsTUFBTSxFQUFFO1lBQ3JFLElBQUksRUFBRSw0QkFBa0IsQ0FBQyxZQUFZO1lBQ25DLDBCQUEwQjtZQUMxQixxQkFBVyxDQUFDLFlBQVksQ0FBQyxp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Q0gsQ0FBQztRQUVELElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBRXpCLENBQUM7SUFFTyxlQUFlLENBQUMsV0FBaUM7UUFDdkQsSUFBSSxXQUFXLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDN0IsT0FBTyxXQUFXLENBQUMsWUFBWSxDQUFDO1FBQ2xDLENBQUM7YUFBTSxJQUFJLFdBQVcsQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN0QyxNQUFNLEVBQUUsR0FBRyxXQUFXLENBQUMsY0FBYyxDQUFDO1lBQ3RDLE9BQU8sa0RBQWtELEVBQUUsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sZ0JBQWdCLEVBQUUsQ0FBQyxNQUFNLENBQUMsU0FBUywrQ0FBK0MsRUFBRSxDQUFDLEdBQUcsS0FBSyxDQUFDO1FBQzdLLENBQUM7YUFBTSxDQUFDO1lBQ04sTUFBTSxJQUFJLEtBQUssQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO1FBQ2xELENBQUM7SUFDSCxDQUFDOztBQW5HSCw0Q0FvR0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDZm5PdXRwdXQsIEZuLCBTdGFjaywgVG9rZW4gfSBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQge1xuICBBbWF6b25MaW51eENwdVR5cGUsXG4gIEJhc3Rpb25Ib3N0TGludXgsXG4gIENmblJvdXRlLFxuICBDbG91ZEZvcm1hdGlvbkluaXQsXG4gIElNYWNoaW5lSW1hZ2UsXG4gIEluaXRDb21tYW5kLFxuICBJbml0RWxlbWVudCxcbiAgSW5zdGFuY2VUeXBlLFxuICBJU2VjdXJpdHlHcm91cCxcbiAgSVZwYyxcbiAgTWFjaGluZUltYWdlLFxuICBQZWVyLFxuICBQb3J0LFxuICBTdWJuZXRTZWxlY3Rpb24sXG4gIFN1Ym5ldFR5cGUsXG59IGZyb20gJ2F3cy1jZGstbGliL2F3cy1lYzInO1xuaW1wb3J0IHsgSVNlY3JldCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1zZWNyZXRzbWFuYWdlcic7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcblxuZXhwb3J0IGludGVyZmFjZSBTZWNyZXRzTWFuYWdlckF1dGhLZXkge1xuICAvKipcbiAgKiBTZWNyZXQgbWFuYWdlciBsb2NhdGlvbiB3aGVyZSB0aGUgdGFpbHNjYWxlIGF1dGgga2V5IGlzIHN0b3JlZC4gTXVzdCBiZSBpbiB0aGUgc3RhbmRhcmQga2V5L3ZhbHVlIEpTT04gZm9ybWF0LlxuICAqL1xuICByZWFkb25seSBzZWNyZXQ6IElTZWNyZXQ7XG4gIC8qKlxuICAgKiBUaGUga2V5IG9mIHRoZSBhdXRoIGtleSB2YWx1ZSBsb2NhdGVkIHdpdGhpbiB0aGUgcHJvdmlkZWQgc2VjcmV0LlxuICAgKi9cbiAgcmVhZG9ubHkga2V5OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVGFpbHNjYWxlQ3JlZGVudGlhbHMge1xuICAvKipcbiAgICogRmV0Y2hlcyB0aGUgQXV0aCBLZXkgZnJvbSBzZWNyZXRzIG1hbmFnZXIuIFRoaXMgdmFsdWUgd2lsbCBiZSBmZXRjaGVkIGR1cmluZyBiYXN0aW9uIHN0YXJ0dXAuXG4gICAqL1xuICByZWFkb25seSBzZWNyZXRzTWFuYWdlcj86IFNlY3JldHNNYW5hZ2VyQXV0aEtleTtcbiAgLyoqXG4gICAqIFByb3ZpZGVzIGFuIGF1dGgga2V5IGFzIGEgcGxhaW50ZXh0IHN0cmluZy5cbiAgICogVGhpcyBvcHRpb24gd2lsbCBleHBvc2UgdGhlIGF1dGgga2V5IGluIHlvdXIgQ0RLIHRlbXBsYXRlIGFuZCBzaG91bGQgb25seSBiZSB1c2VkIHdpdGggbm9uLXJldXNhYmxlIGtleXMuXG4gICAqIFBvdGVudGlhbGx5IHVzZWZ1bCBmb3IgRGV2T3BzIHJ1bmJvb2tzIGFuZCB0ZW1wb3JhcnkgaW5zdGFuY2VzLlxuICAgKlxuICAgKiBUaGUgYGNhY2hlZEluQ29udGV4dGAgY29uZmlndXJhdGlvbiBvcHRpb24gbWlnaHQgYmUgcmVsZXZhbnQgdG8geW91IGlmIHlvdSB1c2UgdGhpcyBwYXJhbWV0ZXIuXG4gICAqL1xuICByZWFkb25seSB1bnNhZmVTdHJpbmc/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVGFpbHNjYWxlQmFzdGlvblByb3BzIHtcbiAgLyoqXG4gICAgKiBWUEMgdG8gbGF1bmNoIHRoZSBpbnN0YW5jZSBpbi5cbiAgICAqL1xuICByZWFkb25seSB2cGM6IElWcGM7XG4gIC8qKlxuICAgKiBDcmVkZW50aWFsIHNldHRpbmdzIGZvciB0aGUgdGFpbHNjYWxlIGF1dGgga2V5LiBPbmUgdHlwZSBtdXN0IGJlIHVzZWQuXG4gICAqIEVwaGVtZXJhbCBrZXlzIGFyZSByZWNvbW1lbmRlZC5cbiAgICovXG4gIHJlYWRvbmx5IHRhaWxzY2FsZUNyZWRlbnRpYWxzOiBUYWlsc2NhbGVDcmVkZW50aWFscztcbiAgLyoqXG4gICAqIEluIHdoaWNoIEFaIHRvIHBsYWNlIHRoZSBpbnN0YW5jZSB3aXRoaW4gdGhlIFZQQy5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBSYW5kb20gem9uZS5cbiAgICovXG4gIHJlYWRvbmx5IGF2YWlsYWJpbGl0eVpvbmU/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBUaGUgbmFtZSBvZiB0aGUgaW5zdGFuY2UuXG4gICAqXG4gICAqIEBkZWZhdWx0ICdCYXN0aW9uSG9zdFRhaWxzY2FsZSdcbiAgICovXG4gIHJlYWRvbmx5IGluc3RhbmNlTmFtZT86IHN0cmluZztcbiAgLyoqXG4gICAqIFNlbGVjdCB0aGUgc3VibmV0cyB0byBydW4gdGhlIGJhc3Rpb24gaG9zdCBpbi5cbiAgICogUFVCTElDIHN1Ym5ldHMgYXJlIHVzZWQgYnkgZGVmYXVsdCB0byBhbGxvdyBmb3IgYSBkaXJlY3QgVGFpbHNjYWxlIGNvbm5lY3Rpb24uIERFUlAgbm9kZXMgd2lsbCBiZSB1c2VkIGluIGEgcHJpdmF0ZSBzdWJuZXQuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gUFVCTElDIHN1Ym5ldHMgb2YgdGhlIHN1cHBsaWVkIFZQQ1xuICAgKi9cbiAgcmVhZG9ubHkgc3VibmV0U2VsZWN0aW9uPzogU3VibmV0U2VsZWN0aW9uO1xuICAvKipcbiAgICogU2VjdXJpdHkgR3JvdXAgdG8gYXNzaWduIHRvIHRoaXMgaW5zdGFuY2UuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gY3JlYXRlIG5ldyBzZWN1cml0eSBncm91cCB3aXRoIG5vIGluYm91bmQgYW5kIGFsbCBvdXRib3VuZCB0cmFmZmljIGFsbG93ZWRcbiAgICovXG4gIHJlYWRvbmx5IHNlY3VyaXR5R3JvdXA/OiBJU2VjdXJpdHlHcm91cDtcbiAgLyoqXG4gICAqIFR5cGUgb2YgaW5zdGFuY2UgdG8gbGF1bmNoLlxuICAgKlxuICAgKiBAZGVmYXVsdCAndDMubmFubydcbiAgICovXG4gIHJlYWRvbmx5IGluc3RhbmNlVHlwZT86IEluc3RhbmNlVHlwZTtcbiAgLyoqXG4gICAqICBDUFUgVHlwZSBvZiB0aGUgaW5zdGFuY2UuXG4gICAqXG4gICAqICBAZGVmYXVsdCBBbWF6b25MaW51eENwdVR5cGUuWDg2XzY0XG4gICAqL1xuICByZWFkb25seSBjcHVUeXBlPzogQW1hem9uTGludXhDcHVUeXBlO1xuICAvKipcbiAgICogQWRkaXRpb25hbCBjbG91ZGZvcm1hdGlvbiBpbml0IGFjdGlvbnMgdG8gcGVyZm9ybSBkdXJpbmcgc3RhcnR1cC5cbiAgICovXG4gIHJlYWRvbmx5IGFkZGl0aW9uYWxJbml0PzogSW5pdEVsZW1lbnRbXTtcbiAgLyoqXG4gICAqIExpc3Qgb2YgaW5jb21pbmcgcm91dGVzIGZyb20gVGFpbHNjYWxlIG5ldHdvcmsuIFZQQyByb3V0ZSB0YWJsZSB3aWxsIGdldCB0aGVzZSB0YXJnZXRzIGFkZGVkLlxuICAgKlxuICAgKiBAZGVmYXVsdCBub25lXG4gICAqL1xuICByZWFkb25seSBpbmNvbWluZ1JvdXRlcz86IHN0cmluZ1tdO1xuICAvKipcbiAgICogQWR2ZXJ0aXNlIGEgY3VzdG9tIHJvdXRlIGluc3RlYWQgb2YgdXNpbmcgdGhlIFZQQyBDSURSLCB1c2VkIGZvciBUYWlsc2NhbGUgNHZpYTYgc3VwcG9ydC5cbiAgICovXG4gIHJlYWRvbmx5IGFkdmVydGlzZVJvdXRlPzogc3RyaW5nO1xuICAvKipcbiAgICogU2V0dGluZyB0aGlzIHRvIHRydWUgd2lsbCByZXN1bHQgaW4gdGhlIEFtYXpvbiBMaW51eCBBTUkgYmVpbmcgY2FjaGVkIGluIGBjZGsuY29udGV4dC5qc29uYCBhbmQgcHJldmVudCB0aGUgaW5zdGFuY2UgYmVpbmcgcmVwbGFjZWQgd2hlbiB0aGUgaW1hZ2UgaXMgdXBkYXRlZC5cbiAgICogRW5hYmxlIHRoaXMgaWYgeW91J2QgbGlrZSB0byB1c2Ugbm9uLXJldXNhYmxlIFRhaWxzY2FsZSBrZXlzLCBvciB5b3UnZCBwcmVmZXIgdGhlIGluc3RhbmNlIHRvIHJlbWFpbiBzdGFibGUuXG4gICAqIEtlZXAgaW4gbWluZCB0aGF0IHRoZSBBTUkgd2lsbCBncm93IG9sZCBvdmVyIHRpbWUgYW5kIGlzIGl0IHlvdXIgcmVzcG9uc2liaWxpdHkgdG8gZXZpY3QgaXQgZnJvbSB0aGUgY29udGV4dC5cbiAgICpcbiAgICogQGRlZmF1bHQgZmFsc2VcbiAgICovXG4gIHJlYWRvbmx5IGNhY2hlZEluQ29udGV4dD86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIElmIHlvdSB3YW50IHRvIGNvbXBsZXRlbHkgb3B0IG91dCBvZiBnZXR0aW5nIHRoZSBsYXRlc3QgQW1hem9uIExpbnV4IEFNSSBhbmQgaW5zdGVhZCBuZWVkIHRvIHNwZWNpZnkgb25lIHlvdXJzZWxmLCBwYXNzIGl0IGluIGhlcmUuXG4gICAqIFdlJ2xsIHVzZSB0aGUgb25lIHlvdSBzcGVjaWZ5IGluc3RlYWQgb2YgdGhlIGRlZmF1bHQuXG4gICAqL1xuICByZWFkb25seSBhbWk/OiBJTWFjaGluZUltYWdlO1xufVxuXG5leHBvcnQgY2xhc3MgVGFpbHNjYWxlQmFzdGlvbiBleHRlbmRzIENvbnN0cnVjdCB7XG4gIHJlYWRvbmx5IGJhc3Rpb246IEJhc3Rpb25Ib3N0TGludXg7XG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcsIHByb3BzOiBUYWlsc2NhbGVCYXN0aW9uUHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuXG4gICAgY29uc3Qge1xuICAgICAgdGFpbHNjYWxlQ3JlZGVudGlhbHMsXG4gICAgICB2cGMsXG4gICAgICBhdmFpbGFiaWxpdHlab25lLFxuICAgICAgaW5zdGFuY2VOYW1lLFxuICAgICAgc3VibmV0U2VsZWN0aW9uLFxuICAgICAgc2VjdXJpdHlHcm91cCxcbiAgICAgIGluc3RhbmNlVHlwZSxcbiAgICAgIGFkZGl0aW9uYWxJbml0LFxuICAgICAgaW5jb21pbmdSb3V0ZXMsXG4gICAgICBhZHZlcnRpc2VSb3V0ZSxcbiAgICAgIGNwdVR5cGUsXG4gICAgICBjYWNoZWRJbkNvbnRleHQsXG4gICAgICBhbWksXG4gICAgfSA9IHByb3BzO1xuXG4gICAgY29uc3QgYXV0aEtleUNvbW1hbmQgPSB0aGlzLmNvbXB1dGVUc0tleUNsaSh0YWlsc2NhbGVDcmVkZW50aWFscyk7XG4gICAgY29uc3QgbWFjaGluZUltYWdlID0gYW1pID8/IE1hY2hpbmVJbWFnZS5sYXRlc3RBbWF6b25MaW51eDIwMjMoe1xuICAgICAgY3B1VHlwZTogY3B1VHlwZSA/PyBBbWF6b25MaW51eENwdVR5cGUuWDg2XzY0LFxuICAgICAgY2FjaGVkSW5Db250ZXh0OiBjYWNoZWRJbkNvbnRleHQgPz8gZmFsc2UsXG4gICAgfSk7XG5cbiAgICBjb25zdCBiYXN0aW9uID0gbmV3IEJhc3Rpb25Ib3N0TGludXgodGhpcywgJ0Jhc3Rpb25Ib3N0Jywge1xuICAgICAgdnBjLFxuICAgICAgYXZhaWxhYmlsaXR5Wm9uZSxcbiAgICAgIGluc3RhbmNlTmFtZTogaW5zdGFuY2VOYW1lID8/ICdCYXN0aW9uSG9zdFRhaWxzY2FsZScsXG4gICAgICBzZWN1cml0eUdyb3VwLFxuICAgICAgaW5zdGFuY2VUeXBlLFxuICAgICAgbWFjaGluZUltYWdlLFxuICAgICAgc3VibmV0U2VsZWN0aW9uOiBzdWJuZXRTZWxlY3Rpb24gPz8geyBzdWJuZXRUeXBlOiBTdWJuZXRUeXBlLlBVQkxJQyB9LFxuICAgICAgaW5pdDogQ2xvdWRGb3JtYXRpb25Jbml0LmZyb21FbGVtZW50cyhcbiAgICAgICAgLy8gQ29uZmlndXJlIElQIGZvcndhcmRpbmdcbiAgICAgICAgSW5pdENvbW1hbmQuc2hlbGxDb21tYW5kKCdlY2hvIFxcJ25ldC5pcHY0LmlwX2ZvcndhcmQgPSAxXFwnIHwgc3VkbyB0ZWUgLWEgL2V0Yy9zeXNjdGwuY29uZicpLFxuICAgICAgICBJbml0Q29tbWFuZC5zaGVsbENvbW1hbmQoJ2VjaG8gXFwnbmV0LmlwdjYuY29uZi5hbGwuZm9yd2FyZGluZyA9IDFcXCcgfCBzdWRvIHRlZSAtYSAvZXRjL3N5c2N0bC5jb25mJyksXG4gICAgICAgIEluaXRDb21tYW5kLnNoZWxsQ29tbWFuZCgnc3VkbyBzeXNjdGwgLXAgL2V0Yy9zeXNjdGwuY29uZicpLFxuXG4gICAgICAgIC8vIENvbmZpZ3VyZSBBbWF6b24gTGludXggMjAyMyBETlMgaHR0cHM6Ly9naXRodWIuY29tL3RhaWxzY2FsZS90YWlsc2NhbGUvaXNzdWVzLzc4MTZcbiAgICAgICAgSW5pdENvbW1hbmQuc2hlbGxDb21tYW5kKCdta2RpciAtcCAvZXRjL3N5c3RlbWQvcmVzb2x2ZWQuY29uZi5kJyksXG4gICAgICAgIEluaXRDb21tYW5kLnNoZWxsQ29tbWFuZCgnbG4gLXNmIC9kZXYvbnVsbCAvZXRjL3N5c3RlbWQvcmVzb2x2ZWQuY29uZi5kL3Jlc29sdmVkLWRpc2FibGUtc3R1Yi1saXN0ZW5lci5jb25mJyksXG4gICAgICAgIEluaXRDb21tYW5kLnNoZWxsQ29tbWFuZCgnbG4gLXNmIC9ydW4vc3lzdGVtZC9yZXNvbHZlL3N0dWItcmVzb2x2LmNvbmYgL2V0Yy9yZXNvbHYuY29uZicpLFxuXG4gICAgICAgIC8vIEluc3RhbGwgVGFpbHNjYWxlXG4gICAgICAgIEluaXRDb21tYW5kLnNoZWxsQ29tbWFuZCgnZG5mIGNvbmZpZy1tYW5hZ2VyIC0tYWRkLXJlcG8gaHR0cHM6Ly9wa2dzLnRhaWxzY2FsZS5jb20vc3RhYmxlL2FtYXpvbi1saW51eC8yMDIzL3RhaWxzY2FsZS5yZXBvJyksXG4gICAgICAgIC8vIFByb3RlY3QgYWdhaW5zdCBhIHBvdGVudGlhbCBjb25mbGljdCB3aXRoIEFXUyBhY3Rpdml0eVxuICAgICAgICBJbml0Q29tbWFuZC5zaGVsbENvbW1hbmQoJ3VudGlsIGRuZiAteSBpbnN0YWxsIHRhaWxzY2FsZSA7IGRvIHNsZWVwIDEwcyA7IGRvbmUnKSxcbiAgICAgICAgSW5pdENvbW1hbmQuc2hlbGxDb21tYW5kKCdzeXN0ZW1jdGwgZW5hYmxlIC0tbm93IHRhaWxzY2FsZWQnKSxcbiAgICAgICAgSW5pdENvbW1hbmQuc2hlbGxDb21tYW5kKGBlY2hvIFRTX0FVVEhLRVk9JHthdXRoS2V5Q29tbWFuZH0gPj4gL2V0Yy9lbnZpcm9ubWVudGApLFxuICAgICAgICBJbml0Q29tbWFuZC5zaGVsbENvbW1hbmQoYHNvdXJjZSAvZXRjL2Vudmlyb25tZW50ICYmIHRhaWxzY2FsZSB1cCAtLWF1dGhrZXkgJFRTX0FVVEhLRVkgLS1hZHZlcnRpc2Utcm91dGVzPSR7YWR2ZXJ0aXNlUm91dGUgPz8gdnBjLnZwY0NpZHJCbG9ja30gLS1hY2NlcHQtcm91dGVzIC0tYWNjZXB0LWRucz1mYWxzZWApLFxuICAgICAgICAuLi4oYWRkaXRpb25hbEluaXQgPz8gW10pLFxuICAgICAgKSxcbiAgICAgIGluaXRPcHRpb25zOiB7fSxcbiAgICB9KTtcblxuICAgIGlmICh0YWlsc2NhbGVDcmVkZW50aWFscy5zZWNyZXRzTWFuYWdlcikge1xuICAgICAgdGFpbHNjYWxlQ3JlZGVudGlhbHMuc2VjcmV0c01hbmFnZXIuc2VjcmV0LmdyYW50UmVhZChiYXN0aW9uKTtcbiAgICB9XG5cbiAgICBiYXN0aW9uLmNvbm5lY3Rpb25zLmFsbG93RnJvbUFueUlwdjQoUG9ydC51ZHAoNDE2NDEpLCAnVGFpbHNjYWxlIElQdjQnKTtcbiAgICBiYXN0aW9uLmNvbm5lY3Rpb25zLmFsbG93RnJvbShQZWVyLmFueUlwdjYoKSwgUG9ydC51ZHAoNDE2NDEpLCAnVGFpbHNjYWxlIElQdjYnKTtcblxuICAgIGNvbnN0IHNwbGl0QWRkcmVzcyA9IEZuLnNwbGl0KCcvJywgcHJvcHMudnBjLnZwY0NpZHJCbG9jaywgMilbMF07XG4gICAgY29uc3Qgc3BsaXRJcCA9IEZuLnNwbGl0KCcuJywgc3BsaXRBZGRyZXNzLCA0KTtcbiAgICBjb25zdCBkbnNTZXJ2ZXIgPSBgJHtzcGxpdElwWzBdfS4ke3NwbGl0SXBbMV19LiR7c3BsaXRJcFsyXX0uMmA7XG5cbiAgICBuZXcgQ2ZuT3V0cHV0KHRoaXMsICdWcGMtRG5zLU5hbWVzZXJ2ZXInLCB7IHZhbHVlOiBkbnNTZXJ2ZXIgfSk7XG5cbiAgICBjb25zdCBzdGFjayA9IFN0YWNrLm9mKHRoaXMpO1xuICAgIGNvbnN0IGRvbWFpbiA9IFRva2VuLmlzVW5yZXNvbHZlZChzdGFjay5yZWdpb24pID8gJ2NvbXB1dGUuaW50ZXJuYWwnIDogYCR7c3RhY2sucmVnaW9ufS5jb21wdXRlLmludGVybmFsYDtcblxuICAgIG5ldyBDZm5PdXRwdXQodGhpcywgJ1ZwYy1EbnMtRG9tYWluJywgeyB2YWx1ZTogZG9tYWluIH0pO1xuXG4gICAgZm9yIChjb25zdCBpbmNvbWluZ1JvdXRlIG9mIGluY29taW5nUm91dGVzID8/IFtdKSB7XG4gICAgICBmb3IgKGNvbnN0IHN1Ym5ldCBvZiB2cGMucHJpdmF0ZVN1Ym5ldHMpIHtcbiAgICAgICAgbmV3IENmblJvdXRlKHN1Ym5ldCwgYFRhaWxzY2FsZVJvdXRlLSR7aW5jb21pbmdSb3V0ZX1gLCB7XG4gICAgICAgICAgcm91dGVUYWJsZUlkOiBzdWJuZXQucm91dGVUYWJsZS5yb3V0ZVRhYmxlSWQsXG4gICAgICAgICAgZGVzdGluYXRpb25DaWRyQmxvY2s6IGluY29taW5nUm91dGUsXG4gICAgICAgICAgaW5zdGFuY2VJZDogYmFzdGlvbi5pbnN0YW5jZUlkLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0aGlzLmJhc3Rpb24gPSBiYXN0aW9uO1xuXG4gIH1cblxuICBwcml2YXRlIGNvbXB1dGVUc0tleUNsaShjcmVkZW50aWFsczogVGFpbHNjYWxlQ3JlZGVudGlhbHMpIHtcbiAgICBpZiAoY3JlZGVudGlhbHMudW5zYWZlU3RyaW5nKSB7XG4gICAgICByZXR1cm4gY3JlZGVudGlhbHMudW5zYWZlU3RyaW5nO1xuICAgIH0gZWxzZSBpZiAoY3JlZGVudGlhbHMuc2VjcmV0c01hbmFnZXIpIHtcbiAgICAgIGNvbnN0IHNtID0gY3JlZGVudGlhbHMuc2VjcmV0c01hbmFnZXI7XG4gICAgICByZXR1cm4gYCQoYXdzIHNlY3JldHNtYW5hZ2VyIGdldC1zZWNyZXQtdmFsdWUgLS1yZWdpb24gJHtzbS5zZWNyZXQuZW52LnJlZ2lvbn0gLS1zZWNyZXQtaWQgJHtzbS5zZWNyZXQuc2VjcmV0QXJufSAtLXF1ZXJ5IFNlY3JldFN0cmluZyAtLW91dHB1dCB0ZXh0IHwganEgJy5cIiR7c20ua2V5fVwiJylgO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIFRhaWxzY2FsZSBjcmVkZW50aWFscyBzZXQnKTtcbiAgICB9XG4gIH1cbn0iXX0=