serverless-next.js icon indicating copy to clipboard operation
serverless-next.js copied to clipboard

Support for multiple domains

Open GraemeAllanBryce opened this issue 4 years ago • 15 comments

CloudFront is capable of supporting multiple domains against a single instance.

This component accepts an "array" for the domain but really it is just two strings that form the domain and optional subdomain that will be assigned in CloudFront and will be used to determine the certificate that should also be assigned.

CloudFront also supports wildcard domains and indeed this IS supported by the component. I can specify:

["*.booking", "mydomain.com"] and all works as expected.

However I really want to provide an array of domain objects having each of them assigned to the CloudFront.

This is not perhaps as simple a change as it may appear. There are two very operations being carried out by the component, The first is to use the main domain string to find (or create) a certificate that can be used in the distribution and the second is to add the domain mapping to CloudFront. Potentially therefore the certificate must contain ALL of the domains.

Ideally I would like either to see separation of the certificate and domain mapping or to have the component create a certificate from a true array of domains and/or sub domains.

In order to maintain backwards compatibility I guess the array should remain but could be either an array of (two) strings as it is now or an array of objects each defining a subdomain and domain.

My eventual config may then be as follows:

[
  {sub: "*.booking", domain: "mydomain.com"},
  {sub: "special", domain: "an-other.com"}  
]

This would require that the component create a single certificate covering all of the domains and sub domain and then add all of the mappings to CloiudFront.

I welcome any comments on how this may be achieved.

Thanks

GraemeAllanBryce avatar Sep 29 '20 10:09 GraemeAllanBryce

Yeah, I think this make sense, I have seen similar requests in other issues. That's a great summary. A temporary workaround could be to not use domain input and instead use Terraform to manage the domain records / certificate, but then you may have possible issues since both Terraform + this component are managing the same resource. From my personal experience, I didn't have conflicts doing it this way as long as you don't have shared configuration (e.g in this component, I don't specify anything related to domains or certificates, Terraform manages all that).

Perhaps having a new input domains which accepts a list of explicit domains (including www or apex domains) may be cleaner and safer for backwards compatibility. Since there is also domainType (which is used along with domain) for www vs. apex domain. Users can use either domains or domain + domainType but not both. We can then deprecate the domain/domainType inputs (recommending user to use domains instead) and then probably remove it after some time.

dphang avatar Sep 29 '20 18:09 dphang

I agree that would be a cleaner approach

GraemeAllanBryce avatar Sep 29 '20 20:09 GraemeAllanBryce

Now there is a way to set the ACM certificate and aliases separately from domains, thanks to some recent PRs, though you'd still need to create the Route53 domains yourself (either manually or via something like Terraform). I'd still think having domains input will still be useful though.

dphang avatar Oct 10 '20 02:10 dphang

This is a valuable new feature.

I have tested using component version: "@sls-next/[email protected]" with success.

changes applied in serverless.yml

remove domains: [***]

add to inputs certificateArn: "arn:aws:acm:us-east-1:841616887838:certificate/*******-2553-4cad-9f46-***********"

add to the cloudfront config aliases: ["*.mydomain.com", "[email protected]", "etc etc"]

deploy.

NOTE: The aliases are added to the cloudFront section and not to the inputs - easy to miss this although to be fair it is correct in the docs :-)

GraemeAllanBryce avatar Oct 12 '20 13:10 GraemeAllanBryce

@GraemeAllanBryce I tried the following but I'm getting this error: TypeError: Missing parameter name at 5. I believe it happens because domains is missing. What did you set for the domains input?

Config looks like this:

app:
  component: '@sls-next/[email protected]'
  inputs:
    certificateArn: 'arn'
    cloudfront:
      aliases: ['alias']

Meemaw avatar Oct 16 '20 11:10 Meemaw

@Meemaw

I have since discovered that our configuration as detailed above only works AFTER an initial deploy that did indeed have a domains: [] node. We subsequently removed it but understand that some of the data was cached in the .serverless folder.

Thus I think you can solve your problem by at least having one domain in the domains node and providing a full set (including the one in domains) in the aliases config.

It is also apparent that the domains node causes the creation of a new certificate but this is then overridden by the certificateArn node in the config.

GraemeAllanBryce avatar Oct 21 '20 13:10 GraemeAllanBryce

This is a very weird behaviour.

So actually I would like to achieve the following: Deploy my NextJS application on every pull request. This way application could be tested in a realistic environment before merging the PR. The domain would be using an existing wildcard certificate of the following shape "*.mydomain.com" and the Cloudfront alias would be "pr-1000.mydomain.com".

I think this pattern would be very powerful and I'm sure many people would like to achieve something like this.

Could you take a look into this @dphang ?

Meemaw avatar Oct 21 '20 17:10 Meemaw

We should just have domain input, not domains I think. I also made a fix where unspecified aliases in the inputs was overriding any existing CloudFront aliases, it might be related.

For deploying on every PR, that's useful, perhaps you can do it with using a deployment script like this: https://gist.github.com/dphang/7395ee09f6182f6b34f224660bed8e8c. I use this for multiple environment stages, but maybe you can adapt it for deploying for each PR.

dphang avatar Oct 22 '20 03:10 dphang

Thanks for the script. We need something similar, but AFAIK you are not using the domain component here which is problematic.

I would expect the component to skip any certificate creation if the ARN of existing certificate is provided and the "domains" not being required in that case (aliases should be enough).

Meemaw avatar Oct 22 '20 11:10 Meemaw

I see, sorry, yeah my script is not using it since I am managing the domains/certs outside of this component using Terraform, so I just rely on it to manage the CF distribution and aliases.

Also, I didn't implement it but I do see in the code, it's actually the domain component that has certificateArn input, and is only executed when domain input is set. So it might be the cause of your issues. We probably need to kind of decouple it from the domain logic and move it to aws-cloudfront library instead? I think it should be a relatively straightforward change.

The code to set the certificate is in domain utilities: https://github.com/serverless-nextjs/serverless-next.js/blob/6bb144385c6621c4c5c00443259df538a185f4f2/packages/serverless-components/domain/utils.js#L414-L421

I'm working on fixing some other bugs at the moment, so if you wish, feel free to submit a PR and I'll review.

dphang avatar Oct 23 '20 03:10 dphang

Hey @dphang. I've managed to get this working using the latest alpha and the certificate input (see https://github.com/insights-io/Insight/pull/730/files#diff-3cc804dfa5fb131a9df47aa81e78ac41d4bb1870a60f8dac2dc6da74a1080d13R55) 🎉

It works as expected, but it seems component does not create CNAME records for the aliases. It would be great if it did, WDYT?

Meemaw avatar Oct 31 '20 10:10 Meemaw

@Meemaw glad to see that works. I added it so we can have control of the CloudFront certificate without needing to use the domain input.

It was intentionally not updating any Route53 records since that is outside of CloudFront.

Also, I think sometimes Route53 records may live in another account (like it does for me). If Route53 logic is conflated with CloudFront logic, then it would not work or make it complex.

A possibility is to allow the component to manage Route53 records separately.

I don't want to make it complex like with domain, which does a lot of magic stuff internally with ACM, Route53, CloudFront that's not really visible to the user when configuring it.

All in all, it is getting quite complex with all the IaC stuff, so I'd rather keep it simpler / more explicit for this component. Although it requires more configuration on the user's part, it will avoid complex/strange situations like with the domain input.

In the future we are hoping to simplify it by exploring proper IaC tools like CDK for Terraform.

dphang avatar Oct 31 '20 17:10 dphang

Trying to revive this.

IIUC it's possible to add aliases to CF (which adds entries to "Alternate domain names") if the domain key is removed from config and certificateArn is added. It won't affect Route53 records and I have to add them manully. Is that still the status?

If so, few questions:

  1. Does it work on first deployment that provisions everything in Route53? (the default A, NS, SOA records, and the certificate)
  2. Does the certificate needs to be updated or is it wildcard anyway?

@dphang I agree with your point that Route53 and CF logic should be separated, but it's already tied up in Next component due to the domain key.. Is there anyway to use something that will provision both necessary Route53 records for Next and subdomain holistically? (they can be 2 different tools, but I expect them to be self contained and not "splitting logic" between them)

Thanks!

elad-maimon avatar Apr 25 '22 15:04 elad-maimon

This would be very handy for my business when onboarding new clients we create the following:

someCompanyname.MYDOMAIN.com

AND

www.somecompanyname.com

The reason being is that if we show customers how fast we made their site on the demo url then all they have to do is point their NS at us and the product is live.

Obviously, we could just pop the domain name out to an env var I suppose to cater for the demo use case, but this would certainly simplify the process.

If there's open branches or any work already done we might be able to contribute although we've never looked at the code base for this before.

marcfielding1 avatar Apr 30 '22 10:04 marcfielding1

Hi everyone, LMK how can I help to make this feature happen, It would be really useful!

In Brazil, for example, all the companies like to use www.companyname.com AND www.companyname.com.br, which needs to set every time a new alias for the same app when deploying it.

Schieck avatar Jun 29 '22 13:06 Schieck