The Private API Gateway is a very secure solution for internal API deployments. When deployed in your account, it can only be reached trough a VPC Endpoint. But this means you can’t connect to the API over VPN or VPC Peering. In this blog post we will solve this issue using an ALB.
When you create a Private API Gateway, it will deploy with a DNS name like n1d30vbzhf.execute-api.eu-west-1.amazonaws.com
. If the API has a stage called test, you could reach it at https://n1d30vbzhf.execute-api.eu-west-1.amazonaws.com/test
. This DNS name, however, only resolves if you connect from a VPC in the same account, with a VPC Endpoint with Private DNS
enabled.
If you don’t have Private DNS enabled, you can still reach the Private API Gateway, but with a request like this: curl https://vpce-0acb7d91a9c1c549f-rbyo7rro.execute-api.eu-west-1.vpce.amazonaws.com/test -H 'Host: n1d30vbzhf.execute-api.eu-west-1.amazonaws.com'
. You connect to the DNS name of the VPC Endpoint, but you need to pass the hostname of the Private API Gateway in a custom header. This allows the VPC Endpoint to route the traffic to the correct API Gateway.
This solution would even work over VPC Peering and VPN connections, but it requires the client to pass a modified host header, which is non-trivial to implement, and requires changes in every single application that consumes the Private API Gateway.
So what does work? Let’s add an ALB!
An ALB in your VPC
If you place an ALB in a VPC that has access to the Private API Gateway, that ALB will be able to connect to the API Gateway. If you make the ALB internal
, you will be able to reach the ALB over VPN and VPC Peering.
The ALB needs to point to the IP addresses of the ENIs for the VPC Endpoint. To find these IP addresses, either look up the addresses in the Network Interfaces section of the EC2 console, or run a host vpce-0be22b56409e271c3-ff6w6z15.execute-api.eu-west-1.vpce.amazonaws.com
, where you fill in the hostname of the VPC Endpoint.
When creating the Application Load Balancer, follow these steps:
- [Step 1] Make the scheme
internal
- [Step 1] Add an HTTP and / or HTTPS listener. HTTP is probably easier at this point.
- [Step 1] Put the ALB in the same AZs as your VPC Endpoint
- [Step 3] Make sure the security group allows inbound port 80 (and 443 if you enabled HTTPS)
- [Step 4] Create a new target group with Target type
IP
and protocolHTTPS
(this is important!) - [Step 4] Set the health check protocol to HTTPS
- [Step 4] Click Advanced health check settings and set the Success codes to 200,403 (this is also important!)
- [Step 5] Register the IP addresses collected above
When this is done, give it a few minutes and your ALB should be deployed with three healthy targets. You should be able to connect to this ALB from the local VPC, but also over VPC Peering and VPN, but you would be greeted with a {"message":"Forbidden"}
response. This is not what we want, but the response does come directly from the API Gateway. Silver linings.
[ec2-user@ip-10-0-0-247 ~]$ curl http://internal-api-gateway-alb-42029014.eu-west-1.elb.amazonaws.com
{"message":"Forbidden"}
Fixing the ‘forbidden’ message by using a custom domain
The reason we’re seeing the {"message":"Forbidden"}
message is because the ALB is not specifying the hostname for the API Gateway. This is the same situation as described in the beginning of the post: we’re connecting directly to the VPC Endpoint, but the VPC Endpoint doesn’t know to which API Gateway to route the traffic without a hostname.
The solution is to use a custom domain. First make sure that you have DNS entry (in Route 53 or any other DNS provider) that points to your loadbalancer. In my case, I used vpcdemo.com
:
When this is done, you should be able to connect to your ALB using the new DNS name:
[ec2-user@ip-10-0-0-247 ~]$ curl http://vpcdemo.com
{"message":"Forbidden"}
Now request an ACM Certificate for the same domain name. Make sure it is created in the same region as your API Gateway. The address for the ACM console is https://console.aws.amazon.com/acm/home.
Then navigate back to the API Gateway console and click Custom Domain Names in the left menu bar. Select ‘Regional’ and fill in your custom hostname and ACM certificate:
When it’s done, click edit and add a base mapping:
Now try to connect to the ALB again, and :tada:!
[ec2-user@ip-10-0-0-247 ~]$ host vpcdemo.com
vpcdemo.com has address 172.31.17.107
vpcdemo.com has address 172.31.34.103
vpcdemo.com has address 172.31.10.29
[ec2-user@ip-10-0-0-247 ~]$ curl http://vpcdemo.com/ -I
HTTP/1.1 200 OK
Date: Mon, 03 Jun 2019 20:18:52 GMT
Content-Type: application/json
Content-Length: 0
Connection: keep-alive
Server: Server
x-amzn-RequestId: ce54e310-863c-11e9-ba01-2f74f1242d3b
x-amz-apigw-id: auELBHSijoEFYDA=
Add HTTPS to the ALB
If you would like to use HTTPS to access your load balancer (and you should!), you can now easily add an HTTPS listener using the ACM certificate you created for the API Gateway.
Conclusion
In this post we’ve shown how to point an ALB to a VPC Endpoint, which in turn points to a Private API Gateway. The ALB is accessible over VPN and VPC Gateway, making sure that any traffic to the ALB remains private.
By using custom domains (which is technically not supported by Private API Gateways), we can ‘trick’ the VPC endpoint into understanding where to send traffic, without custom host headers.
As always, if you have any questions reach out to me on Twitter.