Skip to content

Instantly share code, notes, and snippets.

@ksafranski
Last active September 25, 2017 01:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ksafranski/86a8d598733787d4f0ea46a2c29d6628 to your computer and use it in GitHub Desktop.
Save ksafranski/86a8d598733787d4f0ea46a2c29d6628 to your computer and use it in GitHub Desktop.
Slecret - Slack Secret

Slecret

Add a Slash command to easily share secrets on Slack.

Setup

There are three components to using this Webtask

1. Create Redis Instance on RedisLabs

At RedisLabs setup an account (or use existing), then create a new Redis database (free tier should be more than enough).

Once setup, you'll need to grab the Endpoint from the configuration. You'll also want to setup Access Control & Security with a Redis Password. You'll need both of these in the next step.

2. Create a Webtask

Setup a new Webtask. For secrets configuration add the following:

  • REDIS_URL: the URL from the endpoint in step 2, without the port.
  • REDIS_PASSWORD: the password you setup in step 2.

For meta configuration add the following:

  • REDIS_PORT: the port from the URL endpoint in step 2.
  • SLECRET_LIFESPAN: (optional) number of seconds to retain slecrets (defaults to 300)

Copy and paste the contents from slectret.js into the Webtask editor.

3. Setup Slash Command

In Slack, create a new Slash Command:

  • Command: /slecret
  • URL: <webtask-url>
  • Method: POST

The other settings can be configured to your personal preferences.

Usage

Once setup, the command can be run in Slack like so:

/slecret <something-private>

This will generate an encrypted store of <something-private> with the expiration set by SLECRET_LIFESPAN (or default 300s/5m). A link will be sent back which you can then share.

const Promise = require('bluebird');
const redis = require('redis');
const crypto = require('crypto');
const moment = require('moment');
var REDIS_CLIENT_CACHE;
Promise.promisifyAll(redis.RedisClient.prototype);
// Encryption algorithm to use
const algo = 'aes256';
/**
* Encrypt data stored in redis
* @param {String} data String to encrypt for redis storage
* @param {String} id The ID of the store being created
* @returns {String} encrypted string
*/
const encrypt = (data, id) => {
const cipher = crypto.createCipher(algo, id);
cipher.update(data, 'utf8');
return cipher.final('base64');
};
/**
* Decrypt data stored in redis
* @param {String} data String to decrypt from redis storage
* @param {String} id The ID of the store being read
* @returns {String} decrypted string
*/
const decrypt = (data, id) => {
if (!data) return 'EXPIRED!';
const decipher = crypto.createDecipher(algo, id);
decipher.update(data, 'base64');
return decipher.final('utf8');
};
/**
* Creates new Redis client connection
* @param {Object} context The context of the execution
* @returns {Object} promisified redis client
*/
const createRedisClient = (context) => Promise.resolve().then(() => {
if (REDIS_CLIENT_CACHE) return REDIS_CLIENT_CACHE;
// Create client
const client = redis.createClient({
host: context.secrets.REDIS_URL,
port: context.meta.REDIS_PORT || 6379,
no_ready_check: true
});
// Authenticate
return client.authAsync(context.secrets.REDIS_PASSWORD)
.then(() => {
CLIENT_CACHE = client;
return client;
});
});
/**
* Builds formatted response for Slack
* @param {Object} obj Properties required for response
* @returns {Object} slack response Object
*/
const buildResponse = (obj) => {
return {
response_type: 'ephemeral',
text: `Your Slecret will expire in ${obj.expireIn}`,
attachments: [{
title: 'Only you can see this! Share the link below carefully!',
text: obj.url
}]
};
};
module.exports = function(context, cb) {
// Retrieve a slecret if id in query
if (context.query.id) {
return createRedisClient(context)
.then((client) => client.getAsync(context.query.id))
.then((data) => cb(null, { slecret: decrypt(data, context.query.id) }))
.catch((err) => cb(null, `Could not get data: ${err.message}`));
}
// Create a slecret
const id = crypto.randomBytes(10).toString('hex');
const lifespan = context.meta.SLECRET_LIFESPAN || 300;
const expireIn = moment().startOf('day').seconds(lifespan).format('HH:mm:ss');
const url = `https://${context.headers.host}/slecret?id=${id}`;
// Save to Redis
return createRedisClient(context)
.then((client) => client.setexAsync(id, lifespan, encrypt(context.body.text, id)))
// Fire callback
.then(() => cb(null, buildResponse(Object.assign({ url }, { id }))))
.catch((e) => cb(null, `There was an error: ${e.message}`));
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment