Morgatz (8) [Avatar] Offline
Hi, I have a lambda function that provides a list of products. The web client app can access this lambda function via the API Gateway. For example there's an endpoint for GET /products that will return a list of products for the web app.

However, another lambda function (let's say one that creates an order) needs to get product information as well. I can't call GET /products from the order lambda function because it's not tied to a user and therefore would not use Cognito Identity. I can try to invoke it directly using AWS sdk, but then I wouldn't be able to test it locally because the lambda function exists in a private subnet within my VPC.

Any recommendation on how to get around this? Or is the only to invoke the lambda function directly?
Yan Cui (53) [Avatar] Offline
Hi ya, firstly, let's clear an important (and common) misconception here:

The Lambda function doesn't exist in a private subnet inside your VPC, ever. When you configure the VPC setting for a function, you give that function ACCESS to the private VPC, by creating ENIs in the subnet you configure. This does not equate to the function being "placed" inside that VPC, and does not impede your ability to invoke it via the AWS SDK directly.

Now, let's get back to the question you had - given an endpoint controlled by Cognito User Pools, how do I give another Lambda function access to this endpoint?

The limitation here is that, with API Gateway, each endpoint can only have one authorization method. I can think of a couple of options here (each with its own trade-offs):

  • Invoke the function behind the API endpoint directly : which lets you side-step the API Gateway authorization altogether, and instead you're back in the realm of IAM policies for invoking Lambda function. However, this also breaks your abstraction layer. The fact that there's a Lambda function behind an API endpoint is an implementation detail, and the consumer of the API shouldn't have to depend on implementation details. I don't like this option, UNLESS, both functions are in the same service (i.e. inside the same serverless.yml), in which case you would be better off using shared code modules/library instead of going through function invocation.

  • Use AWS_IAM authentication for the API endpoint instead : which can work for both the client as well as other internal services. The trade-off is that you now need to use Cognito Federated Identities to exchange the JWT you receive from Cognito User Pool into temporary IAM credentials, and then use these credentials to sign the HTTP request. I also don't like this solution, as it introduces unnecessary complexity to the client, unless you're using Cognito Federated Identities already (to talk to AWS IoT services, for example).

  • Create another API endpoint that integrates with the first function (that gets a product), and lock it down with AWS_IAM authentication : e.g. if GET /products is the public facing endpoint that uses Cognito authorizer, then create an endpoint GET /internal/products that uses AWS_IAM authorizer and is used only by internal processes, including the second Lambda. I think it's slightly better than the first 2 options, but it still sucks that you have to change your public facing contract to accommodate limitations in API Gateway...

  • Use a custom authorizer function that supports multiple authorization methods : this is much more involved, as you're taking over the responsibility for authorization and authentication yourself. What you could do, is to inspect the request in the authorizer function, if some specify header (e.g. `x-api-key`) is specified then it's probably an internal request, and you use some shared API keys (you might even manage this with AWS Secrets Manager and rotate it periodically). Otherwise, check the `Authorization` header (where the client would pass in the id token from Cognito), decode it, verify it. Whilst this is the cleanest solution, it's also by far the most complicated to implement...

  • Hope this helps.
    Morgatz (8) [Avatar] Offline
    Thanks for the suggestions Yan, I'll have to weigh options and decide on a path to take.

    With regard to Lambda functions not existing on VPC, that now makes sense to me. However, I have a security question then. If I don't want certain functions to be accessible by the public should I even be using lambdas? If some functions are meant to only be called internally and never exposed publicly, then is this still a good use case for using lambda or is there something I'm still not understanding where lambda is still safe to use for internal only functions?
    Yan Cui (53) [Avatar] Offline
    The answer is IAM.

    In order to invoke a Lambda function, you need to have the right IAM permissions, you control access through IAM roles, users, groups and permissions.

    A common attitude (which is wrong by the way) is to harden around VPC boundaries and then give full trust to anything inside the VPC, so anyone who breaches your VPC protection - e.g. through injection attacks, or compromised app dependencies - would simply sidestep all your security measures.

    Which is why, even with VPCs you need to protect internal APIs with authentication, and IAM is a pretty robust solution for that, and you get it for free (well, you do pay $ for it but you don't have to implement it yourself) with API Gateway and Lambda.