Serverless-next.js: Access denied for translation files in aws s3

Created on 21 Oct 2020  ยท  13Comments  ยท  Source: serverless-nextjs/serverless-next.js

So. First, hello, and thanks a lot for your hard work :)

I am trying to create a POC for an ecommerce. We use Next.JS, and are trying to deploy on serverless. Everything was working fine until I add next-i18next.

At this point, I have the same problem as in #383 . I use the workaround described by @loukmane-issa in his comment, which partially solve the problem: lambda can access at runtime to translation files.

Describe the bug

Browser side, it looks like my application cannot fetch translations (JSON files). XHR request result to the this error:

GET https://{id}.cloudfront.net/static/locales/en/common.json

<?xml version="1.0" encoding="UTF-8"?>
<Error>
   <Code>AccessDenied</Code>
   <Message>Access Denied</Message>
   <RequestId>26D0CBE52D5ACE40</RequestId>
   <HostId>{id}</HostId>
</Error>

In my s3 management console, I can see that translations files exist:

_next/
public/
  static/
    locales/
      en/
      de/

I have also check the manifest.json and I can see that translation files are availables :

โฏ cat .serverless_nextjs/default-lambda/manifest.json | json_pp
{
   // ...
   "publicFiles" : {
      "/static/locales/de/common.json" : "static/locales/de/common.json",
      "/static/locales/en/common.json" : "static/locales/en/common.json",
      "/favicon.ico" : "favicon.ico",
      "/vercel.svg" : "vercel.svg"
   },
   // ...
}

Expected behavior

GET https://{id}.cloudfront.net/static/locales/en/common.json

Fetching this url retrieve the JSON.

I think this is related to permissions, but not sure how to solve that problem.

Steps to reproduce

Well, it's very hard to tell because it looks like it's related to some build configuration. I can provide a repo, but without aws access. If it can help you just tell me, and I'll do it.

Screenshots/Code/Logs

image

Versions

  • OS/Environment: MacOS
  • @sls-next/serverless-component version: 1.15.0-alpha.2
  • Next.js version: 9.5.5
  • next-i18next version: "^6.0.3",

  • [x] You have reviewed the README and FAQs, which answers several common questions.

  • [ ] Please first try using the latest @sls-next/serverless-component release version, which may have already fixed your issue. Note that the old serverless-next.js component and the serverless-next.js plugin are deprecated and no longer maintained.

Most helpful comment

@doliG yeah, I have added a build failure when it detects this since it conflicts.

All 13 comments

Thanks @doliG for submitting the issue. Are you able to repro the same issue on the latest alpha @sls-next/serverless-component? As that 1.15.0-alpha.2 is fairly old and various routing issues were fixed since then.

If you are still getting 403 usually it can mean the request path is not correctly mapped to S3. As you are trying to access https://{id}.cloudfront.net/static/locales/en/common.json it first hits the handler and figures if it's a public file, then the handler will need to map it to public/static/locales/en/common.json key in S3 (e.g: https://github.com/serverless-nextjs/serverless-next.js/blob/master/packages/libs/lambda-at-edge/src/default-handler.ts#L340, it will prepend /public and normalize the URI to build the S3 key).

@doliG could you try to move the locales out of the "static" folder? I think I had this issue and doing this fixed it.

If it doesn't work, could you share your S3 bucket policies and also CORS configuration, so I can compare with my settings and see if this could help?

@doliG I had the same issue and I moved them out of static folder.
public/locales/ instead of public/static/locales

