🎉 We are launching a new weekly show: Hot off the Cloud

🎉 We are launching a new weekly show

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.


Looking for a new challenge?

  • tecRacer

    Cloud Consultant • AWS Migrations

    tecRacer • Premier AWS Consulting Partner • Germany, Austria, Portugal, and Switzerland
    Assessment Transformation Change Management
  • DEMICON

    Senior Lead Full Stack Developer

    DEMICON • AWS Advanced Consulting Partner • Remote
    AWS JavaScript/TypeScript Angular React

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.

Become a cloudonaut supporter

Andreas Wittig

Andreas Wittig ( Email, Twitter, or LinkedIn )

We launched the cloudonaut blog in 2015. Since then, we have published 360 articles, 49 podcast episodes, and 48 videos. It's all free and means a lot of work in our spare time. We enjoy sharing our AWS knowledge with you.

Please support us

Have you learned something new by reading, listening, or watching our content? With your help, we can spend enough time to keep publishing great content in the future. Learn more

$
Amount must be a multriply of 5. E.g, 5, 10, 15.

Thanks to Alan Leech, Alex DeBrie, ANTHONY RAITI, Christopher Hipwell, Jaap-Jan Frans, Jason Yorty, Jeff Finley, Jens Gehring, jhoadley, Johannes Grumböck, Johannes Konings, John Culkin, Jonas Mellquist, Juraj Martinka, Kamil Oboril, Ken Snyder, Markus Ellers, Ross Mohan, Ross Mohan, sam onaga, Satyendra Sharma, Shawn Tolidano, Simon Devlin, Thorsten Hoeger, Todd Valentine, Victor Grenu, and all anonymous supporters for your help! We also want to thank all supporters who purchased a cloudonaut t-shirt.