Implement API Gateway Private Endpoints

Daniel Ilie
5 min readFeb 27, 2020

API Gateway is one of the best services to provide or gain access to back-end cloud resources. It integrates with almost anything and it even provides a mock integration so that the API can be developed separately from any other service. Private endpoints were introduced during 2018, but I have not used them until recently.

My Use Case

I needed to decouple my application into web presentation tier and business logic. API Gateway seemed the perfect way to go, because it gave me the option to make public some of the REST APIs. I started with a couple of mock stages using regional endpoints. For security purposes, I wanted to keep some of the implementation private and as a result, the network traffic. There are different ways to secure API Gateway, but my favorite is applying resource policies.

My Bare Architecture

This is what I built (the bare minimum) to demonstrate the private endpoint and the security features. I do not like using default infrastructure, when I want to discover and try new things.

Bare architecture (mock back end integration)

First, I picked a region close to me: Frankfurt (eu-central-1). I defined the VPC, using CIDR 10.0.0.0/16. I noticed that DNS resolution was enabled, but the DNS hostnames was disabled. I enabled the DNS hostnames, because I wanted to allow resolution of my API’s stage URL to the private IP of the interface VPC endpoint.

Next, I created the rest of the network. Each new VPC has already created a default main Route Table (RT), a default network access control list (NACL) and a default Security Group (SG). I created a new RT and a new SG to define my routes and rules. I have also created and attached an Internet Gateway (IGW) to the VPC.

For security purposes, I never create a route to the internet in the main RT. The reason is that any newly created subnets are implicitly associated with main RT, thus any instances may be publicly accessible. I created a subnet in my VPC with CIDR 10.0.1.0/24 and associated it with the new RT.

I have also created a route to the internet. These are my routes:

Both the my instance and my VPC endpoint (VPCe) need to have attached a security group which can send traffic on Port 443 to the subnet CIDR, like this:

I launched a t2.micro instance running Amazon Linux 2 in my new subnet, with a public IP address. I attached the security group which will be shared with the VPCe and created another one allowing me to SSH into the instance.

My VPC Endpoint

In the VPC Dashboard, I clicked on Endpoints and then on Create Endpoint. The AWS service needed is API Gateway, which can be found as com.amazonaws.eu-central-1.execute-api.

I selected my VPC from the list (checked the CIDR was 10.0.0.0/16). I enabled DNS names for the endpoint, so I can use private DNS names. Finally, I selected the security group allowing traffic to Port 443, my subnet and left the Policy to Full for now.

Each VPC interface endpoint can be associated with only one subnet from any availability zone. When associated, an elastic network interface is attached and an IP address is assigned from the subnet CIDR.

My Private API

I created a new REST API Private, in the API Gateway Service, by selecting REST, New API, Endpoint Type to Private and typing the VPCe Id (just start typing v… and you will be prompted the rest).

I created a new resource called text and implemented the method/verb GET, using a mock integration. For the Method Request, I left the Auth to None, because I will secure the API with Resource Policies. I modified the Mapping Template in the Integration Response to return {“Response”:”Hello World!”}

I used the following Resource Policy, which denies all access not coming from my VPC endpoint. Explicit deny takes precedence over explicit Allow.

AWS Account Number and API Id are obfuscated

Finally, I deployed my API. I called my stage VPCE and made note of my resource called text.

Securing the VPC Endpoint

The policy associated with the VPC endpoint can be updated to reflect the new stage:

AWS Account Number and API Id are obfuscated

Invoking my API from EC2 Instance

After I had logged on to my instance via SSH, I invoked my API using the private DNS (note the stage id is fbv5):

curl -G https://fbv5.execute-api.eu-central-1.amazonaws.com/VPCE/text

and the public DNS:

curl -G https://vpce-0f3969867923b134f-o47mqmrq.execute-api.eu-central-1.vpce.amazonaws.com/VPCE/text -H’x-apigw-api-id:fbv5'

The highlighted text corresponds to the DNS name of the VPCe shown here:

VPCe DNS name

In both cases the GET response returns “Hello World!” as implemented:

API Invocation by private and public DNS name

Clean Up

I terminated my EC2 instance, deleted my API and my VPC.

Summary

I managed to connect from my instance in a public subnet to my private API via a VPC interface endpoint. Both the endpoint and the API were secured to work together by using policies and a common security group.

Further Reading

Invoking Your Private API Using Private DNS Names

API Gateway VPC Connections

Using DNS with Your VPC

--

--

Daniel Ilie

Cloud solution architect at Wood PLC. Provided clarity, employed creativity and managed complexity of systems.