Controlling access to Lambda Function URLs

The new Lambda function URLs offer two methods for access control. Depending on the use case, AWS IAM or the function itself can perform the authorization process.

AWS has recently announced a new feature called Function URLs for Lambda. A Function URL is an HTTPS endpoint that is mapped to a given Lambda function.

AWS recommends Function URLs for use cases where a single function with a public endpoint is needed, for example, form validations, webhook handling, or mobile payment processing.

Based on the AuthType property, we can create two different types of endpoints: one with AuthType: AWS_IAM and one with AuthType: NONE.

1. The scenarios

Alice and Bob are two cloud developers working for Example Corp., which has its whole infrastructure run on AWS serverless technologies.

In the current sprint, Alice and Bob got different tasks.

Alice is working on a new feature and she creates a Lambda function to perform the business logic.

Bob is integrating a 3rd-party solution into the system, and his task is to create a webhook where the 3rd-party can send notifications. The business logic will further process these notifications.

2. Scenario No 1. - AuthType: AWS_IAM

Alice’s Lambda function needs to accept a payload object. She doesn’t like using the Console for function invocation because she needs to create different test events, and she doesn’t like test events. She also doesn’t want to use CLI and type aws lambda invoke ... all the time.

She decides to create a Function URL for the Lambda function with AuthType: AWS_IAM. She needs to go to the Function URL - new section in the Console, hit Create function URL, and select AuthType: AWS_IAM.

Function URL in the console
Function URL in the Console

Alice can create a new function with a URL or add the URL to an existing one.

2.1. Considerations

If we want to create a Function URL with AuthType: AWS_IAM, we must consider the following.

AWS recommends this type of Function URL for authenticated identities (users or roles). So the identity who invokes the URL must provide their credentials (ACCESS_KEY_ID and SECRET_ACCESS_KEY).

As the AuthType: AWS_IAM property implies, IAM performs the access control to the Function URL, which indicates that we should create a policy to allow the lambda:InvokeFunctionUrl action. Assuming same-account access, we can attach the policy to either the identity invoking the URL (identity-based policy) or the Lambda function that is behind the Function URL (resource-based policy).

One such identity-based policy can look like this:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "lambda:InvokeFunctionUrl",
      "Resource": "arn:aws:lambda:us-east-1:123456789012:function:AuthIamFunctionUrl",
      "Condition": {
        "StringEquals": {
          "lambda:FunctionUrlAuthType": "AWS_IAM"
        }
      }
    }
  ]
}

The Condition element has importance. It says that the policy grants the caller permission to invoke the function behind the URL only if we set AuthType: AWS_IAM.

Another important point is that the request must be signed using Signature Version 4 signing process. With the AWS_IAM authorization type Lambda will check for AWS Sigv4 signatures before invoking the function. If we don’t sign the request with the correct credentials, Lambda will deny the request.

2.2. A solution

Alice has created the Function URL, so let’s now see how she can invoke the function.

One option is to use Postman, which handles Sigv4 signatures.

AWS Signature in Postman
AWS Signature in Postman

She enters her ACCESS_KEY_ID and SECRET_ACCESS_KEY, specifies the region and the service (which should be lambda), and she will be able to invoke the URL! Alice doesn’t have to worry about the signature because Postman will automatically sign the request based on her credentials and service data.

Alice, of course, must have the policy above attached to her identity for the request to be successful.

3. Scenario No. 2. - Creating a webhook

On the other hand, Bob is busy creating a webhook for receiving notifications. He doesn’t want to use API Gateway because he thinks it might be an overkill for a small project like this, and he wouldn’t use all the great features API Gateway offers, anyway.

Bob decides to create a Function URL with AuthType: NONE.

3.1. Considerations

When we create a Function URL with AuthType: NONE, Lambda won’t perform any authentication.

The function behind the URL must have a resource-based policy that allows public access, or else it won’t be able to receive requests.

The following snippet is an example of a resource-based policy that allows unauthenticated, public, non-AWS users to invoke the function:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "FunctionURLAllowPublicAccess",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "lambda:InvokeFunctionUrl",
      "Resource": "arn:aws:lambda:us-east-1:123456789012:function:AuthNoneFunctionUrl",
      "Condition": {
        "StringEquals": {
          "lambda:FunctionUrlAuthType": "NONE"
        }
      }
    }
  ]
}

Two critical aspects of this policy are the Principal: '*', which provides the public access, and the Condition block, which allows access to the URL if the value of AuthType is NONE.

Because the URL can be invoked by literally anyone now, we might want to implement some access control logic (JWT, Basic authentication, etc.) in the body of the Lambda function.

The Function URL announcement article has an example for implementing access control in the body of the function.

3.2. A solution

Bob can now create a simple Lambda function with a public URL, which extracts the request headers if it has the correct Authorization header. If the properties in the decoded token are correct, the function will run and return a success response like this:

return {
  statusCode: 200,
  body: 'Success!'
}

In case of a token mismatch, statusCode can be 401, and body can contain some friendly error message.

4. Conclusion

Now that Alice and Bob are happy, we can do a quick recap.

Lambda Function URLs come with two auth models, AWS_IAM and NONE. AWS recommends that developers use AWS_IAM for authenticated users and NONE for the public, unauthenticated (non-AWS user) access.

For AWS_IAM the identity must sign the request with their credentials and have valid permissions in the form of a policy to invoke the endpoint.

For NONE, the function behind the Function URL must have a resource-based policy allowing public access, and it should perform the authorization process.

5. References and further reading

Security and auth model for Lambda function URLs - description of the two methods

Tutorial: Creating a Lambda function with a function URL - the title of the article says it all