Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(aws-lambda/aws-cloudfront): FunctionUrls cannot be added to cloudfront as a http origin #20090

Closed
blacha opened this issue Apr 26, 2022 · 8 comments
Labels
@aws-cdk/aws-cloudfront Related to Amazon CloudFront bug This issue is a bug. effort/small Small work item – less than a day of effort needs-reproduction This issue needs reproduction. p2

Comments

@blacha
Copy link

blacha commented Apr 26, 2022

Describe the bug

When trying to connect a function url into a cloudfront distribution with a HttpOrigin fails

Resource handler returned message: "Invalid request provided: The parameter origin name cannot contain a colon.

Expected Behavior

I would expect to be able to add a LambdaFunctionURL to Cloudfront

Current Behavior

Deployment fails

 ❌  LambdaHttp failed: Error: The stack named LambdaHttp failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: "Invalid request provided: The parameter origin name cannot contain a colon. (Service: CloudFront, Status Code: 400, Request ID: 76dfc5ad-2e21-44db-bfd0-023d08d8a427)" (RequestToken: 925d656b-5ec5-f4fb-e0eb-a37db550d1c1, HandlerErrorCode: InvalidRequest)
    at prepareAndExecuteChangeSet (/home/blacha/tmp/lambda-http/node_modules/aws-cdk/lib/api/deploy-stack.ts:382:13)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at CdkToolkit.deploy (/home/blacha/tmp/lambda-http/node_modules/aws-cdk/lib/cdk-toolkit.ts:209:24)
    at initCommandLine (/home/blacha/tmp/lambda-http/node_modules/aws-cdk/lib/cli.ts:341:12)

 The stack named LambdaHttp failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: "Invalid request provided: The parameter origin name cannot contain a colon. (Service: CloudFront, Status Code: 400, Request ID: 76dfc5ad-2e21-44db-bfd0-023d08d8a427)" (RequestToken: 925d656b-5ec5-f4fb-e0eb-a37db550d1c1, HandlerErrorCode: InvalidRequest)

Reproduction Steps

Given a lambda and cloudfront distribution, trying to hook them together using a HttpOrigin

const lambda = new NodejsFunction(this, 'Lambda', { entry: './echo.js', })
const functionUrl = new FunctionUrl(this, 'LambdaFunctionUrl', { function: lambda, authType: FunctionUrlAuthType.NONE, cors: { allowedOrigins: ['*'] } })
const cf = new Distribution(this, 'Cloudfront', {
  defaultBehavior: {
    origin: new origins.HttpOrigin(functionUrl.url),
    allowedMethods: AllowedMethods.ALLOW_ALL,
    originRequestPolicy: OriginRequestPolicy.ALL_VIEWER
  }
})
new CfnOutput(this, 'CloudfrontUrl', { value: cf.distributionDomainName })
new CfnOutput(this, 'LambdaUrl', { value: functionUrl.url })

Possible Solution

No response

Additional Information/Context

I am assuming that HttpOrigin is how you would expect to connect these, I couldnt see anything in github/docs about how to connect a functionurl to cloudfront.

When creating the function url in the AWS Console then manually connecting them it works fine.

CDK CLI Version

2.21.1 (build a6ee543)

Framework Version

No response

Node.js Version

v16.14.2

OS

Ubuntu 22.04 LTS

Language

Typescript

Language Version

Javascript

Other information

No response

@blacha blacha added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Apr 26, 2022
@github-actions github-actions bot added the @aws-cdk/aws-cloudfront Related to Amazon CloudFront label Apr 26, 2022
@shorn
Copy link

shorn commented Apr 26, 2022

Came here to request this.
For anyone looking for a workaround: https://stackoverflow.com/q/72009805/924597

@ryparker ryparker added p2 needs-reproduction This issue needs reproduction. labels Apr 27, 2022
@peterwoodworth peterwoodworth removed the needs-triage This issue or PR still needs to be triaged. label Apr 27, 2022
@blacha
Copy link
Author

blacha commented Apr 28, 2022

Came here to request this. For anyone looking for a workaround: https://stackoverflow.com/q/72009805/924597

Awesome this fixed part of my problem!

the other slightly weird behaviour was that having a originRequestPolicy: OriginRequestPolicy.ALL_VIEWER caused cloudfront to error and respond {Message: null }

switching to OriginRequestPolicy.CORS_CUSTOM_ORIGIN fixed my problem

So here is a full example stack that now works

import cdk from 'aws-cdk-lib';
import { App, CfnOutput } from 'aws-cdk-lib';
import cf from 'aws-cdk-lib/aws-cloudfront';
import origins from 'aws-cdk-lib/aws-cloudfront-origins';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import lambda from 'aws-cdk-lib/aws-lambda'


