opticon
opticon

Reputation: 3594

Decrypting multiple env. variables in AWS Lambda

I've got a number of encrypted environmental variables I need to decrypt in an AWS Lambda function. They give an example bit of code, but I'd rather not run a huge chunk for each value I need to decrypt:

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

const encrypted = process.env['my_password'];
let decrypted;


function processEvent(event, context, callback) {
    // TODO handle the event here
}

exports.handler = (event, context, callback) => {
    if (decrypted) {
        processEvent(event, context, callback);
    } else {
        // Decrypt code should run once and variables stored outside of the function
        // handler so that these are decrypted once per container
        const kms = new AWS.KMS();
        kms.decrypt({ CiphertextBlob: new Buffer(encrypted, 'base64') }, (err, data) => {
            if (err) {
                console.log('Decrypt error:', err);
                return callback(err);
            }
            decrypted = data.Plaintext.toString('ascii');
            processEvent(event, context, callback);
        });
    }
};

I'm wondering if the AWS SDK includes a function that lets me decrypt multiple values at once. Failing that, is there a way to elegantly chain these calls together so they don't take up ~75 lines of my otherwise simple function?

Upvotes: 8

Views: 2656

Answers (2)

Alessandro Gurgel
Alessandro Gurgel

Reputation: 357

I created a class to decrypt variables in amazon lambda. It uses async await instead of Promises.all. You do not need to import lodash library. You can modifiy the bellow class to not use it (use forEach instead).

var _ = require('lodash/core');
const AWS = require('aws-sdk');
class EnvVarsDecryptor {
  constructor(encryptedVariables) {
    this.encryptedVariables = encryptedVariables;
    this.decrypted = {};
  }
  isDecrypted() {
    return _.every(this.encryptedVariables, (e) => this.decrypted[e] != undefined && this.decrypted[e] != null);
  }
  async decryptVars() {
    const kms = new AWS.KMS();
    try {
      for ( let index = 0; index <  this.encryptedVariables.length; index++) {
        const encrypted = this.encryptedVariables[index];
        const data = await kms.decrypt({CiphertextBlob: new Buffer(process.env[encrypted], 'base64') }).promise();
        this.decrypted[encrypted] = data.Plaintext.toString('ascii');
      }
    } catch( e) {
      console.error(e);
    }
    return this.decrypted;
  }
}
module.exports = EnvVarsDecryptor;

This is a sample illustrating how using the function:

exports.handler = async (event) => {
  if (!decryptor.isDecrypted()) {
        await decryptor.decryptVars();
  }
  console.log(decryptor.decrypted);
  return `Successfully processed ${event.Records.length} messages.`;
};

Upvotes: 0

Chris Silcock
Chris Silcock

Reputation: 156

You can use promises to achieve this. See the example below for decrypting both a username and password via KMS. You can add as many additional decryption promises to the decryptPromises array as you'd like:


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

    const encrypted = {
        username: process.env.username,
        password: process.env.password
    };

    let decrypted = {};

    function processEvent(event, context, callback) {
        //do work
    }

    exports.handler = (event, context, callback) => {
        if ( decrypted.username && decrypted.password ) {
            processEvent(event, context, callback);
        } else {
            const kms = new AWS.KMS();

            const decryptPromises = [
                kms.decrypt( { CiphertextBlob: new Buffer(encrypted.username, 'base64') } ).promise(),
                kms.decrypt( { CiphertextBlob: new Buffer(encrypted.password, 'base64') } ).promise()
            ];

            Promise.all( decryptPromises ).then( data => {
                decrypted.username = data[0].Plaintext.toString('ascii');
                decrypted.password = data[1].Plaintext.toString('ascii');

                processEvent(event, context, callback);
            }).catch( err => {
                console.log('Decrypt error:', err);
                return callback(err);
            });
        }
    };

You can find more information on how promises have been implimented for the AWS SDK in the Support for Promises in the SDK documentation.

Upvotes: 14

Related Questions