35

I'm trying to put an Amazon API Gateway in front of an Application Load Balancer, which balances traffic to my ECS Cluster, where all my microservices are deployed. The motivation to use the API Gateway is to use a custom authorizer through a lambda function.

System diagram

enter image description here

In Amazon words (https://aws.amazon.com/api-gateway/faqs/): "Proxy requests to backend operations also need to be publicly accessible on the Internet". This forces me to make the ELB public (internet-facing) instead of internal. Then, I need a way to ensure that only the API Gateway is able to access the ELB outside the VPC.

My first idea was to use a Client Certificate in the API Gatway, but the ELB doesn't seem to support it.

Any ideas would be highly appreciated!

Jay Prall
  • 5,295
  • 5
  • 49
  • 79
  • 2
    I don't think there is an easy solution. Either API Gateway is not for you or you have to do a bit more. Solutions include putting HAProxy before the ALB that will verify the client certificate. Or put a lambda before the ALB but that will require the entire response to be buffered possibly slowing things down. Or if you can then inject a secret header to the request from API Gateway to the ALB. This secret will then be verified in the backends. I would not recommend IP whitelisting since the IP ranges for the API Gateway are not static and can change an break stuff. – doorstuck Jan 31 '17 at 12:45
  • Thanks @doorstuck. I would like to avoid incorporating new components like the HAProxy. I guess this scenario (API Gateway, ELB, ECS Cluster) is quite common. Doesn't AWS provide an out of the box solution for it? – Ricardo García Martín Jan 31 '17 at 14:30
  • API Gateway is more tailored to Lambda function services. When put in front of docker containers with hosted services I feel like it adds too much latency and complexity. I use another proxy instead of API Gateway since I only use the proxy part of API Gateway anyway. I used Skipper by Zalando with success. But if you want to use authorizers, cloudfront caching, etc then maybe API Gateway is still the way to go. – doorstuck Jan 31 '17 at 16:44
  • Thanks. I created a lambda to proxy requests inside my VPC, but I didn't like this approach because it adds latency and complexity. I ended up replacing Amazon API Gateway by the Open Source TyK. – Ricardo García Martín Feb 15 '17 at 16:23

8 Answers8

13

This seems to be a huge missing piece for the API gateway technology, given the way it's pushed. Not being able to call into an internal-facing server in the VPC severely restricts its usefulness as an authentication front-door for internet access. FWIW, in Azure, API Management supports this out of the box - it can accept requests from the internet and call directly into your virtual network which is otherwise firewalled off. The only way this seems to be possible under AWS is using Lambdas, which adds a significant layer of complexity, esp. if you need to support various binary protocols.

Dylan Nicholson
  • 1,301
  • 9
  • 23
7

Looks like this support has now been added. Haven't tested, YMMV:

https://aws.amazon.com/about-aws/whats-new/2017/11/amazon-api-gateway-supports-endpoint-integrations-with-private-vpcs/

Carlos Macasaet
  • 1,176
  • 7
  • 23
Dylan Nicholson
  • 1,301
  • 9
  • 23
  • 7
    BTW I did eventually try this out and yes it does work but it's hardly super convenient - for a start you can only have an API gateway talk to a network load balancer rather than an application load balancer, so if you currently external-facing ALBs set up that the gateway is talking to, you need to either replace them with network load balancers (which means you lose your SSL termination, and various other options you may or may not be using) or add an NLB in front of the ALBs, which can now be internal only. Also changing an existing route from "HTTP" to "VPC link" is not entirely seamless. – Dylan Nicholson Dec 13 '17 at 19:25
  • 2
    Any advice on how to set up an NLB in front of an ALB? – Joshua G. Edwards Feb 06 '18 at 11:25
  • 3
    @JoshEdwards There's a blog post [here](https://aws.amazon.com/blogs/networking-and-content-delivery/using-static-ip-addresses-for-application-load-balancers/) if you're still interested – Keenan Lawrence Sep 17 '18 at 01:03
  • In addition to using NLB to replace ALB, you also need to config ECS into an existing VPC without exposing a public IP. Still not an easy solution. Instead of getting into this trouble, I may prefer the solution to still use ALB + ECS with adding a way to authenticate the requests coming from API Gateway in the backend logic. – Z.Wei Jun 07 '19 at 15:11
  • Thanks for this voodoo @DylanNicholson – kian Jan 22 '20 at 21:58
  • @DylanNicholson - Looks like too many moving parts... Also, Latency wise how did the solution "APIGW -> NLB -> Internal ALB -> Target Group" fare in comparison to "APIGW -> Public ALB (over Internet) -> Target Group" ? – kiran01bm Apr 05 '20 at 04:03
  • Couldn't tell you, we threw out API gateway after deciding it wasn't worth whatever gains it was supposed to bring. – Dylan Nicholson Apr 06 '20 at 06:15
5

We decided to use a header to check to make sure all traffic is coming through API Gateway. We save a secret in our apps environmental variables and tell the API Gateway to inject that when we create the API. Then check for that key in our app.

Here is what we are doing for this:

In our base controller we check for the key (we just have an REST API behind the gateway):

string ApiGatewayPassthroughHeader = context.HttpContext.Request.Headers["ApiGatewayPassthroughHeader"];

if (ApiGatewayPassthroughHeader != Environment.GetEnvironmentVariable("ApiGatewayPassthroughHeader"))
{
    throw new error;
}

In our swagger file (we are using swagger.json as the source of our APIs)

"x-amazon-apigateway-integration": {
    "type": "http_proxy",
    "uri": "https://${stageVariables.url}/path/to/resource",
    "httpMethod": "post",
    "requestParameters": {
      "integration.request.header.ApiGatewayPassthroughHeader": "${ApiGatewayPassthroughHeader}"
    }
  },

In our docker compose file (we are using docker, but the same could be used in any settings file)

services:
  example:
      environment:
        - ApiGatewayPassthroughHeader=9708cc2d-2d42-example-8526-4586b1bcc74d

At build time we take the secret from our settings file and replace it in the swagger.json file. This way we can rotate the key in our settings file and API gateway will update to use the key the app is looking for.

Cale
  • 597
  • 1
  • 7
  • 17
3

I know this is an old issue, but I think they may have just recently added support.

"Amazon API Gateway announced the general availability of HTTP APIs, enabling customers to easily build high performance RESTful APIs that offer up to 71% cost savings and 60% latency reduction compared to REST APIs available from API Gateway. As part of this launch, customers will be able to take advantage of several new features including the ability the route requests to private AWS Elastic Load Balancers (ELB), including new support for AWS ALB, and IP-based services registered in AWS CloudMap. "

https://aws.amazon.com/about-aws/whats-new/2020/03/api-gateway-private-integrations-aws-elb-cloudmap-http-apis-release/

D. Cook
  • 382
  • 3
  • 10
2

It is possible if you use VPC Link and Network Load Balancer.

Please have a look at this post: https://adrianhesketh.com/2017/12/15/aws-api-gateway-to-ecs-via-vpc-link/

TL;DR

  1. Create internal Network Load Balancer connected to your target group (instances in a VPC)
  2. In the API Gateway console, create a VPC Link and link it to above NLB
  3. Create API Gateway endpoint, choose "VPC Link integration" and specify your NLB internal URL as an "Endpoint URL"

Hope that helps!

1

Currently there is no way to put API Gateway in front of private ELB, so you're right that it has to be internet facing. The best workaround for your case I can think of would be to put ELB into TCP pass through mode and terminate client certificate on your end hosts behind the ELB.

Nithin Kumar Biliya
  • 2,763
  • 3
  • 34
  • 54
1

It is now possible to add an authorizer directly to Application Load Balancer (ALB) in front of ECS.

This can be configured directly in the rules of a listener. See this blog post for details:

https://aws.amazon.com/de/blogs/aws/built-in-authentication-in-alb/

Erik P
  • 353
  • 3
  • 15
0

The ALB should be internal in order to have the requests routed there through private link. Works perfectly fine in my setup without need to put NLB in front of it. Routes should be as following:

$default / GET (or POST or whichever you want to use) Integration should be attached to all paths $default and GET/POST/ANY etc