export class LfStack extends cdk.Stack {
  constructor(scope, id, props) {
    super(scope, id, props);

    this.lambdaFunction = new NodejsFunction(this, 'Lambda', { entry: './echo.js', })

    this.functionUrl = new lambda.FunctionUrl(this, 'LambdaApiUrl', {
      function: this.lambdaFunction,
      authType: lambda.FunctionUrlAuthType.NONE,
      cors: {
        allowedOrigins: ["*"],
        allowedMethods: [lambda.HttpMethod.GET, lambda.HttpMethod.POST],
        allowCredentials: true,
        maxAge: cdk.Duration.minutes(1)
      }
    });
    const splitFunctionUrl = cdk.Fn.select(2, cdk.Fn.split('/', this.functionUrl.url));
    
    this.distribution = new cf.Distribution(this, 'Cloudfront', {
      defaultBehavior: {
        compress: true,
        originRequestPolicy: cf.OriginRequestPolicy.CORS_CUSTOM_ORIGIN,
        origin: new origins.HttpOrigin(splitFunctionUrl, {
          protocolPolicy: cf.OriginProtocolPolicy.HTTPS_ONLY,
          originSslProtocols: [cf.OriginSslPolicy.TLS_V1_2],
        }),
        viewerProtocolPolicy: cf.ViewerProtocolPolicy.HTTPS_ONLY,
        allowedMethods: cf.AllowedMethods.ALLOW_ALL,
      }
    })
    new CfnOutput(this, 'CloudfrontUrl', { value: this.distribution.distributionDomainName })
    new CfnOutput(this, 'LambdaUrl', { value: this.functionUrl.url })
    new CfnOutput(this, 'FunctionArn', { value: this.lambdaFunction.functionArn })
    new CfnOutput(this, 'CfnSplitOutput', { value: splitFunctionUrl })

  }
}

const app = new App();
new LfStack(app, 'LambdaHttp')

where ./echo.js is

export async function handler(evt) {
  console.log(evt)
  return {
    statusCode: 200, headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ "id": evt.requestContext.requestId, "status": 200, statusText: "Ok", path: evt.rawPath, query: evt.rawQueryString })
  }
}

@shorn
Copy link

shorn commented Apr 28, 2022

the other slightly weird behaviour was that having a originRequestPolicy: OriginRequestPolicy.ALL_VIEWER caused cloudfront to error and respond {Message: null }

You can use a custom policy like this: example custom request policy

@huntharo
Copy link
Contributor

huntharo commented May 2, 2022

@blacha - The problem is that Lambda URLs are rejecting requests from CloudFront that have the Host header set to the edge host name and not to the Lambda URL host name.

We were just talking to an AWS PM about this on Friday. Because Lambda URLs use exclusively HTTPS they do not need the Host header for multiplexing of many hosts on single IPs (they will use the SNI for that).

But, currently, including the Host header causes the request to be rejected if it's not the Lambda URL hostname.

Options:

  • Do not set an Origin Request Policy - In this case either no headers are forwarded at all or the Host header is there but it's the Lambda URL hostname - the originRequestPolicy is optional in the Distribution - If you just do not specify one then the requests will go through.
  • Do set an Origin Request Policy with allow list - Use an allow list to forward only specific headers, not including the Host header
  • Do set an Origin Request Policy - forward all headers then use an Origin Request header to set the Host header back to request.custom.origin.domainName (IIRC) - This will cause Lambda URL to receive the correct hostname in the header (can confirm this works, it's a technique I use) - Kind of a lame usage of an origin request lambda...
  • CORS Custom Origin? - This is the option you used... I suspect this works only because it may be not sending the Host header (and possibly no other headers). I tried sending an Origin header with a mismatched Host and could not get it to work, thus my suspicion on what is happening.

Hope this helps. By the way... you can also sign the origin requests using SignatureV4 from CloudFront using an Origin Request Lambda @ Edge Function then enable AWS_IAM auth on your Lambda URLs. I have projects that are using this technique now, which is part of the reason why I ran into the Host header issue (which is similar for API Gateway).

@comcalvi comcalvi removed their assignment Jun 20, 2022
@comcalvi comcalvi added the effort/small Small work item – less than a day of effort label Jun 20, 2022
@joshwand
Copy link

joshwand commented Jul 8, 2022

One other problem is that the FunctionUrl cannot have IAM auth turned on, and therefore must be exposed to the internet! This is because Distribution does not implement IGrantable so one cannot grant_invoke_url() to it.

@huntharo
Copy link
Contributor

@joshwand - It is possible to use Function URL IAM Auth with CloudFront and doing so allows the Function URL to be on the Internet but totally safe, just like all the AWS API endpoints that we use (expose to the Internet but protected with IAM Auth + Sigv4).

What you have to do is sign the requests using Signature v4 via a Lambda @ Edge function. This technique works for both Function URLs and for API Gateway (or for any other non-S3 origin that supports IAM auth).

It would be great if CloudFront allowed using Origin Access Identities for non-S3 origins, but it's been years since that was first requested and there must be some reason it's not been added yet. Signing with Sigv4 is not particularly difficult, but pulling the entire thing together can be a challenge (it took me about a week the first time around).

The repo below is a 100% complete demonstration of how to sign the requests using a Lambda @ Edge Origin Request function and how to deploy all of it into either US-East-1 or other regions using a single CDK deployment that deploys a US-East-1 EdgeFunction child stack.

https://github.com/pwrdrvr/lambda-url-signing

I hope this helps you and others!

Harold

@watany-dev
Copy link
Contributor

As this ticket has been resolved, it would be appropriate to close it.
#29101

Copy link

github-actions bot commented Mar 7, 2024

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-cloudfront Related to Amazon CloudFront bug This issue is a bug. effort/small Small work item – less than a day of effort needs-reproduction This issue needs reproduction. p2
Projects
None yet
Development

No branches or pull requests

9 participants