Skip to content

aws-ses: DKIM records are created with incorrect name from EmailIdentity #21306

@Booligoosh

Description

@Booligoosh

Describe the bug

When creating a new ses.EmailIdentity construct, the names of the DKIM records that get created in Route53 are incorrect.

Expected Behavior

If the DKIM record name is x.y.example.com, and the hosted zone is y.example.com, the name of the record that gets set in Route53 should be x.y.example.com

Current Behavior

If the DKIM record name is x.y.example.com, and the hosted zone is y.example.com, the name of the record that gets set in Route53 should is x.y.example.com.y.example.com

Reproduction Steps

Here is a simple reproduction case:

const myHostedZone = ...; // However you're grabbing or creating your hosted zone

const sesIdentity = new ses.EmailIdentity(this, "ses-identity", {
  identity: ses.Identity.publicHostedZone(hostedZone),
  mailFromDomain: `mail.${hostedZoneDomain}`,
});

Possible Solution

The reason for the incorrect record names is because of the function here in the source code:.

if (hostedZone) {
new route53.CnameRecord(emailIdentity, 'DkimDnsToken1', {
zone: hostedZone,
recordName: Lazy.string({ produce: () => emailIdentity.dkimDnsTokenName1 }),
domainName: Lazy.string({ produce: () => emailIdentity.dkimDnsTokenValue1 }),
});
new route53.CnameRecord(hostedZone, 'DkimDnsToken2', {
zone: hostedZone,
recordName: Lazy.string({ produce: () => emailIdentity.dkimDnsTokenName2 }),
domainName: Lazy.string({ produce: () => emailIdentity.dkimDnsTokenValue2 }),
});
new route53.CnameRecord(hostedZone, 'DkimDnsToken3', {
zone: hostedZone,
recordName: Lazy.string({ produce: () => emailIdentity.dkimDnsTokenName3 }),
domainName: Lazy.string({ produce: () => emailIdentity.dkimDnsTokenValue3 }),
});

It calls route53.CnameRecord, which accepts a record name excluding the hosted zone suffix - eg. for x.y.example.com where the hosted zone is y.example.com, the record name should just be x.

However, it's passing in dkimDnsTokenName1 etc, which according to the Cloudformation docs, is the entire host for the record, including the hosted zone.

This means that route53.CnameRecord tried to append the hosted zone name onto the end of the record name which already includes the hosted zone name, leading to the duplication.

This can be seen in a cdk synth:

...
    Type: AWS::Route53::RecordSet
    Properties:
      Name:
        Fn::Join:
          - ""
          - - Fn::GetAtt:
                - <Our SES Entity ID>
                - DkimDNSTokenName1
            - .y.example.com.
      Type: CNAME
...

There are 2 fixes that I can think of:

  1. Call CfnRecordSet rather than CnameRecord - eg:
new route53.CfnRecordSet(emailIdentity, "DkimDnsToken1", {
  hostedZoneName: hostedZone.zoneName + ".",
  name: Lazy.string({ produce: () => emailIdentity.dkimDnsTokenName1 }),
  type: "CNAME",
  resourceRecords: [Lazy.string({ produce: () => emailIdentity.dkimDnsTokenValue1 })],
  ttl: "1800",
})
  1. Use a hacky workaround to lop off the suffix before passing to CnameRecord - by combining cdk.Fn.select and cdk.Fn.split to remove the last part of the string. Here's how that would look:
// This function removes a string from the end of another string using Cfn functions.
// For example, calling it on "abcd" with suffix "cd" would return "ab".
// We accomplish this by splitting by the suffix (which gives ["ab"]), then selecting the 0th value.
const hackyCfnRemoveSuffix = (sourceString: string, suffixToRemove: string) =>
  cdk.Fn.select(0, cdk.Fn.split(suffixToRemove, sourceString));

// Then use it in the bind function
new route53.CnameRecord(emailIdentity, 'DkimDnsToken1', {
  zone: hostedZone,
  recordName: hackyCfnRemoveSuffix(Lazy.string({ produce: () => emailIdentity.dkimDnsTokenName1 }), "." + hostedZone.zoneName),
  domainName: Lazy.string({ produce: () => emailIdentity.dkimDnsTokenValue1 }),
});

Additional Information/Context

No response

CDK CLI Version

2.29.0 (build 47d7ec4)

Framework Version

2.33.0

Node.js Version

v16.15.1

OS

MacOS

Language

Typescript

Language Version

No response

Other information

Workaround

For anyone else experiencing this issue - we've opted to go with the CfnRecordSet option as it's a bit simpler to see what's going on. Simply map through each of the dkimRecords after creating the EmailIdentity and create the correct records - note that this won't remove the incorrect ones unfortunately.

Code below:

// Create SES email identity based on Route53 hosted zone
const sesIdentity = new ses.EmailIdentity(this, "ses-identity", {
  identity: ses.Identity.publicHostedZone(hostedZone),
  mailFromDomain: `mail.${hostedZone.zoneName}`,
});

// Temporary workaround: There is a bug with the EmailIdentity construct where if the DKIM record is x.y.example.com,
// and the hosted zone is y.example.com, the record that gets set is x.y.example.com.y.example.com. For now, we are
// manually creating the correct records.
const dkimCnameRecords = sesIdentity.dkimRecords.map(
  (dkimRecord, index) =>
    new route53.CfnRecordSet(this, `dkim-record-${index}`, {
      hostedZoneName: hostedZone.zoneName + ".",
      name: dkimRecord.name,
      type: "CNAME",
      resourceRecords: [dkimRecord.value],
      ttl: "1800",
    })
);

Metadata

Metadata

Assignees

Labels

@aws-cdk/aws-sesRelated to Amazon Simple Email ServicebugThis issue is a bug.needs-triageThis issue or PR still needs to be triaged.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions