Skip to content

Commit

Permalink
Merge pull request #23 from Asymmetrik/feature/secret-cachebusting
Browse files Browse the repository at this point in the history
Feature/secret cachebusting
  • Loading branch information
jyoung-asymmetrik committed Dec 5, 2017
2 parents 7a1df59 + 386cc50 commit 795cfef
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 20 deletions.
30 changes: 30 additions & 0 deletions cli/commands/secret-refresh.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use strict';

var Q = require('q');

var config = require('../../config');
var logger = config.logger;

var cliHelpers = require('../helpers');

var secretTasks = require('../../lib/secret-tasks');

module.exports = function(program) {
program.command('secret-refresh')
.arguments('<environment>')
.description('Cache bust all secrets in an environment')
.action(function(environment) {
var runtimeOptions = this.opts();
runtimeOptions.environment = environment;

Q.when(runtimeOptions)
.then(cliHelpers.setup())
.then(cliHelpers.lint)
.then(secretTasks.verify)
.then(secretTasks.setupSecretCenter)
.then(secretTasks.refreshSecrets)
.catch(function (err) {
logger.error(err.message);
});
});
};
4 changes: 3 additions & 1 deletion config/manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,15 @@ v.addSchema(DeploymentCenterSchema);
* @memberof Manifest
* @property {string} kmsKeyAlias - The key alias to use. Must be unique within the deployment centers region.
* @property {string} region - AWS Region the key alias resides in. Does not need to be the environment's region.
* @property {string} cacheBusterKey - The secret key used for cache busting.
*/
var SecretSettingsSchema = {
id: '/SecretSettings',
type: 'object',
properties: {
kmsKeyAlias: { type: 'string' },
region: { type: 'string' }
region: { type: 'string' },
cacheBusterKey: { type: 'string' }
},
required: ['kmsKeyAlias'],
additionalProperties: false
Expand Down
103 changes: 86 additions & 17 deletions lib/secret-tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,46 @@ exports.createSecretCenterPolicy = function(options){
})
};

/**
* Cache bust secrets
* @param {object} options - user provided options
* @param {object} options.DeploymentCenter.secret - Secret Center options
* @param {function} options.DeploymentCenter.secret._prefixKey - Secret key prefixer
* @param {string} options.DeploymentCenter.secret.cacheBusterKey - Secret key to use for cache busting
* @return {Q.Promise}
*/
exports.refreshSecrets = function(options) {
return Q.when(options)
.then(function(){
var secrets = options.DeploymentCenter.secret;
var store = secrets.secretStore();

if(!secrets.cacheBusterKey){
return logger.warn('no cache buster key set');
}

var opts = {
name: secrets._prefixKey(secrets.cacheBusterKey),
secret: Date.now()
};
return store.incrementVersion(opts).then(function(version){
opts.version = version;
return store.putSecret(opts);
})
.then(function(){
logger.info('Successfully refreshed secrets');
});
});
};

/**
* Retrieve secret from credential store
* @param {object} options - user provided options
* @param {object} options.DeploymentCenter.secret - Secret Center options
* @param {function} options.DeploymentCenter.secret._prefixKey - Secret key prefixer
* @param {object} options.secretParams - Secret parameters
* @param {string} options.secretParams.secret - Secret to retrieve from store
* @param {string} options.secretParams.version - (Optional) specify which version you want to retrieve. Will automatically handle padding of 0's
* @param {string} options.secretParams.version - (Optional) specify which version you want to retrieve. Defaults to the latest one. Will automatically handle padding of 0's
* @param {object} options.secretParams.context - (Optional) KMS context
* @return {Q.Promise}
*/
Expand All @@ -110,7 +142,7 @@ exports.getSecret = function(options){

return store.getSecret({
name: secrets._prefixKey(params.secret),
version: _.padStart(params.version, 19, '0'),
version: params.version ? _.padStart(params.version, 19, '0') : undefined,
context: params.context,
})
.then(function(secrets){
Expand All @@ -126,9 +158,9 @@ exports.getSecret = function(options){
* @param {object} options.DeploymentCenter.secret - Secret Center options
* @param {function} options.DeploymentCenter.secret._prefixKey - Secret key prefixer
* @param {object} options.secretParams - Secret parameters
* @param {string} options.secretParams.secret - Secret to retrieve from store
* @param {string} options.secretParams.secret - Secret to encrypt into store
* @param {string} options.secretParams.value - Secret value to encrypt into store
* @param {string} options.secretParams.version - (Optional) specify which version you want to retrieve. Will automatically handle padding of 0's
* @param {string} options.secretParams.version - (Optional) specify which version you want to encrypt. Will default to the next available one. Will automatically handle padding of 0's
* @param {object} options.secretParams.context - (Optional) KMS context
* @return {Q.Promise}
*/
Expand All @@ -146,11 +178,21 @@ exports.putSecret = function(options){
var opts = {
name: secrets._prefixKey(params.secret),
secret: params.value,
version: _.padStart(params.version, 19, '0'),
version: params.version ? _.padStart(params.version, 19, '0') : undefined,
context: params.context,
};

return store.putSecret(opts)
return Q.when()
.then(function(){
if(opts.version === undefined){
return store.incrementVersion(opts).then(function(version){
opts.version = version;
});
}
})
.then(function(){
return store.putSecret(opts);
})
.then(function(){
logger.info('Secret `'+opts.name+'` successfully put in store');
});
Expand All @@ -163,8 +205,8 @@ exports.putSecret = function(options){
* @param {object} options.DeploymentCenter.secret - Secret Center options
* @param {function} options.DeploymentCenter.secret._prefixKey - Secret key prefixer
* @param {object} options.secretParams - Secret parameters
* @param {string} options.secretParams.secret - Secret to retrieve from store
* @param {string} options.secretParams.version - (Optional) specify which version you want to retrieve. Will automatically handle padding of 0's
* @param {string} options.secretParams.secret - Secret to delete from store
* @param {string} options.secretParams.version - (Optional) specify which version you want to delete. Defaults to the latest one. Will automatically handle padding of 0's
* @return {Q.Promise}
*/
exports.deleteSecret = function(options){
Expand All @@ -176,10 +218,20 @@ exports.deleteSecret = function(options){

var opts = {
name: secrets._prefixKey(params.secret),
version: _.padStart(params.version, 19, '0'),
version: params.version ? _.padStart(params.version, 19, '0') : undefined,
};

return store.deleteSecret(opts)
return Q.when()
.then(function(){
if(!opts.version){
return store.getHighestVersion(opts).then(function(version) {
opts.version = version;
});
}
})
.then(function(){
return store.deleteSecret(opts);
})
.then(function(){
logger.info('Secret `'+opts.name+'` (ver: '+Number.parseInt(opts.version)+') successfully deleted');
})
Expand All @@ -198,14 +250,18 @@ exports.listSecrets = function(options){
const secretPrefix = options.DeploymentCenter.secret._prefixKey('') + '/';
return store.listSecrets()
.then(function(list){
list = _.filter(list, function(secret) {
return _.startsWith(secret.name, secretPrefix);
const filteredList = {};
_.each(list, function(secret){
if(!_.startsWith(secret.name, secretPrefix))
return;
if(!filteredList[secret.name] || Number(filteredList[secret.name].version) < Number(secret.version))
filteredList[secret.name] = secret;
});
if(list.length === 0)

if(Object.keys(filteredList).length === 0)
logger.warn('There are no secrets you can see.');

for(var secret in list)
logger.info(list[secret]);
_.each(filteredList, _.unary(logger.info));

return list;
})
Expand Down Expand Up @@ -265,7 +321,19 @@ exports.setupSecretCenter = function(options){

options.DeploymentCenter.secret.secretStore = function(){ return getCredentialStore(options) };

return options;
// check to see if we need to create an initial timestamp for the cache busting
if(!options.DeploymentCenter.secret.cacheBusterKey)
return options;

return options.DeploymentCenter.secret.secretStore().getSecret({
name: options.DeploymentCenter.secret._prefixKey(options.DeploymentCenter.secret.cacheBusterKey)
})
.catch(function(err){
return exports.refreshSecrets(options);
})
.then(function(){
return options;
});
});
};

Expand All @@ -284,6 +352,7 @@ exports.getEnvironmentVariables = function(options){
'__YADDA__DEPLOYMENT_SECRET_TABLE_REGION__': tableDetails.region,
'__YADDA__DEPLOYMENT_SECRET_PREFIX__': options.DeploymentCenter.secret._prefixKey(''),
'__YADDA__DEPLOYMENT_SECRET_KMSALIAS__': options.DeploymentCenter.secret.kmsKeyAlias,
'__YADDA__DEPLOYMENT_SECRET_REGION__': options.DeploymentCenter.secret.region || tableDetails.region
'__YADDA__DEPLOYMENT_SECRET_REGION__': options.DeploymentCenter.secret.region || tableDetails.region,
'__YADDA__DEPLOYMENT_SECRET_CACHE_BUSTER_KEY__': options.DeploymentCenter.secret.cacheBusterKey
};
};
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@asymmetrik/yadda",
"description": "Deployment tool for AWS ECS and ECR",
"version": "0.2.7",
"version": "0.2.8",
"main": "index.js",
"author": "Asymmetrik, Ltd",
"license": "MIT",
Expand All @@ -21,7 +21,7 @@
"url": "https://github.com/Asymmetrik/yadda.git"
},
"dependencies": {
"@asymmetrik/yadda-secret": "^0.0.8",
"@asymmetrik/yadda-secret": "^0.0.10",
"archiver": "^1.1.0",
"aws-sdk": "^2.5.1",
"commander": "^2.9.0",
Expand Down

0 comments on commit 795cfef

Please sign in to comment.