aws / aws/aws-cdk

aws-certificatemanager: invalid cloudformation generated when using wildcard domain to create public certificate

Open
#27,364 4 comments 5 reactions 0 assignees View on GitHub
@aws-cdk/aws-certificatemanager bug p2
Dominant language
TypeScript
Stars
12.9k
Forks
4.6k
Avg merge
1d 19h
Merged PRs (30d)
71

Description

### Describe the bug

When creating a public certificate with a wildcard subdomain, invalid Cfn template is generated and stack creation fails. Cfn includes the same domain validation records twice with exactly the same values which fails when sending the DNS change batch.

Edit: after further investigation, this happens when importing SSM parameters from cross region using the suggested workaround here: https://github.com/henrist/cdk-cross-region-params/blob/master/src/parameter-reader.ts or https://stackoverflow.com/questions/71246435/how-to-read-parameter-store-from-a-different-region-in-cdk. This is a fairly well used way of getting around restrictions with cross-region exports etc.

### Expected Behavior

When creating a certificate with wildcard DNS, the correct Cfn is generated and can be deployed.

Expected Cfn output (slightly edited to avoid private information):

```
"Certificate4E7ABB08": {
"Type": "AWS::CertificateManager::Certificate",
"Properties": {
"DomainName": {
"Fn::GetAtt": [
"ZoneNameReaderFunctionCBCCE744",
"Parameter.Value"
]
},
"DomainValidationOptions": [
{
"DomainName": {
"Fn::GetAtt": [
"ZoneNameReaderFunctionCBCCE744",
"Parameter.Value"
]
},
"HostedZoneId": {
"Fn::GetAtt": [
"ZoneIdReaderFunction84D6D92A",
"Parameter.Value"
]
}
}
],
"SubjectAlternativeNames": [
{
"Fn::Join": [
"",
[
"*.",
{
"Fn::GetAtt": [
"ZoneNameReaderFunctionCBCCE744",
"Parameter.Value"
]
}
]
]
}
],
"Tags": [
{
"Key": "Name",
"Value": "PipelineStack/TestEnvironmentStacksDeployment/CertificateStack-eu-west-1/Certificate"
}
],
"ValidationMethod": "DNS"
},
"Metadata": {
"aws:cdk:path": "PipelineStack/TestEnvironmentStacksDeployment/CertificateStack-eu-west-1/Certificate/Resource"
}
}
```

### Current Behavior

When creating a certificate with wildcard DNS, the incorrect Cfn is generated and can be deployed.

Example Cfn output (slightly edited to avoid private information):

```
"Certificate4E7ABB08": {
"Type": "AWS::CertificateManager::Certificate",
"Properties": {
"DomainName": {
"Fn::GetAtt": [
"ZoneNameReaderFunctionCBCCE744",
"Parameter.Value"
]
},
"DomainValidationOptions": [
{
"DomainName": {
"Fn::GetAtt": [
"ZoneNameReaderFunctionCBCCE744",
"Parameter.Value"
]
},
"HostedZoneId": {
"Fn::GetAtt": [
"ZoneIdReaderFunction84D6D92A",
"Parameter.Value"
]
}
},
{
"DomainName": {
"Fn::Join": [
"",
[
"*.",
{
"Fn::GetAtt": [
"ZoneNameReaderFunctionCBCCE744",
"Parameter.Value"
]
}
]
]
},
"HostedZoneId": {
"Fn::GetAtt": [
"ZoneIdReaderFunction84D6D92A",
"Parameter.Value"
]
}
}
],
"SubjectAlternativeNames": [
{
"Fn::Join": [
"",
[
"*.",
{
"Fn::GetAtt": [
"ZoneNameReaderFunctionCBCCE744",
"Parameter.Value"
]
}
]
]
}
],
"Tags": [
{
"Key": "Name",
"Value": "PipelineStack/TestEnvironmentStacksDeployment/CertificateStack-eu-west-1/Certificate"
}
],
"ValidationMethod": "DNS"
},
"Metadata": {
"aws:cdk:path": "PipelineStack/TestEnvironmentStacksDeployment/CertificateStack-eu-west-1/Certificate/Resource"
}
}
```

Both of the `DomainValidationOptions` generate the same validation CNAME record. When the call to put records is done, it includes a duplicate record. See the below CloudTrail for example:

