AWS

Your Serverless Function has a Secret

Your serverless function has a secret... maybe it's a password for a remote API, a private key, or signing certificate.


Your serverless function has a secret... maybe it's a password for a remote API, a private key, or signing certificate. These secrets have to be stored somewhere, and in the old days that usually meant just a plaintext config file on your server. Sure, you could encrypt it, but then you have to put the key on the server, and you haven't gained anything except a bit of obfuscation. Or you could use more complex schemes, like Hiera-Eyaml, which is a small improvement, but you've really just moved the threat to a different part of your infrastructure.

Using AWS KMS (Key Management Service) with AWS Lambdas offers a significant reduction in attack surface area. You get HSM-like functionality, without the high cost (starting at $1/month). In this model, there are only three ways the secret can be accessed:

  1. From inside the container running the Lambda function code
  2. By an IAM user with admin rights - and you protected these accounts with two-factor auth, right?
  3. Amazon screws up catastrophically

I believe (3) is important to acknowledge here - the risk doesn't go away, but is outsourced to an extremely mature, security-focused organization with a stellar track record and a lot at stake.

Here's a short example using the Serverless framework, and Node.js. Many other language and framework combinations are possible.

Here are the steps. Refer to the quickstart docs for Serverless, KMS, and aws-cli if any of these are unfamiliar.

  1. Create a KMS key in the AWS console, and make a note of its ARN.
  2. Generate some ciphertext with the following CLI command:
    aws kms encrypt --key-id 'arn:aws:kms...' --plaintext 'secret'
  3. Add the ciphertext as an environment variable
  4. Add the ARN to the awsKmsKeyArn property in your serverless.yml file
  5. Update your code with kms.decrypt() to access the secret

index.js

'use strict';
const aws = require('aws-sdk');
var kms = new aws.KMS();
 
exports.hello = (event, context, callback) => {
  kms.decrypt({CiphertextBlob:
    new Buffer(process.env.password, 'base64')},
    (err, data) => {
 
    var password = data.Plaintext.toString('ascii');
 
    /*
     * Do stuff with plaintext password
     */
 
    callback(null, {
      statusCode: 200,
      body: JSON.stringify("hello"),
      headers: {
          'Content-Type': 'application/json',
      },
    });
 
  });
};

serverless.yml

service: example

provider:
  name: aws
  region: us-west-2

functions:
  hello:
    handler: index.hello
    events:
      - http:
          path: hello
          method: get
    awsKmsKeyArn: 'arn:aws:kms:us-west-2:123456789:key/80abc534-be62-992a-7ba1-e3a44d4271ac'
    environment:
      # password value below is KMS ciphertext
      password: 'AQICAHiNrBUmBQhi496ozpFzP9BrHUALq+NAyE7Xsz6gRsiz0AGsJnI1cyYl57qLnxn1FDSkAAAAbTBrBgkqhkiG9w0BBwagXjBcAgEAMFcGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM8TW0l6r7UEi1soz9AgEQgCp4Vy6sWq1W7SXXmeaz3jRL/X5ax4E2Uw3N0KGztPwv9kGK8O2Nk56tM4Q='
    runtime: nodejs6.10

Similar posts

Get notified on new marketing insights

Be the first to know about new B2B SaaS Marketing insights to build or refine your marketing function with the tools and knowledge of today’s industry.