Michael Blum

Developer from Chicago

ACME API Gateway


AWS API Gateway

Amazon Web Service’s scalable API offering: API Gateway is an execellent way to create simple APIs that charge by-the-request. One pain point though is that by default, your API is available via an ugly auto-generated URL such as:

https://4ittmt4ei7.execute-api.us-east-1.amazonaws.com/dev/

There is an option to manually upload an SSL certificate to enable a custom domain but its tedious and well, manual.

manually configuring certificate for an API Gateway

Well that just won’t do.

Unfortunantly, as of this writing, API Gateway can’t be connected to Cloudfront - this is how one would apply a custom domain name to an S3 bucket, for example. Here’s a write-up on how Cloudfront works to secure an S3 bucket: Building a Blog Part 5 - Securing an S3 Website.

Lets Encrypt to the rescue!

No matter! We can solve this problem using Let’s Encrypt and Route 53. Route 53 (AWS’s DNS service) provides an API for creating DNS records (TXT, ALIAS, etc). Using a DNS-challange, we can use Let’s Encrypt to validate our custom domain name (configured with Route 53). I wrote a python script that runs as a plugin on top of Dehydrated (previously known as letsencrypt.sh), an implementation of Let’s Encrypt that runs as a shell script.

ACME API Gateway

acme-api-gateway

This hook uses Route 53 to get certificates for securing an Amazon API Gateway. This allows for secure and custom domains for your API.

Clone Dehydrated

git clone https://github.com/lukas2511/dehydrated.git

Checkout

cd dehydrated
git clone https://github.com/mikeblum/acme-api-gateway.git

Setup

cd into acme-api-gateway:

cd acme-api-gateway

Add the AWS keys (strongly recommend creating an IAM role for this) so we can update the Route53 DNS records for this domain.

vi .env
#!/bin/bash

export AWS_ACCESS_KEY=########################
export AWS_SECRET_ACCESS_KEY=#################
source ./env

Create a virtualenv:

virtualenv env
source ./env/bin/activate

Install dependencies

pip install -r requirements.txt
  • tldextract
  • dnspython
  • boto3
  • route53

Get Certificates

./dehydrated --cron --force --domain {{ domain }} --hook acme-api-gateway/hooks/hook.py --challenge dns-01

This will automatically verify and deploy your custom domain and make it available in API Gateway.

Output looks like this:

Processing {{ your domain }}
 + Checking domain name(s) of existing cert... unchanged.
 + Checking expire date of existing cert...
 + Valid till Feb 11 20:48:00 2017 GMT (Longer than 30 days). Ignoring because renew was forced!
 + Signing domains...
 + Generating private key...
 + Generating signing request...
 + Requesting challenge for {{ your domain }}...
deploy_challenge
{{ your domain }}
IQYeRiF-3MIymB_PwFHIXAhPPicaiks6ec1uHgdi-aE
removing DNS challenge
creating TXT record for letsencrypt challenge
modified: True
 + Responding to challenge for {{ your domain }}...
clean_challenge
{{ your domain }}
IQYeRiF-3MIymB_PwFHIXAhPPicaiks6ec1uHgdi-aE
removing DNS challenge
removing old TXT record for letsencrypt challenge
 + Challenge is valid!
 + Requesting certificate...
 + Checking certificate...
 + Done!
 + Creating fullchain.pem...
deploy_cert
deploying certificates to API Gateway
renewing domain name ({{ your domain }}) in API Gateway
Create Alias record from {{ your domain }} to dqux49yhycipr.cloudfront.net
 + Done!

When you check your AWS API Gateway panel, you’ll find your validated domain available to attach to any deployed API in your account:

api gateway custom domains now available

This script now gives us one-click automated certificate deployments for our API Gateways.

Note, since a Let’s Encrypt certificate expires every 90 days, you’ll want to re-run this script periodically.

Cheers!