A number of months ago Stephan Seidt @evilhackerdude posed a question on Twitter if it was possible to use Fastly to sign requests going to AWS Lambda. For those who do not know what AWS Lambda is here is Wikipedia’s succinct explanation
AWS Lambda is a compute service that runs code in response to events and automatically manages the compute resources required by that code. The purpose of Lambda, as opposed to AWS EC2, is to simplify building smaller, on-demand applications that are responsive to events and new information. AWS targets starting a Lambda instance within milliseconds of an event.
AWS Lambda was designed for use cases such as image upload, responding to website clicks or reacting to output from a connected device. AWS Lambda can also be used to automatically provision back-end services triggered by custom requests.
Unlike Amazon EC2, which is priced by the hour, AWS Lambda is metered in increments of 100 milliseconds.
Initially I thought this was not going to be possible since I thought I could only make asynchronous calls however Stephan pointed out that there was a way to invoke synchronous calls as well since that is what AWS API Gateway does to expose Lambda functions.
In order to be able to send requests to Lambda you would need to sign requests going to Lambda. AWS has gone through a number of versions of their signing API however for most services today you will need to use signature version 4. SIGV4 API relies on a number of HMAC and hashing functions that are not in stock varnish but are available in the Libvmod-Digest VMOD if you are deploying your VCL on Fastly this VMOD is already built it.
Code
You can find full VCL for signing requests to Lambda here
https://github.com/vvuksan/misc-stuff/blob/master/lambda/lambda.vcl
This code has some Fastly specific macros and functions which you can upload as custom VCL however most of the heavy lifting is done inside the aws4_lambda_sign_request subroutine so if you are using stock varnish copy that. Things to change in the vcl_recv are
set req.http.access_key = "CHANGEME"; set req.http.secret_key = "CHANGEME";
Change those with your AWS credentials that have access to Lambda. You can also change the region where you functions run. In addition you will need to come up with a way to map incoming URLs to Lambda functions. In my sample VCL I am using Fastly’s Edge Dictionaries e.g.
table url_mapping { "/": "/2015-03-31/functions/homePage/invocations", "/test": "/2015-03-31/functions/test/invocations", } # If no match req.url will be set to /LAMBDA_Not_Found set req.url = table.lookup(url_mapping, req.url.path, "/LAMBDA_Not_Found" ); # If page has not been found we just throw out a 404 if ( req.url == "/LAMBDA_Not_Found" ) { error 404 "Page not found"; }
Pros and Cons
Pros:
- You get the power of VCL to route requests to different backends including Lambda
- You may be able to cache some of the requests coming out of Lambda
- Lower costs since API Gateway can be pricey
Cons:
- Only POST requests with payload of up to 2 kbytes and GET requests with no query argument are supported
- In order to compute the signature we need to calculate a hash of the payload. Unfortunately Varnish exposes
only 2 kbytes of the payload inside the VCL. This is a tunable if you run your own varnish. You can adjust by
running
varnishadm param.set form_post_body 16384
- Any request other than POST needs to be rewritten as a POST hence GET can query no argument
- In order to compute the signature we need to calculate a hash of the payload. Unfortunately Varnish exposes
only 2 kbytes of the payload inside the VCL. This is a tunable if you run your own varnish. You can adjust by
running
- You can output straight HTML however returned payload you will end up with leading and trailing ‘ character. You will
also need to fix up the returned Content Type since it returns as application/json. You can set Content Type in VCL by
doing following in vcl_deliver e.g.
set resp.http.Content-Type = "text/html";
- Currently it’s impossible to craft POST request froms scratch
Future work
Look into using something like libvmod-curl VMOD to create POST requests on the fly.