Serverless Hybrid Cloud: Accessing an API Gateway via VPN or Direct Connect

Andreas Wittig – 17 Mar 2021

Recently, I’ve been coaching a team building a Serverless application. The extraordinary thing about it? We had to create a solution fitting into the hybrid cloud approach of the organization. An essential requirement was that the Serverless application is only accessible via the internal network. Sounds simple? It’s not because AWS designed their Serverless services with a focus on Internet-based applications.

Serverless Hybrid Cloud: Accessing an API Gateway via VPN or Direct Connect

So how to build a Serverless application - backend and single-page application - accessible from the corporate network via VPN or Direct Connect? Read on to learn more! Terraform code snippets included.

The architecture

Use the following building blocks to assemble a Serverless architecture for the hybrid cloud scenario.

  • A VPN or Direct Connect connection links the corporate network with the VPC (Virtual Private Cloud).
  • The Application Load Balancer (ALB) accepts HTTPS requests and forwards them to a VPC Endpoint.
  • The VPC Endpoint forwards traffic to the API Gateway.
  • The API Gateway processes the request and forwards them to Lambda (backend) or S3 (frontend).

Serverless Hybdrid Cloud: VPN, ALB, VPC Endpoint, API Gateway, and Lambda

Let’s have a look at the details and some Terraform code next.

API Gateway

When configuring the API Gateway, the following attributes are important.

  • Use endpoint type PRIVATE to create an API Gateway only accessible from the VPC.
  • Attach a gateway policy that grants access from a certain VPC only.
resource "aws_api_gateway_rest_api" "myapp" {
name = "myapp"

endpoint_configuration {
types = ["PRIVATE"]
vpc_endpoint_ids = [vpce-11111111111111111]
}

policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": [
"execute-api:/*"
],
"Condition": {
"StringEquals": {
"aws:SourceVpce": "vpc-11111111"
}
}
}
]
}
POLICY
}

VPC Endpoint

The VPC Endpoint establishes the network connectivity between the VPC and the private API Gateway.

Warning: When creating a VPC Endpoint for the API Gateway service with Private DNS Enabled, all requests from within the VPC to public API Gateways will fail. Check out Steffen’s blog post Be careful with AWS Private API Gateway Endpoints to learn more.

resource "aws_vpc_endpoint" "apigw" {
vpc_id = "vpc-11111111"
service_name = "com.amazonaws.us-east-1.execute-api"
vpc_endpoint_type = "Interface"

security_group_ids = [
aws_security_group.apigw.id
]

private_dns_enabled = false
}

resource "aws_security_group" "apigw" {
name = "myapp-apigw"
vpc_id = "vpc-11111111"
}

ALB

Why on earth do we need an ALB for our Serverless architecture? The ALB allows us to define a custom domain name which is essential, especially when building a single-page application. Unfortunately, private API Gateways do not support custom domain names out-of-the-box.

Please note that the following Terraform code snippet creates a domain name intended to be used with a regional API gateway. By accident, it is possible to link the custom domain name with a private API Gateway.

resource "aws_api_gateway_domain_name" "myapp" {
domain_name = "myapp.local.mycompany.com"
regional_certificate_arn = "arn:aws:acm:us-east-1:111111111111:certificate/..."
security_policy = "TLS_1_2"

endpoint_configuration {
types = ["REGIONAL"]
}
}

resource "aws_api_gateway_base_path_mapping" "myapp" {
api_id = aws_api_gateway_rest_api.myapp.id
stage_name = aws_api_gateway_deployment.myapp.stage_name
domain_name = aws_api_gateway_domain_name.myapp.domain_name
}

resource "aws_api_gateway_deployment" "myapp" {
rest_api_id = aws_api_gateway_rest_api.myapp.id
stage_name = "v1"
}

As mentioned already, the private API Gateway does not support custom domain names. Therefore, we are adding the ALB to the equation. The ALB accepts the requests to myapp.local.mycompany.com and forwards them to the private IP addresses of the VPC endpoint. Luckily, the ALB does not verify the TLS certificate used by the API Gateway, which is not valid for myapp.local.mycompany.com but *.execute-api.us-east-1.amazonaws.com.

resource "aws_lb" "myapp" {
name = "myapp"
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = ["subnet-aaaaaaaaaaaaaaaaa", "subnet-bbbbbbbbbbbbbbbbb"]
internal = true
}

resource "aws_lb_listener" "myapp_https" {
load_balancer_arn = aws_lb.myapp.arn
port = "443"
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-FS-1-2-2019-08"

certificate_arn = "arn:aws:acm:us-east-1:111111111111:certificate/..."

default_action {
type = "forward"
target_group_arn = aws_lb_target_group.myapp.arn
}
}

Next, we register the private IP addresses of the VPC endpoint at the load balancer.

I could not find a way to do so with Terraform automatically. Therefore, I’m using variables to configure the IP addresses here manually. Happy to learn a better way to do so from you!

resource "aws_lb_target_group" "myapp" {
name_prefix = "myapp-"
port = 443
protocol = "HTTPS"
vpc_id = "vpc-11111111"
deregistration_delay = 60
target_type = "ip"

health_check {
interval = 10
path = "/"
protocol = "HTTPS"
timeout = 5
healthy_threshold = 2
unhealthy_threshold = 2
matcher = "200-499"
}
}

resource "aws_lb_target_group_attachment" "myapp_1" {
target_group_arn = aws_lb_target_group.myapp.arn
target_id = var.vpce_ip_1
}

resource "aws_lb_target_group_attachment" "myapp_2" {
target_group_arn = aws_lb_target_group.api.arn
target_id = var.vpce_ip_2
}

Finally, point a DNS record the ALB.

resource "aws_route53_record" "myapp" {
zone_id = "..."
name = "myapp.local.mycompany.com"
type = "A"

alias {
name = aws_lb.myapp.dns_name
zone_id = aws_lb.myapp.zone_id
evaluate_target_health = false
}
}

So that’s one option to deploy a Serverless application with a custom domain name in a hybrid cloud scenario. Users access the frontend and backend via a custom domain name.

An alternative

Admittedly, the approach described so far is quite complicated. And not very efficient either since every request has to go through the ALB, the VPC endpoint, and the API gateway. This also results in unnecessary costs. In case the features of the API Gateway - AuthN/AuthZ, Throttling, Request/Response Mapping, and more - are not used, there is a more straightforward solution.

Configure the ALB to forward requests to a Lambda function. I’ve used this approach to build a simple API to send SMS, for example.

Serverless Hybdrid Cloud: VPN, ALB, and Lambda

This approach is less complex and more efficient.

Besides that, AWS introduced interface VPC endpoint for S3 in February 2021. I haven’t looked into this in detail, but this might be an option to access a single-page application’s static content in a hybrid cloud scenario.

Summary

Deploying a Serverless application only accessible via the private network of hybrid cloud architecture is tricky. Mainly because AWS is focusing on Internet-based applications. Therefore, I recommend carefully considering whether a serverless architecture is the right solution for a hybrid cloud environment.

Andreas Wittig

Andreas Wittig

I’ve been building on AWS since 2012 together with my brother Michael. We are sharing our insights into all things AWS on cloudonaut and have written the book AWS in Action. Besides that, we’re currently working on bucketAV,HyperEnv for GitHub Actions, and marbot.

Here are the contact options for feedback and questions.