Skip to content

Commit 93a130f

Browse files
njlynchCurtis Eppel
authored and
Curtis Eppel
committed
fix(certificatemanager): DNS validation for wildcard certificates (aws#9291)
If a certificate with automatic (Route53) DNS validation contains both a base domain name and the wildcard for that domain (e.g., `example.com` and `*.example.com`), the corresponding DNS validation records are identical. This seems to have caused problems for the automated CloudFormation DNS validation. Solving the problem by removing the redundant wildcard entries from the DomainValidationOption. fixes aws#9248 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 7816dfc commit 93a130f

File tree

2 files changed

+113
-30
lines changed

2 files changed

+113
-30
lines changed

packages/@aws-cdk/aws-certificatemanager/lib/certificate.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ function renderDomainValidation(validation: CertificateValidation, domainNames:
238238

239239
switch (validation.method) {
240240
case ValidationMethod.DNS:
241-
for (const domainName of domainNames) {
241+
for (const domainName of getUniqueDnsDomainNames(domainNames)) {
242242
const hostedZone = validation.props.hostedZones?.[domainName] ?? validation.props.hostedZone;
243243
if (hostedZone) {
244244
domainValidation.push({ domainName, hostedZoneId: hostedZone.hostedZoneId });
@@ -260,3 +260,14 @@ function renderDomainValidation(validation: CertificateValidation, domainNames:
260260

261261
return domainValidation.length !== 0 ? domainValidation : undefined;
262262
}
263+
264+
/**
265+
* Removes wildcard domains (*.example.com) where the base domain (example.com) is present.
266+
* This is because the DNS validation treats them as the same thing, and the automated CloudFormation
267+
* DNS validation errors out with the duplicate records.
268+
*/
269+
function getUniqueDnsDomainNames(domainNames: string[]) {
270+
return domainNames.filter(domain => {
271+
return Token.isUnresolved(domain) || !domain.startsWith('*.') || !domainNames.includes(domain.replace('*.', ''));
272+
});
273+
}

packages/@aws-cdk/aws-certificatemanager/test/certificate.test.ts

+101-29
Original file line numberDiff line numberDiff line change
@@ -121,46 +121,118 @@ test('CertificateValidation.fromEmail', () => {
121121
});
122122
});
123123

124-
test('CertificateValidation.fromDns', () => {
125-
const stack = new Stack();
124+
describe('CertificateValidation.fromDns', () => {
126125

127-
new Certificate(stack, 'Certificate', {
128-
domainName: 'test.example.com',
129-
subjectAlternativeNames: ['extra.example.com'],
130-
validation: CertificateValidation.fromDns(),
131-
});
126+
test('without a hosted zone', () => {
127+
const stack = new Stack();
132128

133-
expect(stack).toHaveResource('AWS::CertificateManager::Certificate', {
134-
DomainName: 'test.example.com',
135-
SubjectAlternativeNames: ['extra.example.com'],
136-
ValidationMethod: 'DNS',
129+
new Certificate(stack, 'Certificate', {
130+
domainName: 'test.example.com',
131+
subjectAlternativeNames: ['extra.example.com'],
132+
validation: CertificateValidation.fromDns(),
133+
});
134+
135+
expect(stack).toHaveResource('AWS::CertificateManager::Certificate', {
136+
DomainName: 'test.example.com',
137+
SubjectAlternativeNames: ['extra.example.com'],
138+
ValidationMethod: 'DNS',
139+
});
137140
});
138-
});
139141

140-
test('CertificateValidation.fromDns with hosted zone', () => {
141-
const stack = new Stack();
142+
test('with a hosted zone', () => {
143+
const stack = new Stack();
142144

143-
const exampleCom = new route53.HostedZone(stack, 'ExampleCom', {
144-
zoneName: 'example.com',
145+
const exampleCom = new route53.HostedZone(stack, 'ExampleCom', {
146+
zoneName: 'example.com',
147+
});
148+
149+
new Certificate(stack, 'Certificate', {
150+
domainName: 'test.example.com',
151+
validation: CertificateValidation.fromDns(exampleCom),
152+
});
153+
154+
expect(stack).toHaveResource('AWS::CertificateManager::Certificate', {
155+
DomainName: 'test.example.com',
156+
DomainValidationOptions: [
157+
{
158+
DomainName: 'test.example.com',
159+
HostedZoneId: {
160+
Ref: 'ExampleCom20E1324B',
161+
},
162+
},
163+
],
164+
ValidationMethod: 'DNS',
165+
});
145166
});
146167

147-
new Certificate(stack, 'Certificate', {
148-
domainName: 'test.example.com',
149-
validation: CertificateValidation.fromDns(exampleCom),
168+
test('with hosted zone and a wildcard name', () => {
169+
const stack = new Stack();
170+
171+
const exampleCom = new route53.HostedZone(stack, 'ExampleCom', {
172+
zoneName: 'example.com',
173+
});
174+
175+
new Certificate(stack, 'Certificate', {
176+
domainName: 'test.example.com',
177+
validation: CertificateValidation.fromDns(exampleCom),
178+
subjectAlternativeNames: ['*.test.example.com'],
179+
});
180+
181+
//Wildcard domain names are de-duped.
182+
expect(stack).toHaveResource('AWS::CertificateManager::Certificate', {
183+
DomainName: 'test.example.com',
184+
DomainValidationOptions: [
185+
{
186+
DomainName: 'test.example.com',
187+
HostedZoneId: {
188+
Ref: 'ExampleCom20E1324B',
189+
},
190+
},
191+
],
192+
ValidationMethod: 'DNS',
193+
});
150194
});
151195

152-
expect(stack).toHaveResource('AWS::CertificateManager::Certificate', {
153-
DomainName: 'test.example.com',
154-
DomainValidationOptions: [
155-
{
156-
DomainName: 'test.example.com',
157-
HostedZoneId: {
158-
Ref: 'ExampleCom20E1324B',
196+
test('with hosted zone and multiple wildcard names', () => {
197+
const stack = new Stack();
198+
199+
const exampleCom = new route53.HostedZone(stack, 'ExampleCom', {
200+
zoneName: 'example.com',
201+
});
202+
203+
new Certificate(stack, 'Certificate', {
204+
domainName: 'test.example.com',
205+
validation: CertificateValidation.fromDns(exampleCom),
206+
subjectAlternativeNames: ['*.test.example.com', '*.foo.test.example.com', 'bar.test.example.com'],
207+
});
208+
209+
//Wildcard domain names are de-duped.
210+
expect(stack).toHaveResource('AWS::CertificateManager::Certificate', {
211+
DomainName: 'test.example.com',
212+
DomainValidationOptions: [
213+
{
214+
DomainName: 'test.example.com',
215+
HostedZoneId: {
216+
Ref: 'ExampleCom20E1324B',
217+
},
159218
},
160-
},
161-
],
162-
ValidationMethod: 'DNS',
219+
{
220+
DomainName: '*.foo.test.example.com',
221+
HostedZoneId: {
222+
Ref: 'ExampleCom20E1324B',
223+
},
224+
},
225+
{
226+
DomainName: 'bar.test.example.com',
227+
HostedZoneId: {
228+
Ref: 'ExampleCom20E1324B',
229+
},
230+
},
231+
],
232+
ValidationMethod: 'DNS',
233+
});
163234
});
235+
164236
});
165237

166238
test('CertificateValidation.fromDnsMultiZone', () => {

0 commit comments

Comments
 (0)