Created
November 8, 2017 12:35
-
-
Save issacg/ea661c6652d00191d1d6f08fc9b38b60 to your computer and use it in GitHub Desktop.
Reference code for unsealing a Vault instance using Authy OneTouch push notifications
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"swagger": "2.0", | |
"info": { | |
"version": "2017-11-05T23:36:16Z", | |
"title": "Vault Unseal API" | |
}, | |
"host": "myproject.execute-api.us-east-1.amazonaws.com", | |
"basePath": "/v1", | |
"schemes": [ | |
"https" | |
], | |
"paths": { | |
"/cb": { | |
"post": { | |
"produces": [ | |
"application/json" | |
], | |
"responses": { | |
"200": { | |
"description": "200 response", | |
"schema": { | |
"$ref": "#/definitions/Empty" | |
} | |
} | |
}, | |
"x-amazon-apigateway-integration": { | |
"responses": { | |
"default": { | |
"statusCode": "200" | |
} | |
}, | |
"uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-2:<account>:function:script2/invocations", | |
"passthroughBehavior": "when_no_match", | |
"httpMethod": "POST", | |
"contentHandling": "CONVERT_TO_TEXT", | |
"type": "aws_proxy" | |
} | |
} | |
}, | |
"/start": { | |
"get": { | |
"produces": [ | |
"application/json" | |
], | |
"responses": { | |
"200": { | |
"description": "200 response", | |
"schema": { | |
"$ref": "#/definitions/Empty" | |
} | |
} | |
}, | |
"x-amazon-apigateway-integration": { | |
"responses": { | |
"default": { | |
"statusCode": "200" | |
} | |
}, | |
"uri": "arn:aws:apigateway:us-east-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-2:<account>:function:script1/invocations", | |
"passthroughBehavior": "when_no_match", | |
"httpMethod": "POST", | |
"contentHandling": "CONVERT_TO_TEXT", | |
"type": "aws_proxy" | |
} | |
} | |
} | |
}, | |
"definitions": { | |
"Empty": { | |
"type": "object", | |
"title": "Empty Schema" | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"Version": "2012-10-17", | |
"Statement": [ | |
{ | |
"Effect": "Allow", | |
"Action": [ | |
"kms:Decrypt" | |
], | |
"Resource": "arn:aws:kms:us-east-1:AWS-ACCOUNT:key/KMS-KEY-GUID" | |
} | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "unsealvault", | |
"version": "1.0.0", | |
"description": "AWS Lambda functions for unsealing Vault", | |
"main": "main.js", | |
"scripts": { | |
"test": "echo \"Error: no test specified\" && exit 1" | |
}, | |
"author": "Issac Goldstand <[email protected]>", | |
"license": "Apache-2.0", | |
"dependencies": { | |
"authy-client": "^1.0.10", | |
"request": "^2.83.0", | |
"request-promise-native": "^1.0.5" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2017 Issac Goldstand <[email protected]> | |
/* | |
* This script will send an Authy OneTouch authorization request to unseal the vault | |
* Although in it's current form, the variables are hard-coded, it can be easily | |
* customized to be more dynamic. | |
* The script is written to be called as an AWS Lambda function via the AWS API Gateway proxy service | |
*/ | |
var authy = new (require('authy-client').Client)({key: "YOURKEY"}); // Set to your Authy API key and can be found in the Authy application dashboard | |
var blob = "SOMEBASE64DATA="; // This is the base64 string representing the KMS-encoded unseal key. To encrypt the value, see https://docs.aws.amazon.com/cli/latest/reference/kms/encrypt.html | |
var vault_url = "https://vault.your-company.com:8200"; // This is the base URL of the Vault instance to be unsealed. Ensure that it's accessible from the callback script! | |
var authy_user_id = 123456; // This is the user ID of the Authy user to get the push notification and can be found in the Authy application dashboard | |
exports.handler = (event, context, callback) => { | |
var response; | |
Promise.resolve() | |
.then(() => { | |
if (!(event && event.headers && event.headers['User-Agent'])) | |
throw `Missing UserAgent ${JSON.stringify(event.headers)}`; | |
if (!(event && event.headers && event.headers['X-Forwarded-For'])) | |
throw `Missing IP ${JSON.stringify(event.headers)}`; | |
return authy.createApprovalRequest({ | |
authyId: authy_user_id, | |
message: `Request from IP/chain ${event.headers['X-Forwarded-For']} to unseal Vault at ${vault_url}`, | |
details: { | |
visible: { | |
UserAgent: event.headers['User-Agent'], | |
IP: event.headers['X-Forwarded-For'] | |
}, | |
hidden: { | |
vault_url: vault_url, | |
blob: blob | |
} | |
}, | |
options: { | |
ttl: 120 | |
} | |
}); | |
}).then((res) => { | |
response = { | |
statusCode: 200, | |
body: JSON.stringify({status: "OK", request_id: res.approval_request.uuid}) | |
}; | |
callback(null, response); | |
}).catch((e) => { | |
response = { | |
statusCode: 500, | |
body: JSON.stringify({status:"ERROR", error: e.stack ? e.stack : e.toString()}) | |
}; | |
callback(null, response); | |
}); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2017 Issac Goldstand <[email protected]> | |
/* | |
* This script will validate an authorization request from the Authy OneTouch API | |
* and use the parameters in that request to unseal a Vault instance. | |
* The script is written to be called as an AWS Lambda function via the AWS API Gateway proxy service. | |
* In addition to the standard permissions, this script requires permission to decrypt with the | |
* KMS key used to encrypt the key. | |
* | |
* An example policy is attached to this gist | |
* | |
* The script is written to be called as an AWS Lambda function via the AWS API Gateway proxy service | |
*/ | |
var authy = new (require('authy-client').Client)({key: "YOURKEY"}); // Set to your Authy API key and can be found in the Authy application dashboard | |
var request = require('request-promise-native'); | |
var AWS = require('aws-sdk'); | |
AWS.config.update({region: 'us-east-2'}); // Set to the AWS region that your KMS key exists in | |
exports.handler = (event, context, callback) => { | |
var response; | |
Promise.resolve() | |
.then(() => { | |
if (!(event && event.headers && event.headers['X-Forwarded-Proto'])) | |
throw `Missing X-Forwarded-Proto ${JSON.stringify(event.headers)}`; | |
if (!(event && event.requestContext && event.requestContext.path)) | |
throw `Missing URL info ${JSON.stringify(event.requestContext)}`; | |
event.body = JSON.parse(event.body); | |
// Massage request for authy-client so that it doesn't fail the signature | |
// Host header should be lower-case | |
event.headers.host = | |
(event.headers.host ? event.headers.host : | |
(event.headers.Host ? event.headers.Host : | |
// we're screwed - we didn't get a host header! | |
'')); | |
// device_uuid must be a string, or authy-client will fail validation | |
event.body.device_uuid = event.body.device_uuid.toString(); | |
var obj = { | |
body: event.body, | |
headers: event.headers, | |
method: event.httpMethod, | |
protocol: event.headers["X-Forwarded-Proto"], | |
url: event.requestContext.path | |
}; | |
return authy.verifyCallback(obj); | |
}).then(() => { | |
if (event.body.status != "approved") throw `Request ${event.body.uuid} was ${event.body.status}. ${event.body.approval_request.transaction.reason}`; | |
console.log(`Request approved. Fetching unseal key...`); | |
var KMS = new AWS.KMS(); | |
var blob = event.body.approval_request.transaction.hidden_details.blob; | |
return KMS.decrypt({CiphertextBlob: Buffer.from(blob, 'base64')}).promise(); | |
}).then((res) => { | |
var buf = Buffer(res.Plaintext).toString(); | |
var sub = buf.substring(0,3); | |
var url = event.body.approval_request.transaction.hidden_details.vault_url; | |
console.log(`Unsealing vault at ${url} with key ${sub}...`); | |
return request({ | |
url: `${url}/v1/sys/unseal`, | |
method: "PUT", | |
body: {key: buf}, | |
json: true, | |
strictSSL: false | |
}); | |
}).then((res) => { | |
response = { | |
statusCode: 200, | |
headers: {}, | |
body: JSON.stringify(res) | |
}; | |
callback(null, response); | |
}).catch((e) => { | |
var err = e.stack ? e.stack : e.toString(); | |
console.error(err); | |
if (e.errors) console.log(JSON.stringify(e.errors)); | |
response = { | |
statusCode: 500, | |
body: JSON.stringify({status:"ERROR", error: err}) | |
}; | |
callback(null, response); | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment