sst / ion

❍ — a new engine for SST
https://ion.sst.dev
MIT License
1.19k stars 149 forks source link

Fails to create DNS records with wildcard alias on CloudFlare (Error: Component names must be unique.) #289

Open RyanTheAllmighty opened 3 months ago

RyanTheAllmighty commented 3 months ago

Currently I have a Nextjs application that I'm deploying and using CloudFlare as a DNS provider:

        new sst.aws.Nextjs('App', {
            domain: {
                name: "example.com",
                aliases: ["*.example.com"],
                dns: sst.cloudflare.dns(),
            },
        });

Doing that, ACM will request 2 validations, one for the root domain and then one for the wildcard, but they will both have the same record name:

[
    {
        "domainName": "example.com",
        "resourceRecordName": "_f43842aab9d472519d2848ded75b8d71.example.com.",
        "resourceRecordType": "CNAME",
        "resourceRecordValue": "_91de14983a25616568229b958b2c1830.mhbtsbpdnt.acm-validations.aws."
    },
    {
        "domainName": "*.example.com",
        "resourceRecordName": "_f43842aab9d472519d2848ded75b8d71.example.com.",
        "resourceRecordType": "CNAME",
        "resourceRecordValue": "_91de14983a25616568229b958b2c1830.mhbtsbpdnt.acm-validations.aws."
    }
]

This ends up failing a deploy with the below

× Failed Invalid component name "AppCdnSslCNAMERecordF43842aab9d472519d2848ded75b8d71examplecom". Component names must be unique.

Because the resourceRecordName is used as part of the name, it fails due to not being unique.

The same also happens when it tries to create the actual DNS record pointing to the CloudFront distribution:

× Failed Invalid component name "AppCdnCNAMERecordExamplecom". Component names must be unique.

Seems like when it's creating the names for those, it's running through sanitizeToPascalCase and then stripping the *. so the names end up being the same as seen at https://github.com/sst/ion/blob/dev/pkg/platform/src/components/cloudflare/dns.ts#L70

I'm not sure of the correct fix, but for me I'm currently applying the below changes in my GitHub workflow to get around this issue for now and it works for me by deduplicating the DNS records for validation, and then in the CloudFlare DNS provider, changing the name suffix to explicitly change * to star which definitely feels a bit icky, but for now works to unblock me:

https://github.com/sst/ion/blob/dev/pkg/platform/src/components/aws/dns-validated-certificate.ts#L56-L71

function createDnsRecords() {
      return all([dns, certificate.domainValidationOptions]).apply(
        ([dns, options]) =>
-         options.map((option) =>
+         options.filter((option, index, self) =>
+           index === self.findIndex((t) => (
+             t.resourceRecordName === option.resourceRecordName
+           ))
+         ).map((option) =>
            dns.createRecord(
              name,
              {
                type: option.resourceRecordType,
                name: option.resourceRecordName,
                value: option.resourceRecordValue,
              },
              { parent },
            ),
          ),
      );
    }

https://github.com/sst/ion/blob/dev/pkg/platform/src/components/cloudflare/dns.ts#L70

    return output(record).apply((record) => {
-     const nameSuffix = sanitizeToPascalCase(record.name);
+     const nameSuffix = sanitizeToPascalCase(record.name.replaceAll("*", "star"));
      const zoneId = lookupZone();
      const dnsRecord = createRecord();
fwang commented 3 months ago

@RyanTheAllmighty can you set domain name to *.example.com directly?

        new sst.aws.Nextjs('App', {
            domain: {
                name: "*.example.com",
                dns: sst.cloudflare.dns(),
            },
        });
RyanTheAllmighty commented 3 months ago

@RyanTheAllmighty can you set domain name to *.example.com directly?

        new sst.aws.Nextjs('App', {
            domain: {
                name: "*.example.com",
                dns: sst.cloudflare.dns(),
            },
        });

@fwang It will work yes as there are no conflicting names/records, but *.example.com doesn't cover example.com so there's no cert or DNS record generated for the apex domain. If you need me to confirm that I can, just will take some time, but from my OP it wouldn't seem to be an issue

fwang commented 3 months ago

ah that makes sense.. lemme take a look

ericc59 commented 1 month ago

I'm also seeing this on AWS

guidefari commented 1 month ago

+1 to facing this on AWS. Even sst dev won't work, throwing the same error.

What I've tried: