simonw / public-notes

Public notes as issue threads
22 stars 0 forks source link

Figure out how to serve an AWS Lambda function with a Function URL from a custom subdomain #1

Closed simonw closed 1 year ago

simonw commented 1 year ago

[This issue thread provides a blow-by-blow account of how I figured out the way to serve an AWS Lambda function from a custom domain, using CloudFront and ACM]

I think Cloudfront is the way to do this:

My previous notes on how I shipped the Lambda Function URL are here: https://til.simonwillison.net/awslambda/asgi-mangum

simonw commented 1 year ago

I'm going to first try setting up a CloudFront distribution. If I can get that running in front of a Lambda URL I'll have solved half the problem - the other half will be figuring out how to point a custom domain at CloudFront.

Some useful docs:

simonw commented 1 year ago

I'm going to skim through and make notes on the whole Amazon CloudFront developer guide: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html

simonw commented 1 year ago

It's basically a caching CDN like Fastly. You don't pay for transfer between your Amazon-hosted application servers and CloudFront, but you do pay for bandwidth served between CloudFront and end users.

simonw commented 1 year ago

Optionally, you can configure your origin server to add headers to the files, to indicate how long you want the files to stay in the cache in CloudFront edge locations. By default, each file stays in an edge location for 24 hours before it expires.

Sounds like max-age or s-maxage is pretty important then!

simonw commented 1 year ago

CloudFront can also run "Lambda@Edge" functions, but these can currently only be written in JavaScript.

simonw commented 1 year ago

You can chose a cheaper plan that doesn't have POP locations in some countries: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PriceClass.html

simonw commented 1 year ago

https://fnkvspusjrl5dxytaxnuwidxem0hverw.lambda-url.us-east-1.on.aws/ is the function URL I want to use.

simonw commented 1 year ago

Trying this on https://us-east-1.console.aws.amazon.com/cloudfront/v3/home?region=us-east-1&skipRegion=true#/distributions/create

Screen Shot 2022-10-02 at 12 37 44

simonw commented 1 year ago

https://us-east-1.console.aws.amazon.com/cloudfront/v3/home?region=us-east-1&skipRegion=true#/distributions/E14OFPHUGRRDXW

image
simonw commented 1 year ago

https://d81numqdze872.cloudfront.net/ is not working yet.

simonw commented 1 year ago

OK it works now.

simonw commented 1 year ago

Hitting the same page twice with curl:

~ % time curl 'https://d81numqdze872.cloudfront.net/fixtures/compound_three_primary_keys'
...
</html>curl   0.02s user 0.02s system 4% cpu 0.764 total
~ % time curl 'https://d81numqdze872.cloudfront.net/fixtures/compound_three_primary_keys'
...
</html>curl   0.02s user 0.01s system 26% cpu 0.117 total
simonw commented 1 year ago

I don't think querystring parameters are being passed through correctly.

https://d81numqdze872.cloudfront.net/fixtures/no_primary_key?_facet=a ignores the facet request.

But https://fnkvspusjrl5dxytaxnuwidxem0hverw.lambda-url.us-east-1.on.aws/fixtures/no_primary_key?_facet=a works correctly.

simonw commented 1 year ago

https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html#DownloadDistValuesQueryString

I think I want "Forward all, cache based on all" for query strings, somewhere under "Cache behavior settings".

simonw commented 1 year ago

Looks like that's hidden away under "legacy cache settings":

legacy

simonw commented 1 year ago

That seemed to take effect pretty fast, https://d81numqdze872.cloudfront.net/fixtures/sortable?_facet=pk2 works now.

simonw commented 1 year ago

Got to that edit interface by clicking:

CleanShot 2022-10-02 at 12 55 56@2x

simonw commented 1 year ago

I can't figure out how to enable the x-cache header for Cloudfront.

simonw commented 1 year ago

Weird. I'm getting those headers now!

I tried adding and then removing a Response Headers Policy to the distribution's behaviour, but I don't know if that made a difference here or not. Maybe it was working anyway?

Janky video trying to show where I found that option:

policy

simonw commented 1 year ago

OK, I have that header now and it seems to confirm my 5s TTL is working as it should

Next job: point a custom domain at https://d81numqdze872.cloudfront.net/ !

simonw commented 1 year ago

