Skip to content

Instantly share code, notes, and snippets.

@DaneTheory
Last active December 20, 2017 06:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DaneTheory/69ec6a3c4f6ef00cec336f2c3426d51b to your computer and use it in GitHub Desktop.
Save DaneTheory/69ec6a3c4f6ef00cec336f2c3426d51b to your computer and use it in GitHub Desktop.
S3 Bucket Auth Lamda Func Cloudfront config function
let plainTextEmailAddress = null
let cloudFrontPrivateKey = null
const KMS = require('aws-sdk/clients/kms')
const kms = new KMS({apiVersion: '2014-11-01'}) // <==== TODO: CHECK TO SEE IF apiVersion is correct!!!!!!!!!!!!
const crypto = require('crypto')
const headers = {
"Content-Type": "application/json",
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Methods": "OPTIONS,POST",
"Access-Control-Allow-Origin": "*" // <==== ENABLES CORS! WHOOOOOOO!!!!!
}
const BAD_REQUEST_RESPONSE = {
statusCode: 400,
body: JSON.stringify({
error: "Invalid Request"
}),
headers
}
const TWENTY_FOUR_HOURS = 60 * 60 * 24
/*******************************************************************************************
*
* ==== DECRYPT STUFFS TODO: Is it necessarry to decrypt email address?????? ========
*
********************************************************************************************/
const DecryptEmailAddress = (cb) => {
if (plainTextEmailAddress && cloudFrontPrivateKey) {
console.log("Email address already decrypted")
cb()
} else {
console.log("Email address decryption and CloudFront private key generation")
const params = {
CiphertextBlob: new Buffer(process.env.ENCRYPTED_EMAIL, 'base64') // <=== Basic auth here.
}
kms.decrypt(params, (err, data) => {
if (err) {
console.error("Failed to decrypt email", err)
throw err
} else {
plainTextEmailAddress = data.Plaintext.toString()
console.log("Successfully decrypted/cached Email")
const keyParams = {
CiphertextBlob: new Buffer(process.env.ENCRYPTED_CLOUDFRONT_PRIVATE_KEY, 'base64') // <=== Basic auth here.
}
kms.decrypt(keyParams, (err, data) => {
if (err) {
console.error("Failed to decrypt Cloudfront Private Key", err)
throw err
} else {
cloudFrontPrivateKey = data.Plaintext.toString()
console.log("Successfully decrypted/cached Cloudfront Private Key")
cb()
}
})
}
})
}
}
/*******************************************************************************************
* Generate policy that allows access to S3 bucket CloudFront Origin domain
* See: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-canned-policy.html#private-content-canned-policy-signature-cookies
* @param {*} expires Unix timestamp for expiry
********************************************************************************************/
const makePolicy = (expires) => {
const policy = JSON.stringify({
Statement: [{
Resource: `https://${process.env.CLOUDFRONT_DOMAIN_NAME}/*`,
Condition: {
DateLessThan: {
"AWS:EpochTime": expires
}
}
}]
})
console.log("Using Policy", policy)
return policy
}
/********************************************************************************************
* Sign policy to allow access to S3 bucket CloudFront Origin domain
* See: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-canned-policy.html#private-content-canned-policy-signature-cookies
* @param {*} expires Unix timestamp for expiry
*********************************************************************************************/
const makeSignature = (policy, expires) => {
const signature = crypto.createSign('RSA-SHA1')
signature.update(policy)
return signature
.sign(cloudFrontPrivateKey, 'base64')
.replace(/\+/g, "-")
.replace(/=/g, "_")
.replace(/\//g, "~")
}
exports.handler = (event, context, cb) => {
DecryptEmailAddress(() => {
try {
if (event.body) {
const body = JSON.parse(event.body)
if (body.email) {
if (body.email === plainTextEmailAddress && plainTextEmailAddress !== null && plainTextEmailAddress !== "") {
const expires = Math.floor((new Date()).getTime() / 1000) + TWENTY_FOUR_HOURS
const policy = makePolicy(expires)
const signature = makeSignature(policy, expires)
const keyPairId = process.env.CLOUDFRONT_KEYPAIR_ID
cb(null, {
statusCode: 200,
headers,
body: JSON.stringify({
expires,
signature,
keyPairId,
policy: new Buffer(policy)
.toString("base64")
.replace(/\+/g, "-")
.replace(/=/g, "_")
.replace(/\//g, "~")
})
})
} else {
console.error("Error: Either 1.) Invalid email address provided, 2.) Email address set to null, 3.) Email address is empty string")
cb(null, {
statusCode: 401,
headers,
body: JSON.stringify({
error: "Unauthorized"
})
})
}
} else {
console.error("No email address present within in body")
cb(null, BAD_REQUEST_RESPONSE)
}
} else {
console.error("No request body defined")
cb(null, BAD_REQUEST_RESPONSE)
}
} catch (err) {
console.error(err)
cb(null, {
statusCode: 500,
headers,
body: JSON.stringify({
error: "Internal server error"
})
})
}
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment