Let us learn how to connect an API Gateway to a VPC without exposing your VPC resources (e.g. Load Balancers, EC2) to the internet.
Update (24 Jun 2020)
AWS has introduced a new HTTP API Service as part of API Gateway. It is much easier to configure, and the VPC Link setup is also simpler. Please see the new article for more details
A very typical deployment architecture for smaller startups is to have an API Gateway at the front, which passes on requests to an ELB, which in turn distributes them to a bunch of EC2 instances. ELBs and EC2s are typically inside a VPC. For an API Gateway to use an ELB as the HTTP endpoint for integration, the ELB needs to be exposed to the internet. This implies that an API request can theoretically be made directly to the ELB, bypassing all the rules configured on the API Gateway. This is a concern.
You can allow only requests originating from the API Gateway on your app by configuring client certificates, but that does not prevent flooding style DDOS attacks on the ELB (and the EC2s). An ideal solution is to create a link between the API Gateway and the ELB which is NOT exposed to the internet.
Such a solution does exist, and it is called a VPC Link. This works only with a Network Load Balancer (NLB). An NLB works at the TCP layer, and cannot terminate SSL. But that’s just fine because the connection between the API Gateway and the NLB will be through the VPC Link, which seems to be a VPN tunnel of sorts. The SSL termination will be handled at the API Gateway itself.
So let us build an API that interfaces with a non-internet facing NLB through a VPC Link. We will configure the API as an HTTP Proxy Integration, which passes all requests directly to the NLB. This should work for most simple cases.
AWS has separate tutorials on this here and here, but there are a couple of points that are not clear, and I had to spend the better half of a day debugging this. Here is what I learned. (Some of the instructions are copied from the above AWS tutorials directly. I had to put them in the right order)
- Create an NLB. Use the tutorial here. Keep it internal, instead of external.
- If you want to configure HTTP health checks for the Target Group, you will have to do it while creating the NLB using the wizard. If you create/modify it separately, you only get an option for TCP health checks.
- While adding the EC2s to the NLB Target Group, make sure that the NLB has inbound permissions to the EC2s (in their Security Groups) for port 80. Since an NLB does not have a security group (but does have permanent IPs), these IPs will have to be directly added to the Security Group for the EC2s. The IPs for the NLBs are not very apparent, see here on how to find these: https://docs.aws.amazon.com/elasticloadbalancing/latest/network/target-group-register-targets.html#target-security-groups.
- Create a new VPC Link from the API Gateway console. Give it a name, and choose the newly created NLB from the dropdown as the target NLB. Note the ID of this VPC Link
- Create a new API. (It is up to you to have it as edge optimized or regional)
- Choose the
/
resource item under the Resources tree and then choose Create Resource from the Actions drop-down menu. Then, in the New Child Resource pane, select the Configure as proxy resource option to create a proxy resource. Click on Create Resource. - Choose the resource just created and then choose Create Method from the Actions drop-down menu.
- Choose
ANY
from the HTTP method drop-down list and then choose the check mark icon to save the choice. - Now we will configure the VPC Link as the Integration. Chose
VPC Link
as the Integration Type - Choose Use Proxy Integration.
- From the VPC Link drop-down list, choose
[Use Stage Variables]
and type${stageVariables.VPCLINK}
in the text box below. - Type
http://${stageVariables.VPCNLB}/{proxy}
for Endpoint URL. This is used to set theHost
header of the integration request. We will define theVPCLINK
andVPCNLB
stage variable after deploying the API to a stage. See Figure 2 below. - Choose Save to finish setting up the integration. Now the API is ready for deployment
- From the Actions drop-down menu, choose Deploy API and then choose a new or existing stage to deploy the API. Note the resulting Invoke URL. You need it to invoke the API.
- In the Stage Editor, choose the Stage Variables tab and choose Add Stage Variable. You need to create two of these. First one named
VPCLINK
with a value equal to the ID of the VPC Link you created in Step 3. Second one namedVPCNLB
with a value equal to the DNS name of the NLB you created in Step 1 (it should be something likexxxxxxxxxxxx.elb.{region}.amazonaws.com
). See Figure 3 below.
This completes creating the API. You can test invoking the API as with other integrations. You can now assign a custom domain to invoke the API, as well as enable SSL on it (this is out of the scope of this tut