On-demand compute

Goals

I’ve got a prototype RESTful service I’d like to promote to something a bit more durable for minimal cost, which breaks down to the following steps:

On-demand

I’m using Cloud9, which provisions an EC2 instance. I’ve been running services on different ports of this instance, but this is tedious and relatively unrealistic. I’d like these services to be available for arbitrary requests, but I don’t need to them running all the time. AWS’ Lambda seems appropriate.

Creating a new function via the Lambda console is straightforward. Well-done, AWS 👍

HTTP routing

Routing HTTP requests to a Lambda function requires the API Gateway abstraction.

Routing requests with wildcard paths requires the API Gateway’s Lambda Proxy configuration.

The end result is accessible via HTTP at a URL like: https://jmf9mr2fge.execute-api.us-west-1.amazonaws.com/prod/a/b/c.

Restricting access

Since I’m paying by usage, and the only customer, I only want this function invoked by my requests. API Key security seems appropriate at this stage.

Setting up API Key security on a proxy endpoint is accomplished by drilling into the “Method Request” configuration of the “Method Execution” overview.

I can use ModHeader to include the “x-api-key” header and filter for requests to “*.amazonaws.com”.

Custom domain

Custom domains are configured through the API Gateway. The commands below use fn.example.com as an example domain.

API Gateway requires domains use HTTPS. (“To set up a custom domain name as your API’s host name, you…must provide an SSL/TLS certificate for the custom domain name.” - docs).

So, step one is to get an SSL cert. Let’s Encrypt provides tooling for generating a free SSL cert. I found the google/acme tool (recommended by a concise tutorial) to be relatively sane.

Install Go on Amazon Linux (Cloud9’s EC2):

sudo yum install golang

Install acme:

go get -u github.com/google/acme

Register account:

~/go/bin/acme reg -gen -accept contact@example.com

Verify registration:

~/go/bin/acme whoami

Generate cert (2048 bit, per blog post mentioned above, although it seems more options are now supported):

openssl genrsa -out cert.key 2048

Generate domain ownership token:

~/go/bin/acme cert -k cert.key -dns=true fn.example.com

Follow acme’s instructions to create a TXT record. Google’s Public DNS tool makes to easy to assert record propagation.

Once cert creation is complete, import it into AWS’ Certificate Manager service. (Note at the time of this writing, only N. Virginia was supported, so import there and then reference them in whatever region we’re using for API Gateway.)

Acme generates a single cert file, but it contains two certs. Copy the first into ACM’s “Certificate body” field and the second into the “Certificate chain”. Copy the key into the “Certificate private key” field.

Associate the cert with the API and stage (so we don’t have to pass it in the path) in the API Gateway custom domain configuration. The output of this process is a cloudfront “Target Domain Name”, eg d1eputjh2acqt4.cloudfront.net.

Create a record (CNAME for subdomains, A for apex domains) in your domain registrar mapping the custom domain to the cloudfront domain name.

At long last, we should be able to load https://fn.example.com/a/b/c in a browser.

Feedback

Thoughts? Suggestions? Hit me up @erikeldridge

License

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 International License, and code samples are licensed under the MIT license.