Skip to content
This repository was archived by the owner on Feb 1, 2025. It is now read-only.

Commit d052e65

Browse files
committed
🐛 fix(certificates): correctly validate multi-zone certificates and handle naming of zones
1 parent 9932aa6 commit d052e65

File tree

10 files changed

+40
-26
lines changed

10 files changed

+40
-26
lines changed

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ This is an alternative to existing Lambda@Edge implementation ([see](https://www
66

77
This library uses Cloudfront, S3, ApiGateway and Lambdas to deploy easily in seconds (hotswap supported).
88

9+
Multiple domains can be specified within CLI and Routing as well as Certificates are handled automatically.
910

1011
## TL;DR
1112
- In your NextJS project, set output to standalone.

Diff for: lib/cdk/app.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@ const app = new App()
1010
const commandCwd = process.cwd()
1111

1212
new NextStandaloneStack(app, envConfig.stackName, {
13-
// NextJS lambda specific config
13+
// NextJS lambda specific config.
1414
assetsZipPath: path.resolve(commandCwd, './next.out/assetsLayer.zip'),
1515
codeZipPath: path.resolve(commandCwd, './next.out/code.zip'),
1616
dependenciesZipPath: path.resolve(commandCwd, './next.out/dependenciesLayer.zip'),
1717
customServerHandler: 'index.handler',
1818

19-
// Image lambda specific config
19+
// Image lambda specific config.
2020
imageHandlerZipPath: optimizerCodePath,
2121
imageLayerZipPath: optimizerLayerPath,
2222
imageLambdaHash: `${name}_${version}`,
2323
customImageHandler: handler,
2424

25-
// Lambda & AWS config
25+
// Lambda & AWS config.
2626
apigwServerPath: '/_server',
2727
apigwImagePath: '/_image',
2828

Diff for: lib/cdk/stack.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export class NextStandaloneStack extends Stack {
2323
assetsBucket?: Bucket
2424
cfnDistro?: IDistribution
2525
cfnCertificate?: ICertificate
26-
domains: MappedDomain[]
26+
domains: MappedDomain[] = []
2727

2828
constructor(scope: App, id: string, config: CustomStackProps) {
2929
super(scope, id, config)
@@ -68,6 +68,8 @@ export class NextStandaloneStack extends Stack {
6868
domains: config.domainNames,
6969
profile: config.awsProfile,
7070
})
71+
72+
console.log("Domains's config:", this.domains)
7173
}
7274

7375
if (this.domains.length > 0) {

Diff for: lib/cdk/types.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@ export interface CustomStackProps extends StackProps {
1818
lambdaRuntime: Runtime
1919
imageLambdaTimeout?: number
2020
imageLambdaMemory?: number
21-
domainNames: string[]
2221
redirectFromApex: boolean
23-
awsProfile?: string
2422
customApiDomain?: string
23+
certificateArn?: string
24+
domainNames: string[]
25+
awsProfile?: string
2526
}
2627

2728
export interface MappedDomain {
28-
recordName: string
29+
recordName?: string
2930
domain: string
3031
zone: IHostedZone
3132
}

Diff for: lib/cdk/utils/cfnCertificate.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CfnOutput, Stack } from 'aws-cdk-lib'
2-
import { Certificate, CertificateValidation } from 'aws-cdk-lib/aws-certificatemanager'
2+
import { CertificateValidation, DnsValidatedCertificate } from 'aws-cdk-lib/aws-certificatemanager'
33
import { MappedDomain } from '../types'
44

55
export interface SetupCfnCertificateProps {
@@ -11,13 +11,18 @@ export const setupCfnCertificate = (scope: Stack, { domains }: SetupCfnCertifica
1111

1212
// us-east-1 is needed for Cloudfront to accept certificate.
1313
// https://github.com/aws/aws-cdk/issues/8934
14-
const multiZoneMap = domains.reduce((acc, curr) => ({ ...acc, [curr.domain]: curr.zone }), {})
14+
const multiZoneMap = otherDomains.reduce((acc, curr) => ({ ...acc, [curr.domain]: curr.zone }), {})
1515

16-
const certificate = new Certificate(scope, 'Certificate', {
17-
domainName: firstDomain.domain,
16+
const easyCheck = domains.reduce((acc, curr) => ({ ...acc, [curr.domain]: curr.zone.zoneName }), {})
1817

18+
// We need to stick with DNSValidatedCertificate for now, because Certificate construct does not support region specifiation.
19+
// So we would need to manage two different stacks and create certificate in us-east-1 with second stack.
20+
const certificate = new DnsValidatedCertificate(scope, 'Certificate', {
21+
domainName: firstDomain.domain,
22+
hostedZone: firstDomain.zone,
1923
subjectAlternativeNames: otherDomains.map((a) => a.domain),
2024
validation: CertificateValidation.fromDnsMultiZone(multiZoneMap),
25+
region: 'us-east-1',
2126
})
2227

2328
new CfnOutput(scope, 'certificateArn', { value: certificate.certificateArn })

Diff for: lib/cdk/utils/dnsRecords.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,21 @@ const matchDomainToHostedZone = (domainToMatch: string, zones: string[]) => {
4545
throw new Error(`No hosted zone found for domain: ${domainToMatch}`)
4646
}
4747

48-
return matchedZone.replace('/.$/', '')
48+
// Remove trailing dot
49+
return matchedZone.endsWith('.') ? matchedZone.slice(0, -1) : matchedZone
4950
}
5051

5152
export const prepareDomains = (scope: Stack, { domains, profile }: PrepareDomainProps): MappedDomain[] => {
5253
const zones = getAvailableHostedZones(profile)
5354

5455
return domains.map((domain, index) => {
5556
const hostedZone = matchDomainToHostedZone(domain, zones)
56-
const recordName = domain.replace(hostedZone, '')
57-
57+
const subdomain = domain.replace(hostedZone, '')
58+
const recordName = subdomain.endsWith('.') ? subdomain.slice(0, -1) : subdomain
5859
const zone = HostedZone.fromLookup(scope, `Zone_${index}`, { domainName: hostedZone })
5960

60-
return { zone, recordName, domain }
61+
// Returning additional info, useful for debugging
62+
return { zone, recordName, domain, subdomain, hostedZone }
6163
})
6264
}
6365

Diff for: lib/cdk/utils/redirect.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const setupApexRedirect = (scope: Stack, { domain }: SetupApexRedirectPro
1010
new HttpsRedirect(scope, `ApexRedirect`, {
1111
// Currently supports only apex (root) domain.
1212
zone: domain.zone,
13-
targetDomain: domain.recordName,
13+
targetDomain: domain.domain,
1414
})
1515

1616
new CfnOutput(scope, 'RedirectFrom', { value: domain.zone.zoneName })

Diff for: lib/cli.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ program
6060
.option('--imageLambdaTimeout <sec>', 'Set timeout for lambda function handling image optimization.', Number, IMAGE_LAMBDA_DEFAULT_TIMEOUT)
6161
.option('--imageLambdaMemory <mb>', 'Set memory for lambda function handling image optimization.', Number, IMAGE_LAMBDA_DEFAULT_MEMORY)
6262
.option('--lambdaRuntime <runtime>', "Specify version of NodeJS to use as Lambda's runtime. Options: node14, node16, node18.", 'node16')
63-
.option('--domains <domainList>', 'Comma-separated list of domains to use. (example: mydomain.com,mydonain.au,other.domain.com)', undefined)
63+
.option('--domainNames <domainList>', 'Comma-separated list of domains to use. (example: mydomain.com,mydonain.au,other.domain.com)', undefined)
6464
.option('--customApiDomain <domain>', 'Domain to forward the requests to /api routes, by default API routes will be handled by the server lambda.', undefined)
6565
.option('--redirectFromApex', 'Redirect from apex domain to specified address.', false)
6666
.option('--profile <name>', 'AWS profile to use with CDK.', undefined)
@@ -79,7 +79,7 @@ program
7979
imageLambdaTimeout,
8080
customApiDomain,
8181
redirectFromApex,
82-
domains,
82+
domainNames,
8383
hotswap,
8484
profile,
8585
} = options
@@ -97,7 +97,7 @@ program
9797
imageLambdaTimeout,
9898
customApiDomain,
9999
redirectFromApex,
100-
domains,
100+
domainNames,
101101
hotswap,
102102
profile,
103103
}),

Diff for: lib/cli/deploy.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ interface Props {
1111
imageLambdaMemory?: number
1212
imageLambdaTimeout?: number
1313
customApiDomain?: string
14-
domains?: string
14+
domainNames?: string
1515
redirectFromApex?: boolean
1616
profile?: string
1717
hotswap: boolean
@@ -29,7 +29,7 @@ export const deployHandler = async ({
2929
lambdaRuntime,
3030
imageLambdaMemory,
3131
imageLambdaTimeout,
32-
domains,
32+
domainNames,
3333
customApiDomain,
3434
redirectFromApex,
3535
hotswap,
@@ -56,7 +56,7 @@ export const deployHandler = async ({
5656
...(lambdaRuntime && { LAMBDA_RUNTIME: lambdaRuntime.toString() }),
5757
...(imageLambdaMemory && { IMAGE_LAMBDA_MEMORY: imageLambdaMemory.toString() }),
5858
...(imageLambdaTimeout && { IMAGE_LAMBDA_TIMEOUT: imageLambdaTimeout.toString() }),
59-
...(domains && { DOMAINS: domains }),
59+
...(domainNames && { DOMAIN_NAMES: domainNames }),
6060
...(customApiDomain && { CUSTOM_API_DOMAIN: customApiDomain }),
6161
...(redirectFromApex && { REDIRECT_FROM_APEX: redirectFromApex.toString() }),
6262
}

Diff for: lib/index.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,33 @@ export { NextStandaloneStack } from './cdk/stack'
44
export { CustomStackProps } from './cdk/types'
55

66
import { SetupApiGwProps, setupApiGateway } from './cdk/utils/apiGw'
7-
import { SetupCfnCertificateProps, setupCfnCertificate } from './cdk/utils/cfnCertificate'
7+
import { setupCfnCertificate, SetupCfnCertificateProps } from './cdk/utils/cfnCertificate'
88
import { SetupCfnDistroProps, setupCfnDistro } from './cdk/utils/cfnDistro'
99
import { SetupImageLambdaProps, setupImageLambda } from './cdk/utils/imageLambda'
1010
import { SetupServerLambdaProps, setupServerLambda } from './cdk/utils/serverLambda'
1111
import { UploadAssetsProps, setupAssetsBucket, uploadStaticAssets } from './cdk/utils/s3'
12-
import { SetupDnsRecordsProps, setupDnsRecords } from './cdk/utils/dnsRecords'
12+
import { SetupDnsRecordsProps, setupDnsRecords, prepareDomains, PrepareDomainProps } from './cdk/utils/dnsRecords'
1313

1414
export const CdkUtils = {
1515
setupApiGateway,
16-
setupCfnCertificate,
1716
setupAssetsBucket,
17+
setupCfnCertificate,
1818
setupCfnDistro,
1919
setupDnsRecords,
2020
setupImageLambda,
2121
setupServerLambda,
2222
uploadStaticAssets,
23+
prepareDomains,
2324
}
2425

2526
export {
27+
//
2628
SetupApiGwProps,
27-
SetupCfnCertificateProps,
2829
SetupCfnDistroProps,
30+
SetupCfnCertificateProps,
2931
SetupImageLambdaProps,
3032
SetupServerLambdaProps,
3133
UploadAssetsProps,
3234
SetupDnsRecordsProps,
35+
PrepareDomainProps,
3336
}

0 commit comments

Comments
 (0)