Also I think even in latest alpha, static/* goes through different cloudfront behavior which doesn't seem to have origin request handler set to retrieve from S3. So it tries to find /static/locales/en/common.json but this key does not exist in S3. As mentioned, moving out of static folder would work.

@danielcondemarin on the component side, could we move static files in static folder instead of under public/static?

Actually @danielcondemarin I'm wondering if the static/* cache behavior is even needed anymore, since the default one has a minTTL of 0, defaultTTL of 0, and and maxTTL of 1 year. And for public files we are setting cache control headers on each of the files anyway, which falls within that TTL range.

So it seems we can either just get rid of it so static files can be served properly via the Lambda@Edge handler on the default cache behavior, or attach a Lambda@Edge handler to the static/* cache behavior. Perhaps the latter so that static files can have better CloudFront TTL configuration.

Hello, thanks a lot guys for your help !


Thanks @doliG for submitting the issue. Are you able to repro the same issue on the latest alpha @sls-next/serverless-component? As that 1.15.0-alpha.2 is fairly old and various routing issues were fixed since then.

@dphang , in order to extends NextJsComponent I have installed the package "serverless-next.js, and the latest version is 1.15.0-alpha.2. I changed it to "@sls-next/serverless-component": "^1.17.0" , but it does not work as expected: i have the following error message:

// serverless.js
const NextJsComponent = require('@sls-next/serverless-component');

class MyNextJsComponent extends NextJsComponent {
 // ...
}
// Console output:
Error: Cannot find module '@sls-next/aws-cloudfront'
Require stack:
- ./node_modules/@serverless/core/src/Component.js
- ./node_modules/@serverless/core/src/index.js
- ./node_modules/@sls-next/serverless-component/dist/component.js
- ./node_modules/@sls-next/serverless-component/serverless.js
- ./serverless.js
- /snapshot/serverless/node_modules/@serverless/cli/src/index.js
- /snapshot/serverless/bin/serverless.js
1) If you want to compile the package/file into executable, please pay attention to compilation warnings and specify a literal in 'require' call.
2) If you don't want to compile the package/file into executable and want to 'require' it from filesystem (likely plugin), specify an absolute path in 'require' call using process.cwd() or process.execPath.

It looks like it cannot resolve @sls-next/aws-cloudfront for some reasons. I've checked, and the package is installed in node_modules ๐Ÿค”

โฏ yarn list --pattern @sls-next
yarn list v1.22.10
โ”œโ”€ @sls-next/[email protected]
โ”œโ”€ @sls-next/[email protected]
โ”œโ”€ @sls-next/[email protected]
โ”œโ”€ @sls-next/[email protected]
โ”œโ”€ @sls-next/[email protected]
โ”œโ”€ @sls-next/[email protected]
โ”œโ”€ @sls-next/[email protected]
โ””โ”€ @sls-next/[email protected]

I feel like I'm missing something really simple, but can't figure out what. Any help to extends the latest version of sls-next is welcome :)


I've tried to move locale outisde the "static" folder, as suggested by @loukmane-issa and @LuisFros, and it works ๐ŸŽ‰ That's great.

The only thing that bother me is that I'm wondering why it can't access the static folder, and if it can cause problem with other assets in the future ๐Ÿค”

@doliG Sorry I don't extend the component so not sure about that. It is being updated and published to npm regularly though: https://www.npmjs.com/package/@sls-next/aws-cloudfront

As mentioned before, the static/* paths have its own behavior and even in 1.17.0 and later, it looks like it doesn't have origin request handler attached to it, which I think is the problem. Since the files are uploaded to public/static/* key in S3 but you are trying to access static/* URL, there is no handler to properly map your request to S3 - CloudFront tries to access static/* which doesn't exist in S3 and hence gives a 403 status.

I will make a fix to properly attach the origin request handler to static/* behavior.

Actually I think this is intentional, so I won't make any change yet. If you have static folder at base of your Next app (outside of public), it gets uploaded to s3 into static path. So it will be accessible to CloudFront -> S3 directly without having to use the handler to do routing. For stuff in public folder, the handler is needed to route those files at root of the domain.

The proper way, at least for the later versions (not sure if old version you are using is working correctly), is to do one of the following:

  1. Put assets in static folder at root of your Next.js app. Then this is uploaded directly to S3 in static key and CloudFront can access directly from S3 without using Lambda handler. (static/* behavior does not have handler attached to it).
  2. Put assets in public folder but don't put in public/static. Because the problem is it will generate a route static/* for public files, and this conflicts with the static/* cache behavior, so it will never be able to route correctly.

The difference between 1 and 2 is the use of the handler - if you use the handler there is potentially a bit more overhead due to Lambda cold starts.

I guess one fix is to fail the build if it detects any assets in public/static as that cannot be routed correctly.

Hello @dphang ,

Thanks you for your explanation, it make sense now.

I think this is a good idea to throw a warning at build time if a static folder exist inside the public one, and to explain that it will conflicts with static/ cache behavior.

Tell me if I can help.

So it seems we can either just get rid of it so static files can be served properly via the Lambda@Edge handler on the default cache behavior, or attach a Lambda@Edge handler to the static/* cache behavior. Perhaps the latter so that static files can have better CloudFront TTL configuration.

@dphang Yeah the static/ folder is supported for legacy reasons. The intention was to remove it once public/ was stable for a few versions. It would need to be a breaking change though, to make sure folks update accordingly.

@doliG yeah, I have added a build failure when it detects this since it conflicts.

Yes @dphang , I can see it in #709 .

Thanks a lot, to me this issue as resolved, feel free to close it.

@dphang - I've added the static files I need to ./static and I'm no longer getting issues. However, I do get a warning from next.js stating that the static folder is being deprecated...

The static directory has been deprecated in favor of the public directory. https://err.sh/vercel/next.js/static-dir-deprecated

Do we need another method for adding static assets support with this component?

Was this page helpful?
0 / 5 - 0 ratings