4

I have a Lambda that needs to be on a VPC to talk to protected resources like RDS and AWSDocumentDB. It also needs to be able to see the outside world for some calls to 3rd party APIs. To do this I used the VPC wizard to create a VPC that had both public and private subnets. The wizard also created and attached an Internet Gateway.

After this I attached my Lambda, RDS instance and DocumentDb cluster to the VPC. Since then, however I have been unable to talk to my SQS queues from within my lambda using the NodeJS aws-sdk.

I want to add that I have read and implemented some points from: AWS Lambda: Unable to access SQS Queue from a Lambda function with VPC access however I am still unable to connect.

Here is what I have:

  1. VPC:

    • VPC has public and private subnets, and an IG Gateway. I used the wizard to create it. I don't understand much of the underpinnings here.
    • VPC Config (sorry it's a link, it won't let me embed yet.)
    • CIDR's- the wizard created all but the last block. I am unsure if I did this right or if it even matters as the wizard made me create at least one and I did that to avoid IP overlap.
    • As this is a dev/prototype project the security group attached to the VPC is 'wide open'. All inbound and outbound is allowed.
    • Let me know what other VPC config to show as I'm not sure what's useful
  2. Service Endpoint:

    • I tried creating a service endpoint for SQS per the article linked above, here's what I have: endpoint config
    • I'll talk more about how I'm consuming this in the Lambda section.
    • The endpoint is attached to the VPC
  3. Lambda:

  4. Code

    • Here is what the SQS invocation looks like from within my code:

      • const {SQS} = require('aws-sdk');
        
        // Constructor Init
        const sqs = new SQS({
           apiVersion: '2012-11-05', 
           endpoint: 'https://sqs.us-west-2.amazonaws.com', // not sure if this is 'invoking' the vpc endpoint or not
           region: 'us-west-2'
        });
        
        // Send message
        await sqs.sendMessage({
           MessageBody: 'Test body',
           QueueUrl: 'https://sqs.us-west-2.amazonaws.com/<rest of URI>',
           MessageAttributes: {...someAttrs}
        }).promise();
        
        
        
        

Appreciate any help, please let me know what other information I can provide.

Thanks!

** Edit **

I should also mention that to circumvent this whole issue I started to go down the road of using SQS as a Lambda Destination. While this does inject messages into the target queue, it likely will not scale with my use case. I can elaborate on that further if needed as it isn't totally pertinent to the actual question.

** EDIT 8/31/20 **

Thanks for all the responses, it was a great help and got me to a resolution. I will say that to anyone else who finds this post is to first watch:

https://www.youtube.com/watch?v=JcRKdEP94jM

This is something that I wish I found before I started all of this because, while it's specifically targets to giving lambdas internet access, it goes through the process of mapping IG's and Nats to subnets which is really where I was mis-configuring my vpc. With this video I went and re-created my entire VPC and it is so much cleaner and easier to connect the dots. 10/10 recommend.

Thanks again!

  • I ran into almost this same problem, except I was using python. Mine turned out to be a bug in boto3, as explained (with workaround) in [this answer](https://stackoverflow.com/a/69771594/342647). – Travis Jan 18 '22 at 05:02

2 Answers2

3

The AWS Lambda function should be attached to a private subnet in the VPC.

Amazon SQS lives on the Internet, so the Lambda function needs a way to access the SQS endpoint.

Option 1: VPC Endpoint

A VPC Endpoint for Amazon SQS can provide a direct link between a VPC and the SQS endpoint, without requiring Internet access. Once created, the VPC will automatically send SQS requests across the VPC Endpoint. This is a nice, simple option.

Option 2: NAT Gateway

If you have additional resources in the private subnet(s) that require Internet access, you could consider placing a NAT Gateway in a public subnet. The private subnet(s) will need a Route Table entry that directs Internet-bound traffic (0.0.0.0/0) to the NAT Gateway.

Additional charges apply.

Option 3: Lambda Destination

If you have the Lambda function successfully communicating the SQS via a Lambda Destination, then this sounds like an excellent option! I haven't tried it, but it sounds like the Lambda service is responsible for sending the output directly to SQS, without having to traverse the VPC or Internet. If this is working, then I highly recommend to keep using it. I do not perceive any scaling problems using this method. However, please note that it only works for asynchronous calls to Lambda and will not work, for example, when invoking the function synchronously or by pressing the Test button.

John Rotenstein
  • 241,921
  • 22
  • 380
  • 470
  • Thanks for the response! So per the screenshot above I attached all the subnets the VPC had to the lambda (some public, some private). If I attach only private subnets will I still be able to talk to outside (ie: Stripe API) resources? – Ken Stowell Aug 27 '20 at 21:53
  • Trying the NAT solution now. I had one created but it was never associated to a subnet. – Ken Stowell Aug 27 '20 at 22:06
  • AWS Lambda functions should only be configured to use private subnets. Frankly, I recommend Option #3 or Option #1 over using the NAT Gateway, since additional costs (and more complexity) is involved. – John Rotenstein Aug 27 '20 at 22:10
  • Good advice, I think you're right. By scale, earlier what i was referring to is that AWS will place whatever your lambda returns inside the body of a message. So in my use case, I want to process every transaction for all accounts. So the first query/lambda gets all accounts and originally put each account into a queue as its own message, because downstream each account might have 10K+ txns. But now i have to rethink how im initially querying accounts so I don't have 1K+ accounts packed into one message. – Ken Stowell Aug 27 '20 at 22:19
  • Perhaps I can use step functions to create some offset continuation so I can make each downstream lambda have less to process. – Ken Stowell Aug 27 '20 at 22:21
  • Oh! I suppose from reading option #1 though - I *have* an sqs endpoint and it's attached to the VPC. Anything look off in my screenshots as to why that wouldn't be working? One of my questions in my post was if I was giving the right url to the sqs constructor. – Ken Stowell Aug 27 '20 at 22:23
  • It _might_ be due to your Lambda functions being attached to subnets that are not configured to use the VPC Endpoint. If you look at the Route Table for each subnet, it might give you a clue. – John Rotenstein Aug 27 '20 at 22:43
1

My hunch on this one is that there is a missing rule somewhere in your network configuration - Packets getting dropped either toward your SQS, or on their way back (both need thought through hop by hop).

The three things that come to mind:

  1. Routing: Ensure that the subnets and routing table(s) have appropriate routes to get packets back to your private subnet from the one where SQS endpoint is.
  2. Security Groups - Look carefully at each SG involved. The SQS may be in a SG that restricts access to it, for example.
  3. Network ACLs - these are stateless, so you'll need to ensure both sides are open, and remember that most of the time, there will be random port numbers involved going back to the requester.

Good luck!

Chris Trahey
  • 18,202
  • 1
  • 42
  • 55
  • This was it! I decided to double check my SG I/O's and I noticed something odd: when looking at the SG on its dedicated page the rules looked normal. Both in and out were `0.0.0.0/0`. However, when I went to the SQS endpoint and viewed the SG from there, although it was the same SG it showed an inbound rule of `sg-` meaning that inbound traffic was restricted to anything inside the SG. From there I clicked edit and removed that constraint and now it works! Thank you! – Ken Stowell Aug 30 '20 at 16:39