https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/CNAMEs.html shows how to do that for HTTP (I'll add HTTPS later, following https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-https-alternate-domain-names.html ):

  1. Sign in to the AWS Management Console and open the CloudFront console at https://console.aws.amazon.com/cloudfront/v3/home
  2. Choose the ID for the distribution that you want to update.
  3. On the General tab, choose Edit.
  4. Update the following values: Alternate Domain Names (CNAMEs) Add your alternate domain names. Separate domain names with commas, or type each domain name on a new line.
simonw commented 1 year ago
image

Now in my DNS provider:

Use the method provided by your DNS service provider to add a CNAME record for your domain. This new CNAME record will redirect DNS queries from your alternate domain name (for example, www.example.com) to the CloudFront domain name for your distribution (for example, d111111abcdef8.cloudfront.net). For more information, see the documentation provided by your DNS service provider.

simonw commented 1 year ago

Did this in Vercel:

image

Just noticed I got this error in AWS when I tried to save that though:

image
simonw commented 1 year ago

Decided to click "Request certificate" and see what happens:

Sent me to https://us-east-1.console.aws.amazon.com/acm/home?region=us-east-1#/certificates/request

CleanShot 2022-10-02 at 14 55 14@2x image image

Looks like this has the validation bit: https://us-east-1.console.aws.amazon.com/acm/home?region=us-east-1#/certificates/98a4b28c-d7d1-4ad6-ae28-4db269e03ccc

image
simonw commented 1 year ago

In Vercel:

image
simonw commented 1 year ago

A few seconds later:

image

Annoyingly that certificate is not shown in this drop-down menu:

image

Maybe because this now says "Failed": https://us-east-1.console.aws.amazon.com/acm/home?region=us-east-1#/certificates/98a4b28c-d7d1-4ad6-ae28-4db269e03ccc

image
simonw commented 1 year ago

I'm deleting the certificate and trying again.

simonw commented 1 year ago

I think I didn't put the right values into Vercel.

image

Trying with the full copy of these:

simonw commented 1 year ago

That came back "Failed" too.

simonw commented 1 year ago

I'm going to try email validation.

simonw commented 1 year ago
image

Looks like it emailed these addresses:

image
simonw commented 1 year ago

I don't think I have email configured for that domain at all, so this is not going to work.

simonw commented 1 year ago

Docs: https://docs.aws.amazon.com/acm/latest/userguide/domain-ownership-validation.html

simonw commented 1 year ago

https://docs.aws.amazon.com/acm/latest/userguide/dns-validation.html says:

If your DNS provider does not support CNAME values with a leading underscore, see Troubleshoot DNS Validation Problems.

https://docs.aws.amazon.com/acm/latest/userguide/troubleshooting-DNS-validation.html says:

If your DNS provider prohibits leading underscores in CNAME values, you can remove the underscore from the ACM-provided value and validate your domain without it. For example, the CNAME value _x2.acm-validations.aws can be changed to x2.acm-validations.aws for validation purposes. However, the CNAME name parameter must always begin with a leading underscore.

I think Vercel may have stripped the trailing .:

image
simonw commented 1 year ago

Yeah even if I try and add that back in again Vercel saves it without the trailing ..

simonw commented 1 year ago

https://dev.to/wulfmann/generating-acm-certificates-for-a-vercel-managed-domain-gk5 says:

AWS provides documentation on how to configure a CAA record to allow ACM to generate certs. We need to add an extra record in Vercel:

CAA 0 issue "amazon.com"

Request a new ACM cert and this time it succeeds!

simonw commented 1 year ago
image

I think that worked! Created a new certificate and went through the DNS validation process and:

image

ARN of the certificate is:

arn:aws:acm:us-east-1:462092780466:certificate/66e7114b-6215-4c66-935c-397d60986b9a
simonw commented 1 year ago

And now it shows up here:

image

Selected it and now this looks promising:

image
simonw commented 1 year ago

http://lambda-demo.datasette.io/ (HTTP) shows this:

image

Maybe that will fix itself in a few seconds/minutes?

simonw commented 1 year ago
~ % curl -i 'http://lambda-demo.datasette.io/'
HTTP/1.1 403 Forbidden
Server: CloudFront
Date: Sun, 02 Oct 2022 22:19:51 GMT
Content-Type: text/html
Content-Length: 915
Connection: keep-alive
X-Cache: Error from cloudfront
Via: 1.1 549a5eaa264d3b997d6acfdba72f56d0.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: SFO5-P1
X-Amz-Cf-Id: SC5jwUCUsNhyLxpWaw4S9XuNy8tIYnmYyOGf3g4uBAOjzfkgUteS3A==
simonw commented 1 year ago

But https://d81numqdze872.cloudfront.net/ still works.

Will https://lambda-demo.datasette.io/ work soon? Is the deploy still going out?

simonw commented 1 year ago
image

That suggests it is still serving the *.cloudfront.net certificate, not my now lambda-demo.datasette.io certificate.

simonw commented 1 year ago

Maybe this should be filled in?

CleanShot 2022-10-02 at 15 23 32@2x

simonw commented 1 year ago

Editing here:

image
simonw commented 1 year ago

And it works!!

https://lambda-demo.datasette.io/

simonw commented 1 year ago

So I did it. I managed to point a custom domain and certificate at a Lambda Function URL!

It was really fiddly, and took a lot of steps. And I've so far only done it through the console.

I wonder if it's possible to get a wildcard certificate for a domain such that you don't have to do all of this junk for each new subdomain you want to use?

simonw commented 1 year ago

The HTTP version at http://lambda-demo.datasette.io/ doesn't redirect to HTTPS and it should do.

Found this: https://us-east-1.console.aws.amazon.com/cloudfront/v3/home?region=us-east-1&skipRegion=true#/distributions/E14OFPHUGRRDXW/behaviors/0/edit

image

Trying these options:

image
simonw commented 1 year ago

... and about 15 seconds later the redirect works now.

simonw commented 1 year ago

Here's a bug:

https://lambda-demo.datasette.io/fixtures/facetable

Click a suggested facet link and:

https://fnkvspusjrl5dxytaxnuwidxem0hverw.lambda-url.us-east-1.on.aws/fixtures/facetable?_facet=planet_int#facet-planet_int

It's bumped to the Lambda Function URL.

simonw commented 1 year ago

I bet I can fix that with https://datasette.io/plugins/datasette-x-forwarded-host

Maybe by re-deploying that function again, if I can remember how I did that?