KMS in AWS lambda

KMS in AWS lambda

The main purpose of using KMS is leveraging AWS Key Management Service (KMS) to encrypt your environment variables.
It makes it easy for you to create and control the encrypted values and use Hardware Security Modules (HSMs) to protect the security of your keys. You can see all benefits of using KMS.

How to create and use the KMS?

Create KMS in aws console

Under IAM service you can find the Encryption key. Just press Get Started Now and select the proper region then you will see the list of existing KMS.
Go through the steps of creating a new kms which is pretty simple.
You need to assign the users who need to have access to the kms.
After you run your deployment through yml file, the lambdas will potentially have access to the key as well.

Screen-Shot-2017-10-26-at-6.41.12-pm

Leverage a serverless-kms-secrets

You need to introduce the key in the serverless.yml file and the easiest way is to use a node module library which facilitates accessing to your secret keys and decrypt them.

npm install --save-dev serverless-kms-secrets

Introduce KMS to your serverles.yml

Here are the steps you need to take:

You also need to introduce a plugin to your serverless.yml file. Do you know how to do that? here we are:

plugins:
  - serverless-kms-secrets

You should have a configuration in the serverless.yml file in custom section for secretKey path:

custom:
  serverless-kms-secrets:
    secretsFile: kms-secrets.${opt:stage, self:provider.stage}.${opt:region, self:provider.region}.yml (optional)
  kmsSecrets: ${file(kms-secrets.${opt:stage, self:provider.stage}.${opt:region, self:provider.region}.yml)}

example for that is:

custom:
  serverless-kms-secrets:
    secretsFile: config/config.${opt:stage, self:provider.stage}.yml
  kmsSecrets: ${file(config/config.${opt:stage, self:provider.stage}.yml)}

in the example above we introduce the path (config folder) and the config files per environmet like config.test.yml or config.prod.yml holding our secret values.

You also need to give a permission to your lambda to decrypt the key in order to use it:

- Effect: Allow
      Action:
      - KMS:Decrypt
      Resource: ${self:custom.kmsSecrets.keyArn} 

Initiate the Encrypted passwords

you need to store encrypted values in your config file initially.
In order to do that, you need to run a command for the first time to get back a secret encrypted key.
Lets imagin your keyArn for the recently created KMS is :

keyArn: 'arn:aws:kms:ap-southeast-2:xxxxxxxxxxxxxx:key/0c000000-0c111-0c22222-0c333333333'

we need "0c000000-0c111-0c22222-0c333333333" which is located after "key/" in the keyArn for the next step.
You also need to introduce region in your serverless.yml file as below:

region: ap-southeast-2

Now you are able to run this command:

 sls encrypt -n variableName -v plaintext -k  0c000000-0c111-0c22222-0c333333333

variableName is the variable you want to kepp in your config for associated to the encrypted password.
plaintext is the text you wish to encrypt.

Then you will see a message that your config.{stage}.yml file is updated contains a variable name.

Read the decrypted text in AWS lambda

Let's create a security-helper.js to decrypt the passwords and call it in one of our services:
Security-helper.js

const AWS = require('aws-sdk');
const promise = require('bluebird');

AWS.config.update({ region: 'ap-southeast-2' });
const kms = new AWS.KMS();
  
const decrypt = (secretText) => {
    kms.decrypt({CiphertextBlob: Buffer(secretText, 'base64')})
   .promise()
   .then(data => {
      const decryptedText = String(data.Plaintext)
      return promise.resolve(decryptedText);
     })
  };
 
module.exports = {
  decrypt
};

Now that we have the security helper, we can call it across the project:

const promise = require('bluebird');
const security=require('../security-helper');
const config = require('../../config');

module.exports = class members {
  connectDb(request) {
         security.decrypt(config.secrets.gmsPassword)
         .then(password=>{
               //TODO : connect to db with password
               })
  }
};

Read the yml config file

Having used serverless-kms-secrets library, we need to have config files as yml format. So, you might ask how I can read parameters from yml config file? here is my solution:
Consider the following folder structure as your config files per environment.
Screen-Shot-2017-10-27-at-4.52.14-pm
Then in the config.js you can write this code:

'use strict';

const yaml = require('js-yaml');
const fs = require('fs');
const env = process.env.NODE_ENV;
const _ = require('underscore');

const baseConfig = 'config.base.yml';
const envOverride = env ?  `config.${env}.yml`  : 'config.dev.yml';

var convertToJson=function(baseFileName,envFileName)
{
    try {
        const baseConfig = yaml.safeLoad(fs.readFileSync(__dirname +'/'+baseFileName, 'utf8'));
        const envConfig = yaml.safeLoad(fs.readFileSync(__dirname +'/'+envFileName, 'utf8'));
        return  _.extend(baseConfig,envConfig);

    } 
    catch (e) {
        console.log(e);
         }
}
      
module.exports = convertToJson(baseConfig,envOverride);  

You can simply call config class as we did in the examples above.

Summary

Basically you saw how we define a KMS to manage our keys to encrypt/decrypt secret value.
We are using some libraries to make using kms easier for us specially when we use serverless.yml to deploy our code.
We kept our secret values in config structure with yml format so we can read the secret texts and decrypt them in the code.
Hope you enjoyed