Move your AWS Lambda functions inside your VPC
Yeah, so I'm not great at reading documentation
I don't think anybody really enjoys reading documentation, most of just want to to "git 'er dun". In that spirit, skipping over paragraphs of florid tech writing and jumping straight to the code snippets is usually enough. Not in this case though
TL;DR;
- Lambdas moved to your VPC must be in a private subnet (i.e. no internet gateway)
- Lambdas must use a NAT gateway to have internet access
- Your NAT gateway must be attached to a public subnet i.e. one that has a an internet gateway, not your private subnet where the Lambdas will live
- Your private subnet must have a default route to the NAT gateway in the public subnet
- NAT gateways cost money, $40+ / month at the time of writing.
- Don't forget to fix your security groups and use endpoints if you can for AWS services
The overly florid explanation
I have been trying for some time to completely nail down public access to my AWS resources but one step just didn't seem to work: moving the Lambda functions at the heart of my web system out of the AWS VPC and into my own. The Lambda functions are not complicated (read from database, read/write to S3, get triggered by DynamoDB or S3, send email via SendGrid etc). However, each attempt at moving them inside my VPC so I could close down the limited public acess I had to the AuroraDB instance would fail. Either the Lambda's could not connect to the database, or they couldn't connect to S3, or they couldn't connect to SendGrid. Pretty clearly I was missing something obvious.
Internet gateway? NAT gateway? NAT instance?
The AWS instructions for doing this sort of thing are pretty clear but each time I tried to follow them, something screwed up. Mostly it was access to SendGrid. I have an external SendGrid subscription - if you subscribe via AWS then accessing it is much easier using an endpoint and you don't need actual internet access. Of course I do need internet access for my Lambdas so I'm stuck with trying to work out how it works.
The most promising (and unfortunately expensive) way to do this, as recommended by AWS, is to set up a NAT gateway. For some reason I can't fathom, your Lambda functions must be in a private subnet. In AWS terminology, a private subnet is one that doesn't have an internet gateway attached to it. Which leaves the conundrum that if your Lambda needs internet access, you need to give that private subnet some way out. In all of the naive attempts at this, I would duly create a NAT gateway and attach it to the private subnet and voila! Nothing worked.
The devil in the details
The trick is to put the NAT gateway on a public subnet (i.e. one that already has an internet gateway), then add a route from your private subnet to the NAT in the public subnet. It sounds obvious in retrospect. In the end, my private subnet has these routes:
I have two endpoints (one for S3, one for DynamoDB), two local routes (ipv4 and ipv6) and the NAT gateway for everything else. This allows internal communcation to the AuroraDB inside the VPC, access to S3/DynamoDB via AWS internal connections and the NAT gateway for talking to SendGrid.
Other considerations: security groups and endpoints
A discussion around security groups is probably out of scope here, but you do need to pay attention to them on AuroraDB to give access to your Lambda functions. Most AWS tutorials seem to use a single security group for everything and that's fine for small setups. The AWS endpoints are great for simple access from your private subnets to other AWS services. See here for a good explanation of how they work. If your Lambda doesn't need internet access, but does need access to AWS services, stick to the endpoints and don't use the NAT gateway and save some money.
It costs extra money
What price security? The NAT gateways have a really bad pricing model that I would normally struggle to justify. In my case I think it will add $40 or $50 to the monthly AWS bill just to have a NAT gateway idling there doing not much. There is apparently a "NAT instance" that is a lot cheaper, but AWS discourage it's use (or even finding it!) and would much prefer you use the NAT gateway. I could have powered this laptop with all the eye-rolling that ensued once I worked that out, but at least I'm resting slightly easier that the AuroraDB isn't quite so open any more.