Make Amazon Host Your Lambda Code

A common pattern I see used by Engineering teams when I provide security consulting is them creating Lambda function and hosting their code in their own S3 buckets. This S3 bucket means the Engineering team needs to secure the bucket, which means the following controls are active and maintained:

  • no public S3 access (bucket or object)
  • access logging (logs are sent to yet another bucket!)
  • default encryption of all objects
  • access control and monitoring
  • backups
  • failover region
  • segregation of code written by different departments

…and that's just the start. All of that is tedious and creates security busy-work, not to mention you are still responsible for those code assets. Since AWS is hosting my Lambda function, they can host my code too. Here's how you do it.

When you provision your Lambda function using CloudFormation, you can specify the code inline in the template like the snippet below.

---
AWSTemplateFormatVersion: "2010-09-09"
...
Resources:
  LambdaFn:
    Type: "AWS::Lambda::Function"
    Properties:
      Runtime: "python3.6"
      Handler: "index.lambda_handler"
      Code:
        ZipFile: !Sub |
          def lambda_handler(event, context):
            print("Stubbed code!")
      Role: ...
...

Once the CloudFormation stack is created and the Lambda function finishes provisioning, AWS zips up the inline code and stores it in a bucket owned and managed by them. Thus the risk of code storage has been transferred to them and since you cannot access the bucket, there is little left to be secured!

If you aren't already amazed, you're probably thinking one of two things:

  1. The inline code was just dummy code; or
  2. Inlining my entire application won't work because it's either too large or needs to be compiled.

Worry not! The dummy code I presented is just a stub so that we force AWS to store our code in their bucket. It is written in Python to make it easier to read and because Python code doesn't need compilation.

Once the Lambda function is provisioned, the next step is to tell Lambda what type of code it will execute and what payload should be executed. Yes, you can change a Lambda function's runtime in realtime!

aws lambda update-function-configuration \
  --function-name ... \
  --handler main \
  --runtime go1.x \
  --region ... \
  --profile ...

Now our function expects a Golang payload, but currently it only has Python code so it will start erroring out. That's okay, let's provide the right payload.

aws lambda update-function-code \
  --zip-file fileb://path/to/artifact.zip \
  --function-name ... \
  --region ... \
  --profile ...

Now we have the right code executing on the right runtime in AWS's own bucket. This is a vast security improvement and, better yet, we're not paying for S3 storage. There are a few minor limitations to using inline code with Lambda but I haven't hit any major roadblocks yet. Try it out for yourself and get rid of those pesky Lambda S3 code hosting buckets today.

To see this in action, check out my lambda-py2go-flip repository on Github for code samples.

« Previous: When a mv is a cp