UNPKG

@aws-amplify/amplify-category-api

Version:
283 lines 11.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EcsAlbStack = void 0; const acm = __importStar(require("aws-cdk-lib/aws-certificatemanager")); const cloudfront = __importStar(require("aws-cdk-lib/aws-cloudfront")); const cognito = __importStar(require("aws-cdk-lib/aws-cognito")); const ec2 = __importStar(require("aws-cdk-lib/aws-ec2")); const elb2 = __importStar(require("aws-cdk-lib/aws-elasticloadbalancingv2")); const route53 = __importStar(require("aws-cdk-lib/aws-route53")); const route53targets = __importStar(require("aws-cdk-lib/aws-route53-targets")); const cdk = __importStar(require("aws-cdk-lib")); const uuid_1 = require("uuid"); const base_api_stack_1 = require("./base-api-stack"); class EcsAlbStack extends base_api_stack_1.ContainersStack { constructor(scope, id, ecsProps) { super(scope, id, { ...ecsProps, skipWait: true, createCloudMapService: false, }); this.ecsProps = ecsProps; const { authName, restrictAccess } = ecsProps; if (restrictAccess) { const param = this.parameters.get(`auth${authName}HostedUIDomain`); this.userPoolDomain = param.valueAsString; } this.parameters.get('ParamZipPath').default = 'site.zip'; this.alb(); } alb() { const { domainName, hostedZoneId, exposedContainer: { name: containerName, port }, restrictAccess, } = this.ecsProps; const sharedSecretHeaderName = 'x-cf-token'; const sharedSecretHeader = (0, uuid_1.v4)(); const userPoolDomain = this.userPoolDomain; const vpcId = this.vpcId; const subnets = this.subnets; const userPoolArn = cdk.Fn.join('', [ 'arn:', cdk.Aws.PARTITION, ':cognito-idp:', cdk.Aws.REGION, ':', cdk.Aws.ACCOUNT_ID, ':userpool/', this.userPoolId, ]); const [distributionDomainName, , domainNameSuffix] = domainName.match(/([^\.]+)\.(.*)/); const lbPrefix = `lb-${this.envName}`; const albDomainName = `${lbPrefix}.${domainNameSuffix}`; const wildcardDomainName = `*.${domainNameSuffix}`; const wildcardCertificate = new acm.CfnCertificate(this, 'Certificate', { domainName: wildcardDomainName, validationMethod: hostedZoneId ? acm.ValidationMethod.DNS : acm.ValidationMethod.EMAIL, domainValidationOptions: [ { domainName: wildcardDomainName, validationDomain: hostedZoneId === undefined ? domainNameSuffix : undefined, hostedZoneId, }, ], }); const userPoolClient = restrictAccess ? new cognito.CfnUserPoolClient(this, 'UserPoolClient', { userPoolId: this.userPoolId, allowedOAuthFlows: [ 'code', ], allowedOAuthFlowsUserPoolClient: true, allowedOAuthScopes: ['profile', 'phone', 'email', 'openid', 'aws.cognito.signin.user.admin'], generateSecret: true, supportedIdentityProviders: ['COGNITO'], callbackUrLs: [`https://${distributionDomainName}/oauth2/idpresponse`], logoutUrLs: [`https://${distributionDomainName}/oauth2/idpresponse`], }) : undefined; const targetGroup = new elb2.CfnTargetGroup(this, 'TargetGroup', { healthCheckIntervalSeconds: cdk.Duration.seconds(90).toSeconds(), healthCheckPath: '/', healthCheckTimeoutSeconds: cdk.Duration.minutes(1).toSeconds(), healthyThresholdCount: 2, port, protocol: elb2.Protocol.HTTP, targetType: elb2.TargetType.IP, unhealthyThresholdCount: 2, vpcId, }); const albSecurityGroup = new ec2.CfnSecurityGroup(this, 'AlbSecurityGroup', { vpcId, groupDescription: 'ALB Security Group', securityGroupEgress: [ { description: 'Allow all outbound traffic by default', ipProtocol: '-1', cidrIp: '0.0.0.0/0', }, ], securityGroupIngress: [ { description: 'Allow from anyone on port 443', ipProtocol: ec2.Protocol.TCP, cidrIp: '0.0.0.0/0', fromPort: 443, toPort: 443, }, ], }); const loadBalancer = new elb2.CfnLoadBalancer(this, 'LoadBalancer', { type: 'application', securityGroups: [albSecurityGroup.attrGroupId], loadBalancerAttributes: [ { key: 'deletion_protection.enabled', value: 'false', }, ], scheme: 'internet-facing', subnets, }); this.ecsService.loadBalancers = [ { containerName, containerPort: port, targetGroupArn: targetGroup.ref, }, ]; this.ecsServiceSecurityGroup.securityGroupIngress.push({ ipProtocol: ec2.Protocol.TCP, fromPort: port, toPort: port, sourceSecurityGroupId: albSecurityGroup.attrGroupId, }); const listener = new elb2.CfnListener(this, 'AlbListener', { defaultActions: [ { fixedResponseConfig: { statusCode: '403', }, type: 'fixed-response', }, ], loadBalancerArn: loadBalancer.ref, port: 443, protocol: elb2.Protocol.HTTPS, certificates: [{ certificateArn: wildcardCertificate.ref }], }); this.ecsService.addDependency(listener); let actionsOrderCounter = 1; const listenerRule = new elb2.CfnListenerRule(this, 'AlbListenerRule', { priority: 1, listenerArn: listener.ref, actions: [].concat(restrictAccess ? { order: actionsOrderCounter++, type: 'authenticate-cognito', authenticateCognitoConfig: { userPoolArn, userPoolClientId: userPoolClient.ref, userPoolDomain, }, } : undefined, { order: actionsOrderCounter++, type: 'forward', targetGroupArn: targetGroup.ref, }), conditions: [ { field: 'host-header', hostHeaderConfig: { values: [distributionDomainName], }, }, { field: 'http-header', httpHeaderConfig: { httpHeaderName: sharedSecretHeaderName, values: [sharedSecretHeader], }, }, ], }); this.ecsService.addDependency(listenerRule); const originId = `${loadBalancer.logicalId}-origin`; const distribution = new cloudfront.CfnDistribution(this, 'Distribution', { distributionConfig: { enabled: true, httpVersion: 'http2', ipv6Enabled: true, aliases: [distributionDomainName], defaultCacheBehavior: { forwardedValues: { cookies: { forward: 'all' }, headers: ['*'], queryString: true, }, targetOriginId: originId, viewerProtocolPolicy: 'redirect-to-https', }, origins: [ { customOriginConfig: { originProtocolPolicy: 'https-only', }, domainName: albDomainName, id: originId, originCustomHeaders: [ { headerName: sharedSecretHeaderName, headerValue: sharedSecretHeader, }, ], }, ], viewerCertificate: { acmCertificateArn: wildcardCertificate.ref, minimumProtocolVersion: 'TLSv1.2_2019', sslSupportMethod: 'sni-only', }, }, }); if (hostedZoneId) { new route53.CfnRecordSetGroup(this, 'RecordSetGroup', { hostedZoneId, recordSets: [ { name: albDomainName, type: route53.RecordType.A, aliasTarget: { hostedZoneId: loadBalancer.attrCanonicalHostedZoneId, dnsName: loadBalancer.attrDnsName, }, }, { name: distributionDomainName, type: route53.RecordType.A, aliasTarget: { hostedZoneId: route53targets.CloudFrontTarget.CLOUDFRONT_ZONE_ID, dnsName: distribution.attrDomainName, }, }, ], }); } new cdk.CfnOutput(this, 'PipelineUrl', { value: cdk.Fn.join('', [ 'https://', cdk.Aws.REGION, '.console.aws.amazon.com/codesuite/codepipeline/pipelines/', this.getPipelineName(), '/view', ]), }); new cdk.CfnOutput(this, 'LoadBalancerAliasDomainName', { value: loadBalancer.attrDnsName }); new cdk.CfnOutput(this, 'LoadBalancerCnameDomainName', { value: albDomainName }); new cdk.CfnOutput(this, 'CloudfrontDistributionAliasDomainName', { value: distribution.attrDomainName }); new cdk.CfnOutput(this, 'CloudfrontDistributionCnameDomainName', { value: distributionDomainName }); } } exports.EcsAlbStack = EcsAlbStack; //# sourceMappingURL=ecs-alb-stack.js.map