```
"errorCode": "InvalidChangeBatch",
"errorMessage": "[The request contains an invalid set of changes for a resource record set 'CNAME _00e851c0ad3747082dea8abddd0f1515.test.my-domain.com.']",
"requestParameters": {
"hostedZoneId": "MY_ZONE_ID",
"changeBatch": {
"changes": [
{
"action": "UPSERT",
"resourceRecordSet": {
"name": "_thecnamegenerated.test.my-domain.com.",
"type": "CNAME",
"tTL": 300,
"resourceRecords": [
{
"value": "_thecnamegenerated.mrhcxwpsky.acm-validations.aws."
}
]
}
},
{
"action": "UPSERT",
"resourceRecordSet": {
"name": "_thecnamegenerated.test.my-domain.com.",
"type": "CNAME",
"tTL": 300,
"resourceRecords": [
{
"value": "_thecnamegenerated.mrhcxwpsky.acm-validations.aws."
}
]
}
}
]
}
},
```

### Reproduction Steps

```
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { HostedZone } from 'aws-cdk-lib/aws-route53';
import { Certificate, CertificateValidation } from 'aws-cdk-lib/aws-certificatemanager';

export class CertificateStack extends Stack {
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props);

const zoneId = new SSMParameterReader(this, 'ZoneId', {
parameterName: DomainStack.HOSTED_ZONE_ID_PARAMETER,
region: 'us-east-1'
}).getParameterValue();

const zoneName = new SSMParameterReader(this, 'ZoneName', {
parameterName: DomainStack.HOSTED_ZONE_NAME_PARAMETER,
region: 'us-east-1'
}).getParameterValue();

const zone = HostedZone.fromHostedZoneAttributes(this, 'Zone', {
hostedZoneId: zoneId,
zoneName: zoneName // eg. my-domain.com
});

const sharedCertificate = new Certificate(this, 'Certificate', {
domainName: zoneName, // eg. my-domain.com
subjectAlternativeNames: [`*.${zoneName}`], // eg. *.my-domain.com
validation: CertificateValidation.fromDns(zone)
});
}
}
```

### Possible Solution

Unsure the best way to deal with this to be honest. Potentially need to understand more about the scenarios that domain validation cname records can clash. When they clash, some logic may need to be executed.

The current workaround I've put in place is:

```
private patchCertificateValidation(certificate: Certificate) {
const cfnCertificate = certificate.node.defaultChild as CfnCertificate;

if (Array.isArray(cfnCertificate.domainValidationOptions) && cfnCertificate.domainValidationOptions.length === 2) {
delete cfnCertificate.domainValidationOptions[1];
}
}
```

Of course this works for my scenario but may need to be updated for other peoples requirements depending on what domains and alternative domains are requested. It could be improved to read all domains in the list and look specifically for wildcards that need to be removed.

### Additional Information/Context

Unsure if it would help since most people won't have access to the case details but this fix was suggested by AWS Support in case 13934396421. This was roughly the response:

> Upon dive deep, I found that cloudtrail request which got failed as InvalidChangeBatch - 834fb28e-8ec1-47a3-ab8c-3c83cfd8b410 - has two UPSERT actions of same record and value. Upon checking the CloudFormation template and checking with CloudFormation engineer, they have suggested to remove the wildcard value under the DomainValidationOptions.
>
> Please keep the SubjectAlternativeNames with both Apex domain (test.my-domain.com.) and Wildcard domain (*.test.my-domain.com.). This will be able to create the ACM certificate for both the domains and certificate will also be able to get validated by DNS.

### CDK CLI Version

2.94.0

### Framework Version

_No response_

### Node.js Version

v18.12.1

### OS

Ubuntu 22.04.2 LTS

### Language

Typescript

### Language Version

4.9.5

### Other information

Also tested on the latest version of the CDK (`2.99.1`) with the same outcome.

Probably related to https://github.com/aws/aws-cdk/issues/15574.

Only seems to be an issue using `HostedZone.fromHostedZoneAttributes`. Other code paths appear to work. Also noted in https://github.com/aws/aws-cdk/issues/9248.

This was supposedly fixed in https://github.com/aws/aws-cdk/pull/9291/files but that doesn't appear to be the case.

Contributor guide

Open the contributing guide

Research direction

Reproduce the issue with Certificate, CertificateValidation.fromDns, and HostedZone.fromHostedZoneAttributes in the provided TypeScript example, then inspect the generated CfnCertificate DomainValidationOptions. Compare the output with the expected template and related issues 15574 and 9248. Done means wildcard and apex names remain in SubjectAlternativeNames without duplicate validation records, and the certificate deployment succeeds.

Written by the indexing model from the issue text.

Assessment

Tech stack
aws, typescript
Domain
cloud, infrastructure
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.